mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
c5b4041c32
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<!-- 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"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<!-- 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"/>
|
||||
|
@ -19,7 +19,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="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<!-- 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"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
|
||||
|
@ -19,7 +19,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="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<!-- 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"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
{
|
||||
"git": {
|
||||
"git_revision": "1d62b32408567f9f7cf1c71c1e5a0c6593be757b",
|
||||
"git_revision": "ea27c4ed5b6083c9e21d233d4804372ac4d5d353",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "63c872ccb3541c2ddb0d67f112480eb8f2650cda",
|
||||
"revision": "4ba0821dc786dd00cbb5b3cc0ce997d00e7963be",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,10 +17,10 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1d62b32408567f9f7cf1c71c1e5a0c6593be757b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="ea27c4ed5b6083c9e21d233d4804372ac4d5d353"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5deaf27fd266316f27e68206cc3be0e6f47ded54"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
@ -23,7 +23,7 @@
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="d172b02d2d43774b934a866912b9a170c7f495df"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
|
||||
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0"?>
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1432051555000">
|
||||
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1433264296000">
|
||||
<emItems>
|
||||
<emItem blockID="i58" id="webmaster@buzzzzvideos.info">
|
||||
<versionRange minVersion="0" maxVersion="*">
|
||||
@ -282,6 +282,15 @@
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i914" id="{0153E448-190B-4987-BDE1-F256CADA672F}">
|
||||
<versionRange minVersion="0" maxVersion="15.0.6" severity="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="39.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i678" id="{C4A4F5A0-4B89-4392-AFAC-D58010E349AF}">
|
||||
<versionRange minVersion="0" maxVersion="*" severity="1">
|
||||
@ -463,8 +472,13 @@
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i106" os="WINNT" id="{97E22097-9A2F-45b1-8DAF-36AD648C7EF4}">
|
||||
<emItem blockID="i916" os="WINNT" id="{97E22097-9A2F-45b1-8DAF-36AD648C7EF4}">
|
||||
<versionRange minVersion="0" maxVersion="15.0.5" severity="1">
|
||||
</versionRange>
|
||||
<versionRange minVersion="0" maxVersion="15.0.4" severity="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="39.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
@ -786,8 +800,13 @@
|
||||
<prefs>
|
||||
</prefs>
|
||||
</emItem>
|
||||
<emItem blockID="i111" os="WINNT" id="{C3949AC2-4B17-43ee-B4F1-D26B9D42404D}">
|
||||
<emItem blockID="i918" os="WINNT" id="{C3949AC2-4B17-43ee-B4F1-D26B9D42404D}">
|
||||
<versionRange minVersion="0" maxVersion="15.0.5" severity="1">
|
||||
</versionRange>
|
||||
<versionRange minVersion="0" maxVersion="15.0.5" severity="1">
|
||||
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
|
||||
<versionRange minVersion="39.0a1" maxVersion="*" />
|
||||
</targetApplication>
|
||||
</versionRange>
|
||||
<prefs>
|
||||
</prefs>
|
||||
|
@ -28,6 +28,159 @@ const gXPInstallObserver = {
|
||||
return null;
|
||||
},
|
||||
|
||||
pendingInstalls: new WeakMap(),
|
||||
|
||||
showInstallConfirmation: function(browser, installInfo, height = undefined) {
|
||||
// If the confirmation notification is already open cache the installInfo
|
||||
// and the new confirmation will be shown later
|
||||
if (PopupNotifications.getNotification("addon-install-confirmation", browser)) {
|
||||
let pending = this.pendingInstalls.get(browser);
|
||||
if (pending) {
|
||||
pending.push(installInfo);
|
||||
} else {
|
||||
this.pendingInstalls.set(browser, [installInfo]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const anchorID = "addons-notification-icon";
|
||||
|
||||
// Make notifications persist a minimum of 30 seconds
|
||||
var options = {
|
||||
timeout: Date.now() + 30000
|
||||
};
|
||||
|
||||
try {
|
||||
options.displayOrigin = installInfo.originatingURI.host;
|
||||
} catch (e) {
|
||||
// originatingURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
|
||||
}
|
||||
|
||||
let cancelInstallation = () => {
|
||||
if (installInfo) {
|
||||
for (let install of installInfo.installs)
|
||||
install.cancel();
|
||||
}
|
||||
|
||||
this.acceptInstallation = null;
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (tab)
|
||||
tab.removeEventListener("TabClose", cancelInstallation);
|
||||
|
||||
window.removeEventListener("unload", cancelInstallation);
|
||||
|
||||
// Make sure the browser is still alive.
|
||||
if (gBrowser.browsers.indexOf(browser) == -1)
|
||||
return;
|
||||
|
||||
let pending = this.pendingInstalls.get(browser);
|
||||
if (pending && pending.length)
|
||||
this.showInstallConfirmation(browser, pending.shift());
|
||||
};
|
||||
|
||||
let unsigned = installInfo.installs.filter(i => i.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
|
||||
let someUnsigned = unsigned.length > 0 && unsigned.length < installInfo.installs.length;
|
||||
|
||||
options.eventCallback = (aEvent) => {
|
||||
switch (aEvent) {
|
||||
case "removed":
|
||||
cancelInstallation();
|
||||
break;
|
||||
case "shown":
|
||||
let addonList = document.getElementById("addon-install-confirmation-content");
|
||||
while (addonList.firstChild)
|
||||
addonList.firstChild.remove();
|
||||
|
||||
for (let install of installInfo.installs) {
|
||||
let container = document.createElement("hbox");
|
||||
|
||||
let name = document.createElement("label");
|
||||
name.setAttribute("value", install.addon.name);
|
||||
name.setAttribute("class", "addon-install-confirmation-name");
|
||||
container.appendChild(name);
|
||||
|
||||
if (someUnsigned && install.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
|
||||
let unsigned = document.createElement("label");
|
||||
unsigned.setAttribute("value", gNavigatorBundle.getString("addonInstall.unsigned"));
|
||||
unsigned.setAttribute("class", "addon-install-confirmation-unsigned");
|
||||
container.appendChild(unsigned);
|
||||
}
|
||||
|
||||
addonList.appendChild(container);
|
||||
}
|
||||
|
||||
this.acceptInstallation = () => {
|
||||
for (let install of installInfo.installs)
|
||||
install.install();
|
||||
installInfo = null;
|
||||
|
||||
Services.telemetry
|
||||
.getHistogramById("SECURITY_UI")
|
||||
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
|
||||
};
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
||||
"find-and-install-add-ons";
|
||||
|
||||
let messageString;
|
||||
let notification = document.getElementById("addon-install-confirmation-notification");
|
||||
if (unsigned.length == installInfo.installs.length) {
|
||||
// None of the add-ons are verified
|
||||
messageString = gNavigatorBundle.getString("addonConfirmInstallUnsigned.message");
|
||||
notification.setAttribute("warning", "true");
|
||||
}
|
||||
else if (unsigned.length == 0) {
|
||||
// All add-ons are verified or don't need to be verified
|
||||
messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
|
||||
notification.removeAttribute("warning");
|
||||
}
|
||||
else {
|
||||
// Some of the add-ons are unverified, the list of names will indicate
|
||||
// which
|
||||
messageString = gNavigatorBundle.getString("addonConfirmInstallSomeUnsigned.message");
|
||||
notification.setAttribute("warning", "true");
|
||||
}
|
||||
|
||||
let brandBundle = document.getElementById("bundle_brand");
|
||||
let brandShortName = brandBundle.getString("brandShortName");
|
||||
|
||||
messageString = PluralForm.get(installInfo.installs.length, messageString);
|
||||
messageString = messageString.replace("#1", brandShortName);
|
||||
messageString = messageString.replace("#2", installInfo.installs.length);
|
||||
|
||||
let cancelButton = document.getElementById("addon-install-confirmation-cancel");
|
||||
cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
|
||||
cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
|
||||
|
||||
let acceptButton = document.getElementById("addon-install-confirmation-accept");
|
||||
acceptButton.label = gNavigatorBundle.getString("addonInstall.acceptButton.label");
|
||||
acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
|
||||
|
||||
if (height) {
|
||||
let notification = document.getElementById("addon-install-confirmation-notification");
|
||||
notification.style.minHeight = height + "px";
|
||||
}
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (tab) {
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.addEventListener("TabClose", cancelInstallation);
|
||||
}
|
||||
|
||||
window.addEventListener("unload", cancelInstallation);
|
||||
|
||||
PopupNotifications.show(browser, "addon-install-confirmation", messageString,
|
||||
anchorID, null, null, options);
|
||||
|
||||
Services.telemetry
|
||||
.getHistogramById("SECURITY_UI")
|
||||
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
|
||||
},
|
||||
|
||||
observe: function (aSubject, aTopic, aData)
|
||||
{
|
||||
var brandBundle = document.getElementById("bundle_brand");
|
||||
@ -54,22 +207,6 @@ const gXPInstallObserver = {
|
||||
// originatingURI might be missing or 'host' might throw for non-nsStandardURL nsIURIs.
|
||||
}
|
||||
|
||||
let cancelInstallation = () => {
|
||||
if (installInfo) {
|
||||
for (let install of installInfo.installs)
|
||||
install.cancel();
|
||||
}
|
||||
|
||||
if (aTopic == "addon-install-confirmation")
|
||||
this.acceptInstallation = null;
|
||||
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (tab)
|
||||
tab.removeEventListener("TabClose", cancelInstallation);
|
||||
|
||||
window.removeEventListener("unload", cancelInstallation);
|
||||
};
|
||||
|
||||
switch (aTopic) {
|
||||
case "addon-install-disabled": {
|
||||
notificationID = "xpinstall-disabled";
|
||||
@ -188,106 +325,16 @@ const gXPInstallObserver = {
|
||||
this._removeProgressNotification(browser);
|
||||
break; }
|
||||
case "addon-install-confirmation": {
|
||||
let unsigned = installInfo.installs.filter(i => i.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING);
|
||||
let someUnsigned = unsigned.length > 0 && unsigned.length < installInfo.installs.length;
|
||||
|
||||
options.eventCallback = (aEvent) => {
|
||||
switch (aEvent) {
|
||||
case "removed":
|
||||
cancelInstallation();
|
||||
break;
|
||||
case "shown":
|
||||
let addonList = document.getElementById("addon-install-confirmation-content");
|
||||
while (addonList.firstChild)
|
||||
addonList.firstChild.remove();
|
||||
|
||||
for (let install of installInfo.installs) {
|
||||
let container = document.createElement("hbox");
|
||||
|
||||
let name = document.createElement("label");
|
||||
name.setAttribute("value", install.addon.name);
|
||||
name.setAttribute("class", "addon-install-confirmation-name");
|
||||
container.appendChild(name);
|
||||
|
||||
if (someUnsigned && install.addon.signedState <= AddonManager.SIGNEDSTATE_MISSING) {
|
||||
let unsigned = document.createElement("label");
|
||||
unsigned.setAttribute("value", gNavigatorBundle.getString("addonInstall.unsigned"));
|
||||
unsigned.setAttribute("class", "addon-install-confirmation-unsigned");
|
||||
container.appendChild(unsigned);
|
||||
}
|
||||
|
||||
addonList.appendChild(container);
|
||||
}
|
||||
|
||||
this.acceptInstallation = () => {
|
||||
for (let install of installInfo.installs)
|
||||
install.install();
|
||||
installInfo = null;
|
||||
|
||||
Services.telemetry
|
||||
.getHistogramById("SECURITY_UI")
|
||||
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
|
||||
};
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
options.learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") +
|
||||
"find-and-install-add-ons";
|
||||
|
||||
let notification = document.getElementById("addon-install-confirmation-notification");
|
||||
if (unsigned.length == installInfo.installs.length) {
|
||||
// None of the add-ons are verified
|
||||
messageString = gNavigatorBundle.getString("addonConfirmInstallUnsigned.message");
|
||||
notification.setAttribute("warning", "true");
|
||||
}
|
||||
else if (unsigned.length == 0) {
|
||||
// All add-ons are verified or don't need to be verified
|
||||
messageString = gNavigatorBundle.getString("addonConfirmInstall.message");
|
||||
notification.removeAttribute("warning");
|
||||
}
|
||||
else {
|
||||
// Some of the add-ons are unverified, the list of names will indicate
|
||||
// which
|
||||
messageString = gNavigatorBundle.getString("addonConfirmInstallSomeUnsigned.message");
|
||||
notification.setAttribute("warning", "true");
|
||||
}
|
||||
|
||||
messageString = PluralForm.get(installInfo.installs.length, messageString);
|
||||
messageString = messageString.replace("#1", brandShortName);
|
||||
messageString = messageString.replace("#2", installInfo.installs.length);
|
||||
|
||||
let cancelButton = document.getElementById("addon-install-confirmation-cancel");
|
||||
cancelButton.label = gNavigatorBundle.getString("addonInstall.cancelButton.label");
|
||||
cancelButton.accessKey = gNavigatorBundle.getString("addonInstall.cancelButton.accesskey");
|
||||
|
||||
let acceptButton = document.getElementById("addon-install-confirmation-accept");
|
||||
acceptButton.label = gNavigatorBundle.getString("addonInstall.acceptButton.label");
|
||||
acceptButton.accessKey = gNavigatorBundle.getString("addonInstall.acceptButton.accesskey");
|
||||
|
||||
let showNotification = () => {
|
||||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
if (tab) {
|
||||
gBrowser.selectedTab = tab;
|
||||
tab.addEventListener("TabClose", cancelInstallation);
|
||||
}
|
||||
|
||||
window.addEventListener("unload", cancelInstallation);
|
||||
let height = undefined;
|
||||
|
||||
if (PopupNotifications.isPanelOpen) {
|
||||
let rect = document.getElementById("addon-progress-notification").getBoundingClientRect();
|
||||
let notification = document.getElementById("addon-install-confirmation-notification");
|
||||
notification.style.minHeight = rect.height + "px";
|
||||
height = rect.height;
|
||||
}
|
||||
|
||||
PopupNotifications.show(browser, notificationID, messageString, anchorID,
|
||||
action, null, options);
|
||||
|
||||
this._removeProgressNotification(browser);
|
||||
|
||||
Services.telemetry
|
||||
.getHistogramById("SECURITY_UI")
|
||||
.add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
|
||||
this.showInstallConfirmation(browser, installInfo, height);
|
||||
};
|
||||
|
||||
let progressNotification = PopupNotifications.getNotification("addon-progress", browser);
|
||||
|
@ -61,8 +61,20 @@ var FullScreen = {
|
||||
this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
|
||||
}
|
||||
|
||||
if (enterFS) {
|
||||
gNavToolbox.setAttribute("inFullscreen", true);
|
||||
document.documentElement.setAttribute("inFullscreen", true);
|
||||
} else {
|
||||
gNavToolbox.removeAttribute("inFullscreen");
|
||||
document.documentElement.removeAttribute("inFullscreen");
|
||||
}
|
||||
|
||||
// show/hide menubars, toolbars (except the full screen toolbar)
|
||||
this.showXULChrome("toolbar", !enterFS);
|
||||
// On OS X Lion, we don't want to hide toolbars when entering
|
||||
// fullscreen, unless we're entering DOM fullscreen.
|
||||
if (document.mozFullScreen || !this.useLionFullScreen) {
|
||||
this.showXULChrome("toolbar", !enterFS);
|
||||
}
|
||||
|
||||
if (enterFS) {
|
||||
document.addEventListener("keypress", this._keyToggleCallback, false);
|
||||
@ -557,14 +569,6 @@ var FullScreen = {
|
||||
}
|
||||
}
|
||||
|
||||
if (aShow) {
|
||||
gNavToolbox.removeAttribute("inFullscreen");
|
||||
document.documentElement.removeAttribute("inFullscreen");
|
||||
} else {
|
||||
gNavToolbox.setAttribute("inFullscreen", true);
|
||||
document.documentElement.setAttribute("inFullscreen", true);
|
||||
}
|
||||
|
||||
ToolbarIconColor.inferFromText();
|
||||
|
||||
// For Lion fullscreen, all fullscreen controls are hidden, don't
|
||||
|
@ -1181,7 +1181,7 @@ var gBrowserInit = {
|
||||
break;
|
||||
case "restoreAll":
|
||||
for (let browserWin of browserWindows()) {
|
||||
for (let tab of window.gBrowser.tabs) {
|
||||
for (let tab of browserWin.gBrowser.tabs) {
|
||||
SessionStore.reviveCrashedTab(tab);
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,11 @@ function get_observer_topic(aNotificationId) {
|
||||
return topic;
|
||||
}
|
||||
|
||||
function wait_for_progress_notification(aCallback) {
|
||||
wait_for_notification(PROGRESS_NOTIFICATION, aCallback, "popupshowing");
|
||||
function wait_for_progress_notification(aCallback, aExpectedCount = 1) {
|
||||
wait_for_notification(PROGRESS_NOTIFICATION, aCallback, aExpectedCount, "popupshowing");
|
||||
}
|
||||
|
||||
function wait_for_notification(aId, aCallback, aEvent = "popupshown") {
|
||||
function wait_for_notification(aId, aCallback, aExpectedCount = 1, aEvent = "popupshown") {
|
||||
info("Waiting for " + aId + " notification");
|
||||
|
||||
let topic = get_observer_topic(aId);
|
||||
@ -70,10 +70,11 @@ function wait_for_notification(aId, aCallback, aEvent = "popupshown") {
|
||||
function verify() {
|
||||
info("Saw a notification");
|
||||
ok(PopupNotifications.isPanelOpen, "Panel should be open");
|
||||
is(PopupNotifications.panel.childNodes.length, 1, "Should be only one notification");
|
||||
is(PopupNotifications.panel.childNodes.length, aExpectedCount, "Should be the right number of notifications");
|
||||
if (PopupNotifications.panel.childNodes.length) {
|
||||
is(PopupNotifications.panel.childNodes[0].id,
|
||||
aId + "-notification", "Should have seen the right notification");
|
||||
let nodes = Array.from(PopupNotifications.panel.childNodes);
|
||||
let notification = nodes.find(n => n.id == aId + "-notification");
|
||||
ok(notification, "Should have seen the right notification");
|
||||
}
|
||||
aCallback(PopupNotifications.panel);
|
||||
}
|
||||
@ -135,6 +136,15 @@ function accept_install_dialog() {
|
||||
}
|
||||
}
|
||||
|
||||
function cancel_install_dialog() {
|
||||
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
|
||||
document.getElementById("addon-install-confirmation-cancel").click();
|
||||
} else {
|
||||
let win = Services.wm.getMostRecentWindow("Addons:Install");
|
||||
win.document.documentElement.cancelDialog();
|
||||
}
|
||||
}
|
||||
|
||||
function wait_for_single_notification(aCallback) {
|
||||
function inner_waiter() {
|
||||
info("Waiting for single notification");
|
||||
@ -456,6 +466,84 @@ function test_multiple() {
|
||||
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
|
||||
},
|
||||
|
||||
function test_sequential() {
|
||||
// This test is only relevant if using the new doorhanger UI
|
||||
if (!Preferences.get("xpinstall.customConfirmationUI", false)) {
|
||||
runNextTest();
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the progress notification
|
||||
wait_for_progress_notification(function(aPanel) {
|
||||
// Wait for the install confirmation dialog
|
||||
wait_for_install_dialog(function() {
|
||||
// Wait for the progress notification
|
||||
|
||||
// Should see the right add-on
|
||||
let container = document.getElementById("addon-install-confirmation-content");
|
||||
is(container.childNodes.length, 1, "Should be one item listed");
|
||||
is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
|
||||
|
||||
wait_for_progress_notification(function(aPanel) {
|
||||
// Should still have the right add-on in the confirmation notification
|
||||
is(container.childNodes.length, 1, "Should be one item listed");
|
||||
is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
|
||||
|
||||
// Wait for the install to complete, we won't see a new confirmation
|
||||
// notification
|
||||
Services.obs.addObserver(function observer() {
|
||||
Services.obs.removeObserver(observer, "addon-install-confirmation");
|
||||
|
||||
// Make sure browser-addons.js executes first
|
||||
executeSoon(function () {
|
||||
// Should have dropped the progress notification
|
||||
is(PopupNotifications.panel.childNodes.length, 1, "Should be the right number of notifications");
|
||||
is(PopupNotifications.panel.childNodes[0].id, "addon-install-confirmation-notification",
|
||||
"Should only be showing one install confirmation");
|
||||
|
||||
// Should still have the right add-on in the confirmation notification
|
||||
is(container.childNodes.length, 1, "Should be one item listed");
|
||||
is(container.childNodes[0].firstChild.getAttribute("value"), "XPI Test", "Should have the right add-on");
|
||||
|
||||
cancel_install_dialog();
|
||||
|
||||
ok(PopupNotifications.isPanelOpen, "Panel should still be open");
|
||||
is(PopupNotifications.panel.childNodes.length, 1, "Should be the right number of notifications");
|
||||
is(PopupNotifications.panel.childNodes[0].id, "addon-install-confirmation-notification",
|
||||
"Should still have an install confirmation open");
|
||||
|
||||
// Should have the next add-on's confirmation dialog
|
||||
is(container.childNodes.length, 1, "Should be one item listed");
|
||||
is(container.childNodes[0].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on");
|
||||
|
||||
Services.perms.remove("example.com", "install");
|
||||
wait_for_notification_close(() => {
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
cancel_install_dialog();
|
||||
});
|
||||
}, "addon-install-confirmation", false);
|
||||
}, 2);
|
||||
|
||||
var triggers = encodeURIComponent(JSON.stringify({
|
||||
"Theme XPI": "theme.xpi"
|
||||
}));
|
||||
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
|
||||
});
|
||||
});
|
||||
|
||||
var pm = Services.perms;
|
||||
pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
|
||||
|
||||
var triggers = encodeURIComponent(JSON.stringify({
|
||||
"Restartless XPI": "restartless.xpi"
|
||||
}));
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
|
||||
},
|
||||
|
||||
function test_someunverified() {
|
||||
// This test is only relevant if using the new doorhanger UI and allowing
|
||||
// unsigned add-ons
|
||||
|
@ -48,7 +48,6 @@ support-files =
|
||||
[browser_bug818118.js]
|
||||
[browser_bug820497.js]
|
||||
[browser_clearplugindata.js]
|
||||
skip-if = e10s # bug 1149253
|
||||
[browser_CTP_context_menu.js]
|
||||
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # fails intermittently on Linux (bug 909342)
|
||||
[browser_CTP_crashreporting.js]
|
||||
|
@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
.preview-input-toolbar {
|
||||
display: -moz-box;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
margin-bottom: 1px;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
-moz-box-flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
:root {
|
||||
|
@ -52,6 +52,7 @@ support-files =
|
||||
[browser_perf-details-04.js]
|
||||
[browser_perf-details-05.js]
|
||||
[browser_perf-details-06.js]
|
||||
[browser_perf-details-07.js]
|
||||
[browser_perf-events-calltree.js]
|
||||
[browser_perf-front-basic-profiler-01.js]
|
||||
[browser_perf-front-basic-timeline-01.js]
|
||||
|
63
browser/devtools/performance/test/browser_perf-details-07.js
Normal file
63
browser/devtools/performance/test/browser_perf-details-07.js
Normal file
@ -0,0 +1,63 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that when flame chart views scroll to change selection,
|
||||
* other detail views are rerendered
|
||||
*/
|
||||
let HORIZONTAL_AXIS = 1;
|
||||
let VERTICAL_AXIS = 2;
|
||||
|
||||
function* spawnTest() {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController, OverviewView, DetailsView, WaterfallView, JsCallTreeView, JsFlameGraphView } = panel.panelWin;
|
||||
|
||||
yield startRecording(panel);
|
||||
yield stopRecording(panel);
|
||||
|
||||
let waterfallRendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
|
||||
let calltreeRendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
let flamegraphRendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
|
||||
OverviewView.setTimeInterval({ startTime: 10, endTime: 20 });
|
||||
DetailsView.selectView("waterfall");
|
||||
yield waterfallRendered;
|
||||
DetailsView.selectView("js-calltree");
|
||||
yield calltreeRendered;
|
||||
DetailsView.selectView("js-flamegraph");
|
||||
yield flamegraphRendered;
|
||||
|
||||
waterfallRendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
|
||||
calltreeRendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
let overviewRangeSelected = once(OverviewView, EVENTS.OVERVIEW_RANGE_SELECTED);
|
||||
|
||||
once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED).then(() =>
|
||||
ok(false, "FlameGraphView should not rerender, but be handled via its graph widget"));
|
||||
|
||||
// Reset the range to full view, trigger a "selection" event as if
|
||||
// our mouse has done this
|
||||
scroll(JsFlameGraphView.graph, 200, HORIZONTAL_AXIS, 10);
|
||||
|
||||
DetailsView.selectView("waterfall");
|
||||
yield waterfallRendered;
|
||||
ok(true, "Waterfall rerendered by flame graph changing interval");
|
||||
|
||||
DetailsView.selectView("js-calltree");
|
||||
yield calltreeRendered;
|
||||
ok(true, "CallTree rerendered by flame graph changing interval");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
||||
// EventUtils just doesn't work!
|
||||
|
||||
function scroll(graph, wheel, axis, x, y = 1) {
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
graph._onMouseMove({ testX: x, testY: y });
|
||||
graph._onMouseWheel({ testX: x, testY: y, axis, detail: wheel, axis,
|
||||
HORIZONTAL_AXIS,
|
||||
VERTICAL_AXIS
|
||||
});
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
function* spawnTest() {
|
||||
let { target, panel } = yield initPerformance(MARKERS_URL);
|
||||
let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin;
|
||||
let { L10N } = devtools.require("devtools/performance/global");
|
||||
|
||||
// Hijack the markers massaging part of creating the waterfall view,
|
||||
// to prevent collapsing markers and allowing this test to verify
|
||||
@ -45,9 +44,9 @@ function* spawnTest() {
|
||||
|
||||
const tests = {
|
||||
ConsoleTime: function (marker) {
|
||||
shouldHaveLabel($, L10N.getStr("timeline.markerDetail.consoleTimerName"), "!!!", marker);
|
||||
shouldHaveStack($, "startStack", marker);
|
||||
shouldHaveStack($, "endStack", marker);
|
||||
shouldHaveLabel($, "Timer Name:", "!!!", marker);
|
||||
return true;
|
||||
},
|
||||
TimeStamp: function (marker) {
|
||||
@ -83,10 +82,12 @@ function* spawnTest() {
|
||||
let m = markers[i];
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" }, bar);
|
||||
|
||||
if (testsDone.indexOf(m.name) === -1 && tests[m.name]) {
|
||||
let fullTestComplete = tests[m.name](m);
|
||||
if (fullTestComplete) {
|
||||
testsDone.push(m.name);
|
||||
if (tests[m.name]) {
|
||||
if (testsDone.indexOf(m.name) === -1) {
|
||||
let fullTestComplete = tests[m.name](m);
|
||||
if (fullTestComplete) {
|
||||
testsDone.push(m.name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info(`TODO: Need to add marker details tests for ${m.name}`);
|
||||
|
@ -44,6 +44,15 @@ let DetailsSubview = {
|
||||
*/
|
||||
rangeChangeDebounceTime: 0,
|
||||
|
||||
/**
|
||||
* When the overview range changes, all details views will require a
|
||||
* rerendering at a later point, determined by `shouldUpdateWhenShown` and
|
||||
* `canUpdateWhileHidden` and whether or not its the current view.
|
||||
* Set `requiresUpdateOnRangeChange` to false to not invalidate the view
|
||||
* when the range changes.
|
||||
*/
|
||||
requiresUpdateOnRangeChange: true,
|
||||
|
||||
/**
|
||||
* Flag specifying if this view should be updated when selected. This will
|
||||
* be set to true, for example, when the range changes in the overview and
|
||||
@ -93,6 +102,9 @@ let DetailsSubview = {
|
||||
* Fired when a range is selected or cleared in the OverviewView.
|
||||
*/
|
||||
_onOverviewRangeChange: function (_, interval) {
|
||||
if (!this.requiresUpdateOnRangeChange) {
|
||||
return;
|
||||
}
|
||||
if (DetailsView.isViewSelected(this)) {
|
||||
let debounced = () => {
|
||||
if (!this.shouldUpdateWhileMouseIsActive && OverviewView.isMouseActive) {
|
||||
|
@ -86,7 +86,13 @@ let JsFlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
*/
|
||||
_onRangeChangeInGraph: function () {
|
||||
let interval = this.graph.getViewRange();
|
||||
OverviewView.setTimeInterval(interval, { stopPropagation: true });
|
||||
|
||||
// Squelch rerendering this view when we update the range here
|
||||
// to avoid recursion, as our FlameGraph handles rerendering itself
|
||||
// when originating from within the graph.
|
||||
this.requiresUpdateOnRangeChange = false;
|
||||
OverviewView.setTimeInterval(interval);
|
||||
this.requiresUpdateOnRangeChange = true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -84,7 +84,13 @@ let MemoryFlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
*/
|
||||
_onRangeChangeInGraph: function () {
|
||||
let interval = this.graph.getViewRange();
|
||||
OverviewView.setTimeInterval(interval, { stopPropagation: true });
|
||||
|
||||
// Squelch rerendering this view when we update the range here
|
||||
// to avoid recursion, as our FlameGraph handles rerendering itself
|
||||
// when originating from within the graph.
|
||||
this.requiresUpdateOnRangeChange = false;
|
||||
OverviewView.setTimeInterval(interval);
|
||||
this.requiresUpdateOnRangeChange = true;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,7 @@ support-files =
|
||||
[browser_graphs-07b.js]
|
||||
[browser_graphs-07c.js]
|
||||
[browser_graphs-07d.js]
|
||||
[browser_graphs-07e.js]
|
||||
[browser_graphs-08.js]
|
||||
[browser_graphs-09a.js]
|
||||
[browser_graphs-09b.js]
|
||||
|
105
browser/devtools/shared/test/browser_graphs-07e.js
Normal file
105
browser/devtools/shared/test/browser_graphs-07e.js
Normal file
@ -0,0 +1,105 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that selections are drawn onto the canvas.
|
||||
|
||||
const TEST_DATA = [{ delta: 112, value: 48 }, { delta: 213, value: 59 }, { delta: 313, value: 60 }, { delta: 413, value: 59 }, { delta: 530, value: 59 }, { delta: 646, value: 58 }, { delta: 747, value: 60 }, { delta: 863, value: 48 }, { delta: 980, value: 37 }, { delta: 1097, value: 30 }, { delta: 1213, value: 29 }, { delta: 1330, value: 23 }, { delta: 1430, value: 10 }, { delta: 1534, value: 17 }, { delta: 1645, value: 20 }, { delta: 1746, value: 22 }, { delta: 1846, value: 39 }, { delta: 1963, value: 26 }, { delta: 2080, value: 27 }, { delta: 2197, value: 35 }, { delta: 2312, value: 47 }, { delta: 2412, value: 53 }, { delta: 2514, value: 60 }, { delta: 2630, value: 37 }, { delta: 2730, value: 36 }, { delta: 2830, value: 37 }, { delta: 2946, value: 36 }, { delta: 3046, value: 40 }, { delta: 3163, value: 47 }, { delta: 3280, value: 41 }, { delta: 3380, value: 35 }, { delta: 3480, value: 27 }, { delta: 3580, value: 39 }, { delta: 3680, value: 42 }, { delta: 3780, value: 49 }, { delta: 3880, value: 55 }, { delta: 3980, value: 60 }, { delta: 4080, value: 60 }, { delta: 4180, value: 60 }];
|
||||
let {LineGraphWidget} = Cu.import("resource:///modules/devtools/Graphs.jsm", {});
|
||||
let {Promise} = devtools.require("resource://gre/modules/Promise.jsm");
|
||||
let CURRENT_ZOOM = 1;
|
||||
|
||||
add_task(function*() {
|
||||
yield promiseTab("about:blank");
|
||||
yield performTest();
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
||||
function* performTest() {
|
||||
let [host, win, doc] = yield createHost();
|
||||
let graph = new LineGraphWidget(doc.body, "fps");
|
||||
yield graph.once("ready");
|
||||
graph.setData(TEST_DATA);
|
||||
|
||||
info("Testing with normal zoom.");
|
||||
testGraph(graph);
|
||||
|
||||
info("Testing while zoomed out.");
|
||||
setZoom(host.frame, .5);
|
||||
testGraph(graph);
|
||||
|
||||
info("Testing while zoomed in.");
|
||||
setZoom(host.frame, 2);
|
||||
testGraph(graph);
|
||||
|
||||
yield graph.destroy();
|
||||
host.destroy();
|
||||
}
|
||||
|
||||
function testGraph(graph) {
|
||||
graph.dropSelection();
|
||||
|
||||
info("Making a selection.");
|
||||
|
||||
dragStart(graph, 100);
|
||||
ok(graph.hasSelectionInProgress(),
|
||||
"The selection should start (1).");
|
||||
is(graph.getSelection().start, 100,
|
||||
"The current selection start value is correct (1).");
|
||||
is(graph.getSelection().end, 100,
|
||||
"The current selection end value is correct (1).");
|
||||
|
||||
hover(graph, 200);
|
||||
ok(graph.hasSelectionInProgress(),
|
||||
"The selection should still be in progress (2).");
|
||||
is(graph.getSelection().start, 100,
|
||||
"The current selection start value is correct (2).");
|
||||
is(graph.getSelection().end, 200,
|
||||
"The current selection end value is correct (2).");
|
||||
|
||||
dragStop(graph, 300);
|
||||
ok(!graph.hasSelectionInProgress(),
|
||||
"The selection should have stopped (3).");
|
||||
is(graph.getSelection().start, 100,
|
||||
"The current selection start value is correct (3).");
|
||||
is(graph.getSelection().end, 300,
|
||||
"The current selection end value is correct (3).");
|
||||
}
|
||||
|
||||
function setZoom(frame, zoomValue) {
|
||||
let contViewer = frame.docShell.contentViewer;
|
||||
CURRENT_ZOOM = contViewer.fullZoom = zoomValue;
|
||||
}
|
||||
|
||||
// EventUtils just doesn't work!
|
||||
|
||||
function dispatchEvent(graph, x, y, fn) {
|
||||
x *= CURRENT_ZOOM;
|
||||
y *= CURRENT_ZOOM;
|
||||
x /= window.devicePixelRatio;
|
||||
y /= window.devicePixelRatio;
|
||||
let quad = graph._canvas.getBoxQuads({
|
||||
relativeTo: window.document
|
||||
})[0];
|
||||
|
||||
let screenX = (window.screenX + quad.p1.x + x);
|
||||
let screenY = (window.screenY + quad.p1.y + y);
|
||||
|
||||
fn({
|
||||
screenX: screenX,
|
||||
screenY: screenY,
|
||||
});
|
||||
}
|
||||
|
||||
function hover(graph, x, y = 1) {
|
||||
dispatchEvent(graph, x, y, graph._onMouseMove);
|
||||
}
|
||||
|
||||
function dragStart(graph, x, y = 1) {
|
||||
dispatchEvent(graph, x, y, graph._onMouseMove);
|
||||
dispatchEvent(graph, x, y, graph._onMouseDown);
|
||||
}
|
||||
|
||||
function dragStop(graph, x, y = 1) {
|
||||
dispatchEvent(graph, x, y, graph._onMouseMove);
|
||||
dispatchEvent(graph, x, y, graph._onMouseUp);
|
||||
}
|
@ -10,6 +10,7 @@ const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
const {DevToolsWorker} = Cu.import("resource://gre/modules/devtools/shared/worker.js", {});
|
||||
const {LayoutHelpers} = Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", {});
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"GraphCursor",
|
||||
@ -977,6 +978,12 @@ AbstractCanvasGraph.prototype = {
|
||||
let mouseX = Math.max(0, Math.min(x, maxX)) * this._pixelRatio;
|
||||
let mouseY = Math.max(0, Math.min(x, maxY)) * this._pixelRatio;
|
||||
|
||||
// The coordinates need to be modified with the current zoom level
|
||||
// to prevent them from being wrong.
|
||||
let zoom = LayoutHelpers.getCurrentZoom(this._canvas);
|
||||
mouseX /= zoom;
|
||||
mouseY /= zoom;
|
||||
|
||||
return {mouseX,mouseY};
|
||||
},
|
||||
|
||||
|
@ -38,7 +38,7 @@
|
||||
<body>
|
||||
|
||||
<div id="root" class="devtools-monospace">
|
||||
<div class="devtools-toolbar">
|
||||
<div id="ruleview-toolbar" class="devtools-toolbar">
|
||||
<div class="devtools-searchbox">
|
||||
<input id="ruleview-searchbox"
|
||||
class="devtools-searchinput devtools-rule-searchbox"
|
||||
@ -47,6 +47,12 @@
|
||||
</div>
|
||||
<!-- TODO : Bug 1165122 : Show this button by default -->
|
||||
<button hidden="true" id="ruleview-add-rule-button" title="&addRuleButtonTooltip;" class="devtools-button"></button>
|
||||
<button id="pseudo-class-panel-toggle" class="devtools-button"></button>
|
||||
</div>
|
||||
<div id="pseudo-class-panel" class="devtools-toolbar" hidden="true">
|
||||
<label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" />:hover</label>
|
||||
<label><input id="pseudo-active-toggle" type="checkbox" value=":active" />:active</label>
|
||||
<label><input id="pseudo-focus-toggle" type="checkbox" value=":focus" />:focus</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1130,11 +1130,18 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle) {
|
||||
this._onFilterKeyPress = this._onFilterKeyPress.bind(this);
|
||||
this._onClearSearch = this._onClearSearch.bind(this);
|
||||
this._onFilterTextboxContextMenu = this._onFilterTextboxContextMenu.bind(this);
|
||||
this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this);
|
||||
this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this);
|
||||
|
||||
this.element = this.doc.getElementById("ruleview-container");
|
||||
this.addRuleButton = this.doc.getElementById("ruleview-add-rule-button");
|
||||
this.searchField = this.doc.getElementById("ruleview-searchbox");
|
||||
this.searchClearButton = this.doc.getElementById("ruleview-searchinput-clear");
|
||||
this.pseudoClassPanel = this.doc.getElementById("pseudo-class-panel");
|
||||
this.pseudoClassToggle = this.doc.getElementById("pseudo-class-panel-toggle");
|
||||
this.hoverCheckbox = this.doc.getElementById("pseudo-hover-toggle");
|
||||
this.activeCheckbox = this.doc.getElementById("pseudo-active-toggle");
|
||||
this.focusCheckbox = this.doc.getElementById("pseudo-focus-toggle");
|
||||
|
||||
this.searchClearButton.hidden = true;
|
||||
|
||||
@ -1145,6 +1152,10 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle) {
|
||||
this.searchField.addEventListener("keypress", this._onFilterKeyPress);
|
||||
this.searchField.addEventListener("contextmenu", this._onFilterTextboxContextMenu);
|
||||
this.searchClearButton.addEventListener("click", this._onClearSearch);
|
||||
this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
|
||||
this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
|
||||
this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
|
||||
this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);
|
||||
|
||||
this._handlePrefChange = this._handlePrefChange.bind(this);
|
||||
this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this);
|
||||
@ -1823,8 +1834,19 @@ CssRuleView.prototype = {
|
||||
this.searchField.removeEventListener("contextmenu",
|
||||
this._onFilterTextboxContextMenu);
|
||||
this.searchClearButton.removeEventListener("click", this._onClearSearch);
|
||||
this.pseudoClassToggle.removeEventListener("click",
|
||||
this._onTogglePseudoClassPanel);
|
||||
this.hoverCheckbox.removeEventListener("click", this._onTogglePseudoClass);
|
||||
this.activeCheckbox.removeEventListener("click", this._onTogglePseudoClass);
|
||||
this.focusCheckbox.removeEventListener("click", this._onTogglePseudoClass);
|
||||
|
||||
this.searchField = null;
|
||||
this.searchClearButton = null;
|
||||
this.pseudoClassPanel = null;
|
||||
this.pseudoClassToggle = null;
|
||||
this.hoverCheckbox = null;
|
||||
this.activeCheckbox = null;
|
||||
this.focusCheckbox = null;
|
||||
|
||||
if (this.element.parentNode) {
|
||||
this.element.parentNode.removeChild(this.element);
|
||||
@ -1849,10 +1871,12 @@ CssRuleView.prototype = {
|
||||
}
|
||||
|
||||
this.clear();
|
||||
this.clearPseudoClassPanel();
|
||||
|
||||
this._viewedElement = aElement;
|
||||
if (!this._viewedElement) {
|
||||
this._showEmpty();
|
||||
this.refreshPseudoClassPanel();
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@ -1894,6 +1918,45 @@ CssRuleView.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear the pseudo class options panel by removing the checked and disabled
|
||||
* attributes for each checkbox.
|
||||
*/
|
||||
clearPseudoClassPanel: function() {
|
||||
this.hoverCheckbox.checked = this.hoverCheckbox.disabled = false;
|
||||
this.activeCheckbox.checked = this.activeCheckbox.disabled = false;
|
||||
this.focusCheckbox.checked = this.focusCheckbox.disabled = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the pseudo class options for the currently highlighted element.
|
||||
*/
|
||||
refreshPseudoClassPanel: function() {
|
||||
if (!this._elementStyle || !this.inspector.selection.isElementNode()) {
|
||||
this.hoverCheckbox.disabled = true;
|
||||
this.activeCheckbox.disabled = true;
|
||||
this.focusCheckbox.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (let pseudoClassLock of this._elementStyle.element.pseudoClassLocks) {
|
||||
switch (pseudoClassLock) {
|
||||
case ":hover": {
|
||||
this.hoverCheckbox.checked = true;
|
||||
break;
|
||||
}
|
||||
case ":active": {
|
||||
this.activeCheckbox.checked = true;
|
||||
break;
|
||||
}
|
||||
case ":focus": {
|
||||
this.focusCheckbox.checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_populate: function(clearRules = false) {
|
||||
let elementStyle = this._elementStyle;
|
||||
return this._elementStyle.populate().then(() => {
|
||||
@ -1906,6 +1969,8 @@ CssRuleView.prototype = {
|
||||
}
|
||||
this._createEditors();
|
||||
|
||||
this.refreshPseudoClassPanel();
|
||||
|
||||
// Notify anyone that cares that we refreshed.
|
||||
this.emit("ruleview-refreshed");
|
||||
return undefined;
|
||||
@ -2289,7 +2354,29 @@ CssRuleView.prototype = {
|
||||
}
|
||||
|
||||
this._editorsExpandedForFilter = [];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the pseudo class panel button is clicked and toggles
|
||||
* the display of the pseudo class panel.
|
||||
*/
|
||||
_onTogglePseudoClassPanel: function() {
|
||||
if (this.pseudoClassPanel.hidden) {
|
||||
this.pseudoClassToggle.setAttribute("checked", "true");
|
||||
} else {
|
||||
this.pseudoClassToggle.removeAttribute("checked");
|
||||
}
|
||||
this.pseudoClassPanel.hidden = !this.pseudoClassPanel.hidden;
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a pseudo class checkbox is clicked and toggles
|
||||
* the pseudo class for the current selected element.
|
||||
*/
|
||||
_onTogglePseudoClass: function(event) {
|
||||
let target = event.currentTarget;
|
||||
this.inspector.togglePseudoClass(target.value);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -24,9 +24,28 @@ body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#root .devtools-toolbar {
|
||||
.devtools-toolbar {
|
||||
width: 100%;
|
||||
display: -moz-box;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#pseudo-class-panel {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
overflow-y: hidden;
|
||||
max-height: 24px;
|
||||
justify-content: space-around;
|
||||
transition-property: max-height;
|
||||
transition-duration: 150ms;
|
||||
transition-timing-function: ease;
|
||||
}
|
||||
|
||||
#pseudo-class-panel[hidden] {
|
||||
max-height: 0px;
|
||||
}
|
||||
|
||||
#pseudo-class-panel > label {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.ruleview {
|
||||
|
@ -114,6 +114,7 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
|
||||
[browser_ruleview_pseudo-element_01.js]
|
||||
[browser_ruleview_pseudo-element_02.js]
|
||||
skip-if = e10s # Bug 1090340
|
||||
[browser_ruleview_pseudo_lock_options.js]
|
||||
[browser_ruleview_refresh-on-attribute-change_01.js]
|
||||
[browser_ruleview_refresh-on-attribute-change_02.js]
|
||||
[browser_ruleview_refresh-on-style-change.js]
|
||||
|
@ -0,0 +1,104 @@
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that the rule view pseudo lock options work properly.
|
||||
|
||||
let TEST_URI = [
|
||||
"<style type='text/css'>",
|
||||
" div {",
|
||||
" color: red;",
|
||||
" }",
|
||||
" div:hover {",
|
||||
" color: blue;",
|
||||
" }",
|
||||
" div:active {",
|
||||
" color: yellow;",
|
||||
" }",
|
||||
" div:focus {",
|
||||
" color: green;",
|
||||
" }",
|
||||
"</style>",
|
||||
"<div>test div</div>"
|
||||
].join("\n");
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||
let {inspector, view} = yield openRuleView();
|
||||
yield selectNode("div", inspector);
|
||||
|
||||
info("Toggle the pseudo class panel open");
|
||||
ok(view.pseudoClassPanel.hidden, "Pseudo Class Panel Hidden");
|
||||
view.pseudoClassToggle.click();
|
||||
ok(!view.pseudoClassPanel.hidden, "Pseudo Class Panel Opened");
|
||||
ok(!view.hoverCheckbox.disabled, ":hover checkbox is not disabled");
|
||||
ok(!view.activeCheckbox.disabled, ":active checkbox is not disabled");
|
||||
ok(!view.focusCheckbox.disabled, ":focus checkbox is not disabled");
|
||||
|
||||
info("Toggle each pseudo lock and check that the pseudo lock is added");
|
||||
yield togglePseudoClass(inspector, view, view.hoverCheckbox);
|
||||
yield assertPseudoAdded(inspector, view, ":hover", 3, 1);
|
||||
yield togglePseudoClass(inspector, view, view.hoverCheckbox);
|
||||
yield assertPseudoRemoved(inspector, view, 2);
|
||||
|
||||
yield togglePseudoClass(inspector, view, view.activeCheckbox);
|
||||
yield assertPseudoAdded(inspector, view, ":active", 3, 1);
|
||||
yield togglePseudoClass(inspector, view, view.activeCheckbox);
|
||||
yield assertPseudoRemoved(inspector, view, 2);
|
||||
|
||||
yield togglePseudoClass(inspector, view, view.focusCheckbox);
|
||||
yield assertPseudoAdded(inspector, view, ":focus", 3, 1);
|
||||
yield togglePseudoClass(inspector, view, view.focusCheckbox);
|
||||
yield assertPseudoRemoved(inspector, view, 2);
|
||||
|
||||
info("Toggle all pseudo lock and check that the pseudo lock is added");
|
||||
yield togglePseudoClass(inspector, view, view.hoverCheckbox);
|
||||
yield togglePseudoClass(inspector, view, view.activeCheckbox);
|
||||
yield togglePseudoClass(inspector, view, view.focusCheckbox);
|
||||
yield assertPseudoAdded(inspector, view, ":focus", 5, 1);
|
||||
yield assertPseudoAdded(inspector, view, ":active", 5, 2);
|
||||
yield assertPseudoAdded(inspector, view, ":hover", 5, 3);
|
||||
yield togglePseudoClass(inspector, view, view.hoverCheckbox);
|
||||
yield togglePseudoClass(inspector, view, view.activeCheckbox);
|
||||
yield togglePseudoClass(inspector, view, view.focusCheckbox);
|
||||
yield assertPseudoRemoved(inspector, view, 2);
|
||||
|
||||
info("Select a null element");
|
||||
yield view.selectElement(null);
|
||||
ok(!view.hoverCheckbox.checked && view.hoverCheckbox.disabled,
|
||||
":hover checkbox is unchecked and disabled");
|
||||
ok(!view.activeCheckbox.checked && view.activeCheckbox.disabled,
|
||||
":active checkbox is unchecked and disabled");
|
||||
ok(!view.focusCheckbox.checked && view.focusCheckbox.disabled,
|
||||
":focus checkbox is unchecked and disabled");
|
||||
|
||||
info("Toggle the pseudo class panel close");
|
||||
view.pseudoClassToggle.click();
|
||||
ok(view.pseudoClassPanel.hidden, "Pseudo Class Panel Closed");
|
||||
});
|
||||
|
||||
function* togglePseudoClass(inspector, ruleView, pseudoClassOption) {
|
||||
info("Toggle the pseudoclass, wait for it to be applied");
|
||||
let onRefresh = inspector.once("rule-view-refreshed");
|
||||
pseudoClassOption.click();
|
||||
yield onRefresh;
|
||||
}
|
||||
|
||||
function* assertPseudoAdded(inspector, ruleView, pseudoClass, numRules,
|
||||
childIndex) {
|
||||
info("Check that the ruleview contains the pseudo-class rule");
|
||||
is(ruleView.element.children.length, numRules,
|
||||
"Should have " + numRules + " rules.");
|
||||
is(getRuleViewRuleEditor(ruleView, childIndex).rule.selectorText,
|
||||
"div" + pseudoClass, "rule view is showing " + pseudoClass + " rule");
|
||||
}
|
||||
|
||||
function* assertPseudoRemoved(inspector, ruleView, numRules) {
|
||||
info("Check that the ruleview no longer contains the pseudo-class rule");
|
||||
is(ruleView.element.children.length, numRules,
|
||||
"Should have " + numRules + " rules.");
|
||||
is(getRuleViewRuleEditor(ruleView, 1).rule.selectorText, "div",
|
||||
"Second rule is div");
|
||||
}
|
@ -665,8 +665,9 @@
|
||||
@RESPATH@/browser/chrome/shumway.manifest
|
||||
@RESPATH@/browser/chrome/shumway/*
|
||||
#endif
|
||||
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
|
||||
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.manifest
|
||||
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/icon.png
|
||||
@RESPATH@/browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/install.rdf
|
||||
@RESPATH@/chrome/toolkit@JAREXT@
|
||||
@RESPATH@/chrome/toolkit.manifest
|
||||
@RESPATH@/chrome/recording.manifest
|
||||
|
@ -258,6 +258,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/add.svg (../shared/devtools/images/add.svg)
|
||||
skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg)
|
||||
skin/classic/browser/devtools/filter-swatch.svg (../shared/devtools/images/filter-swatch.svg)
|
||||
skin/classic/browser/devtools/pseudo-class.svg (../shared/devtools/images/pseudo-class.svg)
|
||||
skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png)
|
||||
skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
|
||||
skin/classic/browser/devtools/performance-icons.svg (../shared/devtools/images/performance-icons.svg)
|
||||
@ -469,6 +470,7 @@ browser.jar:
|
||||
skin/classic/browser/warning16.png (../shared/warning16.png)
|
||||
skin/classic/browser/warning16@2x.png (../shared/warning16@2x.png)
|
||||
|
||||
../extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
% override chrome://browser/skin/feeds/videoFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
|
@ -373,6 +373,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/add.svg (../shared/devtools/images/add.svg)
|
||||
skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg)
|
||||
skin/classic/browser/devtools/filter-swatch.svg (../shared/devtools/images/filter-swatch.svg)
|
||||
skin/classic/browser/devtools/pseudo-class.svg (../shared/devtools/images/pseudo-class.svg)
|
||||
skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png)
|
||||
skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
|
||||
skin/classic/browser/devtools/performance-icons.svg (../shared/devtools/images/performance-icons.svg)
|
||||
@ -613,6 +614,7 @@ browser.jar:
|
||||
skin/classic/browser/warning16.png (../shared/warning16.png)
|
||||
skin/classic/browser/warning16@2x.png (../shared/warning16@2x.png)
|
||||
|
||||
../extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
% override chrome://browser/skin/feeds/videoFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
|
@ -153,7 +153,7 @@ body {
|
||||
|
||||
#root .devtools-toolbar {
|
||||
width: 100%;
|
||||
display: -moz-box;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.link {
|
||||
|
29
browser/themes/shared/devtools/images/pseudo-class.svg
Normal file
29
browser/themes/shared/devtools/images/pseudo-class.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<!-- 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/. -->
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<style>
|
||||
use[id^="pseudo-class"]:not(:target) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<rect id="class-block-maskBG" width="8" height="8" fill="#fff"/>
|
||||
<rect id="class-block" width="8" height="8" rx="1" ry="1"/>
|
||||
<mask id="mask-block-solid">
|
||||
<use xlink:href="#class-block-maskBG"/>
|
||||
<use xlink:href="#class-block" transform="translate(3 3)" fill="#000"/>
|
||||
</mask>
|
||||
<g id="pseudo-class-shape">
|
||||
<rect x=".5" y=".5" width="7" height="7" rx="1" ry="1" mask="url(#mask-block-solid)" fill="none" stroke="currentColor" stroke-width="1"/>
|
||||
<use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" fill-opacity=".4"/>
|
||||
<use xlink:href="#class-block" mask="url(#mask-block-solid)" fill="currentColor" transform="translate(4 4)"/>
|
||||
<g transform="translate(8 8)" fill="currentColor">
|
||||
<path d="M2.5,0C2.2,0,2,0.2,2,0.5C2,0.8,2.2,1,2.5,1C2.8,1,3,0.8,3,0.5 C3,0.2,2.8,0,2.5,0z M4.5,0C4.2,0,4,0.2,4,0.5C4,0.8,4.2,1,4.5,1C4.8,1,5,0.8,5,0.5C5,0.2,4.8,0,4.5,0z M0.5,6C0.8,6,1,5.8,1,5.5 C1,5.2,0.8,5,0.5,5C0.2,5,0,5.2,0,5.5C0,5.8,0.2,6,0.5,6z M0.5,4C0.8,4,1,3.8,1,3.5C1,3.2,0.8,3,0.5,3C0.2,3,0,3.2,0,3.5 C0,3.8,0.2,4,0.5,4z M7.5,2C7.2,2,7,2.2,7,2.5C7,2.8,7.2,3,7.5,3C7.8,3,8,2.8,8,2.5C8,2.2,7.8,2,7.5,2z M7.5,4C7.2,4,7,4.2,7,4.5 C7,4.8,7.2,5,7.5,5C7.8,5,8,4.8,8,4.5C8,4.2,7.8,4,7.5,4z M5.5,7C5.2,7,5,7.2,5,7.5C5,7.8,5.2,8,5.5,8C5.8,8,6,7.8,6,7.5 C6,7.2,5.8,7,5.5,7z M3.5,7C3.2,7,3,7.2,3,7.5C3,7.8,3.2,8,3.5,8C3.8,8,4,7.8,4,7.5C4,7.2,3.8,7,3.5,7z M0.5,2C0.8,2,1,1.8,1,1.5v-1 C1,0.2,0.8,0,0.5,0C0.2,0,0,0.2,0,0.5v1C0,1.8,0.2,2,0.5,2z M8,0.5C8,0.2,7.8,0,7.5,0h-1C6.2,0,6,0.2,6,0.5C6,0.8,6.2,1,6.5,1h1 C7.8,1,8,0.8,8,0.5z M7.5,6C7.2,6,7,6.2,7,6.5v1C7,7.8,7.2,8,7.5,8C7.8,8,8,7.8,8,7.5v-1C8,6.2,7.8,6,7.5,6z M1.5,7h-1 C0.2,7,0,7.2,0,7.5C0,7.8,0.2,8,0.5,8h1C1.8,8,2,7.8,2,7.5C2,7.2,1.8,7,1.5,7z"/>
|
||||
<use xlink:href="#class-block" fill-opacity=".2"/>
|
||||
</g>
|
||||
</g>
|
||||
</defs>
|
||||
<use xlink:href="#pseudo-class-shape" id="pseudo-class" color="#edf0f1"/>
|
||||
<use xlink:href="#pseudo-class-shape" id="pseudo-class-checked" color="#3089C9"/>
|
||||
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
@ -273,3 +273,11 @@
|
||||
background-image: url("chrome://browser/skin/devtools/add.svg");
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#pseudo-class-panel-toggle::before {
|
||||
background-image: url("chrome://browser/skin/devtools/pseudo-class.svg#pseudo-class");
|
||||
background-size: cover;
|
||||
}
|
||||
#pseudo-class-panel-toggle[checked]::before {
|
||||
background-image: url("chrome://browser/skin/devtools/pseudo-class.svg#pseudo-class-checked");
|
||||
}
|
||||
|
@ -368,8 +368,8 @@
|
||||
|
||||
/* Searchbox is a div container element for a search input element */
|
||||
.devtools-searchbox {
|
||||
display: -moz-box;
|
||||
-moz-box-flex: 1;
|
||||
display: flex;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,7 @@ browser.jar:
|
||||
skin/classic/browser/devtools/add.svg (../shared/devtools/images/add.svg)
|
||||
skin/classic/browser/devtools/filters.svg (../shared/devtools/filters.svg)
|
||||
skin/classic/browser/devtools/filter-swatch.svg (../shared/devtools/images/filter-swatch.svg)
|
||||
skin/classic/browser/devtools/pseudo-class.svg (../shared/devtools/images/pseudo-class.svg)
|
||||
skin/classic/browser/devtools/controls.png (../shared/devtools/images/controls.png)
|
||||
skin/classic/browser/devtools/controls@2x.png (../shared/devtools/images/controls@2x.png)
|
||||
skin/classic/browser/devtools/performance-icons.svg (../shared/devtools/images/performance-icons.svg)
|
||||
@ -563,6 +564,7 @@ browser.jar:
|
||||
skin/classic/browser/warning16.png (../shared/warning16.png)
|
||||
skin/classic/browser/warning16@2x.png (../shared/warning16@2x.png)
|
||||
|
||||
../extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
% override chrome://browser/skin/page-livemarks.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon.png chrome://browser/skin/feeds/feedIcon.png
|
||||
% override chrome://browser/skin/feeds/audioFeedIcon16.png chrome://browser/skin/feeds/feedIcon16.png
|
||||
|
@ -947,6 +947,29 @@ nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx, int line
|
||||
return;
|
||||
}
|
||||
|
||||
if (cx.mType == NS_SKIN_LOCATION) {
|
||||
bool chromeSkinOnly = false;
|
||||
nsresult rv = chromeuri->SchemeIs("chrome", &chromeSkinOnly);
|
||||
chromeSkinOnly = chromeSkinOnly && NS_SUCCEEDED(rv);
|
||||
if (chromeSkinOnly) {
|
||||
rv = resolveduri->SchemeIs("chrome", &chromeSkinOnly);
|
||||
chromeSkinOnly = chromeSkinOnly && NS_SUCCEEDED(rv);
|
||||
}
|
||||
if (chromeSkinOnly) {
|
||||
nsAutoCString chromePath, resolvedPath;
|
||||
chromeuri->GetPath(chromePath);
|
||||
resolveduri->GetPath(resolvedPath);
|
||||
chromeSkinOnly = StringBeginsWith(chromePath, NS_LITERAL_CSTRING("/skin/")) &&
|
||||
StringBeginsWith(resolvedPath, NS_LITERAL_CSTRING("/skin/"));
|
||||
}
|
||||
if (!chromeSkinOnly) {
|
||||
LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
|
||||
"Cannot register non-chrome://.../skin/ URIs '%s' and '%s' as overrides and/or to be overridden from a skin manifest.",
|
||||
chrome, resolved);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CanLoadResource(resolveduri)) {
|
||||
LogMessageWithContext(cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
|
||||
"Cannot register non-local URI '%s' for an override.", resolved);
|
||||
|
@ -70,6 +70,10 @@ public class GlobalConstants {
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", // 20+
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", // 20+
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 11+
|
||||
|
||||
// For Sync 1.1.
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA", // 9+
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA", // 9+
|
||||
};
|
||||
} else if (Versions.feature11Plus) {
|
||||
DEFAULT_CIPHER_SUITES = new String[]
|
||||
@ -77,7 +81,10 @@ public class GlobalConstants {
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", // 11+
|
||||
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", // 11+
|
||||
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // 11+
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA", // 9+
|
||||
|
||||
// For Sync 1.1.
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA", // 9+
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA", // 9+
|
||||
};
|
||||
} else { // 9+
|
||||
// Fall back to the only half-decent cipher suites supported on Gingerbread.
|
||||
@ -91,8 +98,8 @@ public class GlobalConstants {
|
||||
"TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
|
||||
|
||||
// This is for Sync 1.1.
|
||||
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", // 9+
|
||||
"TLS_RSA_WITH_AES_256_CBC_SHA", // 9+
|
||||
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA", // 9+
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA", // 9+
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
@ -47,11 +47,11 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:21.+'
|
||||
compile 'com.android.support:support-v4:22.+'
|
||||
|
||||
if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
|
||||
compile 'com.android.support:appcompat-v7:21.+'
|
||||
compile 'com.android.support:mediarouter-v7:21.+'
|
||||
compile 'com.android.support:appcompat-v7:22.+'
|
||||
compile 'com.android.support:mediarouter-v7:22.+'
|
||||
compile 'com.google.android.gms:play-services-base:6.5.+'
|
||||
compile 'com.google.android.gms:play-services-cast:6.5.+'
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
@ -29,5 +29,5 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:21.+'
|
||||
compile 'com.android.support:support-v4:22.+'
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 21
|
||||
compileSdkVersion 22
|
||||
buildToolsVersion "22.0.1"
|
||||
|
||||
defaultConfig {
|
||||
targetSdkVersion 21
|
||||
targetSdkVersion 22
|
||||
minSdkVersion 9
|
||||
}
|
||||
|
||||
@ -28,5 +28,5 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:21.+'
|
||||
compile 'com.android.support:support-v4:22.+'
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class JarMaker(object):
|
||||
'''
|
||||
|
||||
ignore = re.compile('\s*(\#.*)?$')
|
||||
jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/]+).jar\:)|(?:\s*(\#.*)?)\s*$')
|
||||
jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/{}]+).jar\:)|(?:\s*(\#.*)?)\s*$')
|
||||
relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
|
||||
regline = re.compile('\%\s+(.*)$')
|
||||
entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
|
||||
|
@ -1018,6 +1018,8 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
||||
{ "goto.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "groups.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "gstatic.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "gvt2.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "gvt3.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "hangouts.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "history.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
{ "hostedtalkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
@ -1089,8 +1091,8 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
|
||||
{ "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
|
||||
};
|
||||
|
||||
// Pinning Preload List Length = 350;
|
||||
// Pinning Preload List Length = 352;
|
||||
|
||||
static const int32_t kUnknownId = -1;
|
||||
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1441448291452000);
|
||||
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1442053104631000);
|
||||
|
@ -1,6 +1,6 @@
|
||||
300651.ru: did not receive HSTS header
|
||||
56ct.com: could not connect to host
|
||||
9point6.com: could not connect to host
|
||||
activiti.alfresco.com: did not receive HSTS header
|
||||
adamkostecki.de: could not connect to host
|
||||
admin.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
adsfund.org: could not connect to host
|
||||
@ -25,6 +25,9 @@ atavio.at: could not connect to host
|
||||
atavio.ch: could not connect to host
|
||||
atavio.de: did not receive HSTS header
|
||||
au.search.yahoo.com: did not receive HSTS header
|
||||
aurainfosec.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
auraredeye.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
auraredshield.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
auth.mail.ru: did not receive HSTS header
|
||||
auto4trade.nl: did not receive HSTS header
|
||||
az.search.yahoo.com: did not receive HSTS header
|
||||
@ -37,7 +40,6 @@ bccx.com: could not connect to host
|
||||
bcm.com.au: max-age too low: 0
|
||||
be.search.yahoo.com: did not receive HSTS header
|
||||
bedeta.de: could not connect to host
|
||||
bentertain.de: could not connect to host
|
||||
betnet.fr: could not connect to host
|
||||
bi.search.yahoo.com: did not receive HSTS header
|
||||
bidon.ca: did not receive HSTS header
|
||||
@ -48,12 +50,14 @@ bitgo.com: max-age too low: 0
|
||||
bizon.sk: did not receive HSTS header
|
||||
blacklane.com: did not receive HSTS header
|
||||
blog.lookout.com: did not receive HSTS header
|
||||
blubbablasen.de: could not connect to host
|
||||
bonigo.de: did not receive HSTS header
|
||||
br.search.yahoo.com: did not receive HSTS header
|
||||
braintreepayments.com: did not receive HSTS header
|
||||
brainvation.de: did not receive HSTS header
|
||||
bran.cc: could not connect to host
|
||||
browserid.org: did not receive HSTS header
|
||||
brunosouza.org: could not connect to host
|
||||
business.medbank.com.mt: did not receive HSTS header
|
||||
buttercoin.com: did not receive HSTS header
|
||||
ca.search.yahoo.com: did not receive HSTS header
|
||||
@ -75,7 +79,7 @@ cheesetart.my: did not receive HSTS header
|
||||
chfr.search.yahoo.com: did not receive HSTS header
|
||||
chit.search.yahoo.com: did not receive HSTS header
|
||||
chm.vn: did not receive HSTS header
|
||||
chontalpa.pw: did not receive HSTS header
|
||||
chontalpa.pw: could not connect to host
|
||||
chrome-devtools-frontend.appspot.com: did not receive HSTS header (error ignored - included regardless)
|
||||
chrome.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
cimballa.com: did not receive HSTS header
|
||||
@ -101,10 +105,10 @@ ct.search.yahoo.com: did not receive HSTS header
|
||||
cujanovic.com: did not receive HSTS header
|
||||
cyanogenmod.xxx: could not connect to host
|
||||
cybershambles.com: could not connect to host
|
||||
dash-board.jp: could not connect to host
|
||||
datasnitch.co.uk: could not connect to host
|
||||
daylightcompany.com: did not receive HSTS header
|
||||
de.search.yahoo.com: did not receive HSTS header
|
||||
deadbeef.ninja: could not connect to host
|
||||
decibelios.li: did not receive HSTS header
|
||||
destinationbijoux.fr: max-age too low: 2678400
|
||||
devh.de: did not receive HSTS header
|
||||
@ -136,6 +140,7 @@ esec.rs: did not receive HSTS header
|
||||
espanol.search.yahoo.com: did not receive HSTS header
|
||||
espra.com: could not connect to host
|
||||
etsysecure.com: could not connect to host
|
||||
exiahost.com: did not receive HSTS header
|
||||
ezimoeko.net: did not receive HSTS header
|
||||
eztv.ch: did not receive HSTS header
|
||||
fabianfischer.de: did not receive HSTS header
|
||||
@ -145,6 +150,7 @@ firebaseio.com: could not connect to host
|
||||
fixingdns.com: did not receive HSTS header
|
||||
fj.search.yahoo.com: did not receive HSTS header
|
||||
fleximus.org: max-age too low: 1
|
||||
floweslawncare.com: could not connect to host
|
||||
fm83.nl: did not receive HSTS header
|
||||
fonetiq.io: could not connect to host
|
||||
fr.search.yahoo.com: did not receive HSTS header
|
||||
@ -163,14 +169,17 @@ googlemail.com: did not receive HSTS header (error ignored - included regardless
|
||||
googleplex.com: could not connect to host
|
||||
googleplex.com: could not connect to host (error ignored - included regardless)
|
||||
goto.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
gotowned.org: did not receive HSTS header
|
||||
gparent.org: did not receive HSTS header
|
||||
gr.search.yahoo.com: did not receive HSTS header
|
||||
grandmascookieblog.com: did not receive HSTS header
|
||||
greplin.com: could not connect to host
|
||||
groups.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
guidetoiceland.is: did not receive HSTS header
|
||||
hackerone-user-content.com: could not connect to host
|
||||
gvt2.com: could not connect to host (error ignored - included regardless)
|
||||
gvt3.com: could not connect to host (error ignored - included regardless)
|
||||
hangouts.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
hash-list.com: could not connect to host
|
||||
hatoko.net: could not connect to host
|
||||
history.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
hk.search.yahoo.com: did not receive HSTS header
|
||||
@ -209,6 +218,7 @@ jkbuster.com: could not connect to host
|
||||
johners.me: could not connect to host
|
||||
jottit.com: could not connect to host
|
||||
julian-kipka.de: did not receive HSTS header
|
||||
jwilsson.com: did not receive HSTS header
|
||||
k-dev.de: could not connect to host
|
||||
kamikano.com: did not receive HSTS header
|
||||
keeley.gq: could not connect to host
|
||||
@ -246,13 +256,11 @@ lumi.do: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FA
|
||||
luxus-russen.de: could not connect to host
|
||||
lv.search.yahoo.com: did not receive HSTS header
|
||||
m.gparent.org: could not connect to host
|
||||
maff.scot: did not receive HSTS header
|
||||
mail.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
maktoob.search.yahoo.com: did not receive HSTS header
|
||||
malaysia.search.yahoo.com: did not receive HSTS header
|
||||
manage.zenpayroll.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
market.android.com: did not receive HSTS header (error ignored - included regardless)
|
||||
markusueberallassetmanagement.de: could not connect to host
|
||||
marshut.net: could not connect to host
|
||||
mccrypto.de: could not connect to host
|
||||
mediacru.sh: could not connect to host
|
||||
@ -267,6 +275,7 @@ mnemotiv.com: could not connect to host
|
||||
mobilethreat.net: could not connect to host
|
||||
mobilethreatnetwork.net: could not connect to host
|
||||
mocloud.eu: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
mpreserver.com: could not connect to host
|
||||
mqas.net: could not connect to host
|
||||
mt.search.yahoo.com: did not receive HSTS header
|
||||
mu.search.yahoo.com: did not receive HSTS header
|
||||
@ -300,6 +309,7 @@ npw.net: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FA
|
||||
null-sec.ru: could not connect to host
|
||||
nz.search.yahoo.com: did not receive HSTS header
|
||||
nzb.cat: did not receive HSTS header
|
||||
ooonja.de: could not connect to host
|
||||
opendesk.cc: did not receive HSTS header
|
||||
openshift.redhat.com: did not receive HSTS header
|
||||
otakurepublic.com: did not receive HSTS header
|
||||
@ -311,6 +321,7 @@ passwordbox.com: did not receive HSTS header
|
||||
passwords.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
pe.search.yahoo.com: did not receive HSTS header
|
||||
ph.search.yahoo.com: did not receive HSTS header
|
||||
phurl.de: could not connect to host
|
||||
piratenlogin.de: could not connect to host
|
||||
pisidia.de: did not receive HSTS header
|
||||
pk.search.yahoo.com: did not receive HSTS header
|
||||
@ -322,7 +333,6 @@ popcorntime.ws: max-age too low: 2592000
|
||||
pr.search.yahoo.com: did not receive HSTS header
|
||||
pressfreedomfoundation.org: did not receive HSTS header
|
||||
prodpad.com: did not receive HSTS header
|
||||
projektzentrisch.de: could not connect to host
|
||||
promecon-gmbh.de: did not receive HSTS header
|
||||
proximato.com: could not connect to host
|
||||
pult.co: could not connect to host
|
||||
@ -342,7 +352,6 @@ ro.search.yahoo.com: did not receive HSTS header
|
||||
roan24.pl: did not receive HSTS header
|
||||
roddis.net: did not receive HSTS header
|
||||
ru.search.yahoo.com: did not receive HSTS header
|
||||
rubyshop.nl: did not receive HSTS header
|
||||
rw.search.yahoo.com: did not receive HSTS header
|
||||
sah3.net: could not connect to host
|
||||
saturngames.co.uk: could not connect to host
|
||||
@ -351,6 +360,7 @@ script.google.com: did not receive HSTS header (error ignored - included regardl
|
||||
sdsl-speedtest.de: could not connect to host
|
||||
se.search.yahoo.com: did not receive HSTS header
|
||||
search.yahoo.com: did not receive HSTS header
|
||||
security-carpet.com: could not connect to host
|
||||
security.google.com: did not receive HSTS header (error ignored - included regardless)
|
||||
sellocdn.com: could not connect to host
|
||||
semenkovich.com: did not receive HSTS header
|
||||
@ -358,7 +368,6 @@ seomobo.com: did not receive HSTS header
|
||||
seowarp.net: max-age too low: 1576800
|
||||
serverdensity.io: did not receive HSTS header
|
||||
sg.search.yahoo.com: did not receive HSTS header
|
||||
sh-network.de: could not connect to host
|
||||
shops.neonisi.com: could not connect to host
|
||||
siammedia.co: did not receive HSTS header
|
||||
silentcircle.org: could not connect to host
|
||||
@ -383,7 +392,6 @@ sunshinepress.org: could not connect to host
|
||||
surfeasy.com: did not receive HSTS header
|
||||
suzukikenichi.com: did not receive HSTS header
|
||||
sv.search.yahoo.com: did not receive HSTS header
|
||||
sychov.pro: could not connect to host
|
||||
sylaps.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
t.facebook.com: did not receive HSTS header
|
||||
tablet.facebook.com: did not receive HSTS header
|
||||
@ -423,10 +431,9 @@ ve.search.yahoo.com: did not receive HSTS header
|
||||
vhost.co.id: could not connect to host
|
||||
viennan.net: did not receive HSTS header
|
||||
vn.search.yahoo.com: did not receive HSTS header
|
||||
vyncke.org: did not receive HSTS header
|
||||
vyncke.org: max-age too low: 2678400
|
||||
webmail.mayfirst.org: did not receive HSTS header
|
||||
wikidsystems.com: did not receive HSTS header
|
||||
willnorris.com: could not connect to host
|
||||
withustrading.com: did not receive HSTS header
|
||||
wiz.biz: could not connect to host
|
||||
wohnungsbau-ludwigsburg.de: did not receive HSTS header
|
||||
@ -455,5 +462,6 @@ xtream-hosting.eu: could not connect to host
|
||||
xtreamhosting.eu: could not connect to host
|
||||
za.search.yahoo.com: did not receive HSTS header
|
||||
zarooba.com: did not receive HSTS header
|
||||
zenpayroll.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134" data: no]
|
||||
zh.search.yahoo.com: did not receive HSTS header
|
||||
zoo24.de: could not connect to host
|
||||
|
@ -8,7 +8,7 @@
|
||||
/*****************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1443867487249000);
|
||||
const PRTime gPreloadListExpirationTime = INT64_C(1444472299639000);
|
||||
|
||||
class nsSTSPreload
|
||||
{
|
||||
@ -39,11 +39,11 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "5apps.com", false },
|
||||
{ "7183.org", true },
|
||||
{ "8ack.de", true },
|
||||
{ "9point6.com", true },
|
||||
{ "abmahnhelfer.de", true },
|
||||
{ "accounts.firefox.com", true },
|
||||
{ "accounts.google.com", true },
|
||||
{ "aclu.org", false },
|
||||
{ "activiti.alfresco.com", false },
|
||||
{ "acuica.co.uk", false },
|
||||
{ "acus.gov", true },
|
||||
{ "adamkostecki.de", true },
|
||||
@ -512,7 +512,6 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "everhome.de", true },
|
||||
{ "eveshamglass.co.uk", true },
|
||||
{ "evstatus.com", true },
|
||||
{ "exiahost.com", false },
|
||||
{ "exon.io", true },
|
||||
{ "expatads.com", true },
|
||||
{ "explodie.org", true },
|
||||
@ -636,7 +635,6 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "googleplex.com", true },
|
||||
{ "gothamlimo.com", true },
|
||||
{ "goto.google.com", true },
|
||||
{ "gotowned.org", true },
|
||||
{ "gotspot.com", true },
|
||||
{ "gplintegratedit.com", true },
|
||||
{ "gpsfix.cz", true },
|
||||
@ -659,11 +657,14 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "guru-naradi.cz", true },
|
||||
{ "gurusupe.com", true },
|
||||
{ "guthabenkarten-billiger.de", true },
|
||||
{ "gvt2.com", true },
|
||||
{ "gvt3.com", true },
|
||||
{ "gw2treasures.com", true },
|
||||
{ "gwijaya.com", true },
|
||||
{ "haber1903.com", true },
|
||||
{ "hachre.de", false },
|
||||
{ "hack.li", true },
|
||||
{ "hackerone-user-content.com", true },
|
||||
{ "hackerone.com", true },
|
||||
{ "hangouts.google.com", true },
|
||||
{ "hansvaneijsden.com", true },
|
||||
@ -808,7 +809,6 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "jpbike.cz", true },
|
||||
{ "jrc9.ca", true },
|
||||
{ "julianmeyer.de", true },
|
||||
{ "jwilsson.com", true },
|
||||
{ "jwilsson.me", true },
|
||||
{ "jwnotifier.org", true },
|
||||
{ "k-dev.de", true },
|
||||
@ -950,6 +950,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "madars.org", true },
|
||||
{ "madeitwor.se", true },
|
||||
{ "mafamane.com", true },
|
||||
{ "maff.scot", false },
|
||||
{ "magneticanvil.com", true },
|
||||
{ "mahamed91.pw", true },
|
||||
{ "mail.de", true },
|
||||
@ -1071,7 +1072,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "mutantmonkey.sexy", true },
|
||||
{ "mvno.io", true },
|
||||
{ "mvsecurity.nl", true },
|
||||
{ "mwe.st", true },
|
||||
{ "mwe.st", false },
|
||||
{ "my.onlime.ch", false },
|
||||
{ "my.xero.com", false },
|
||||
{ "myaccount.google.com", true },
|
||||
@ -1337,6 +1338,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "ru-sprachstudio.ch", true },
|
||||
{ "rubecodeberg.com", true },
|
||||
{ "rubendv.be", true },
|
||||
{ "rubyshop.nl", true },
|
||||
{ "rudloff.pro", true },
|
||||
{ "rusadmin.biz", true },
|
||||
{ "ruudkoot.nl", true },
|
||||
@ -1644,7 +1646,7 @@ static const nsSTSPreload kSTSPreloadList[] = {
|
||||
{ "twolinepassbrewing.com", true },
|
||||
{ "typingrevolution.com", true },
|
||||
{ "uae-company-service.com", true },
|
||||
{ "ub3rk1tten.com", true },
|
||||
{ "ub3rk1tten.com", false },
|
||||
{ "ubertt.org", true },
|
||||
{ "ucfirst.nl", true },
|
||||
{ "ukdefencejournal.org.uk", true },
|
||||
|
@ -131,7 +131,7 @@ HistoryStore.prototype = {
|
||||
"SELECT visit_type type, visit_date date " +
|
||||
"FROM moz_historyvisits " +
|
||||
"WHERE place_id = (SELECT id FROM moz_places WHERE url = :url) " +
|
||||
"ORDER BY date DESC LIMIT 10");
|
||||
"ORDER BY date DESC LIMIT 20");
|
||||
},
|
||||
_visitCols: ["date", "type"],
|
||||
|
||||
@ -276,14 +276,14 @@ HistoryStore.prototype = {
|
||||
if (!visit.date || typeof visit.date != "number") {
|
||||
this._log.warn("Encountered record with invalid visit date: "
|
||||
+ visit.date);
|
||||
throw "Visit has no date!";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!visit.type || !(visit.type >= PlacesUtils.history.TRANSITION_LINK &&
|
||||
visit.type <= PlacesUtils.history.TRANSITION_FRAMED_LINK)) {
|
||||
this._log.warn("Encountered record with invalid visit type: "
|
||||
+ visit.type);
|
||||
throw "Invalid visit type!";
|
||||
this._log.warn("Encountered record with invalid visit type: " +
|
||||
visit.type + "; ignoring.");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dates need to be integers.
|
||||
@ -294,6 +294,7 @@ HistoryStore.prototype = {
|
||||
// overwritten.
|
||||
continue;
|
||||
}
|
||||
|
||||
visit.visitDate = visit.date;
|
||||
visit.transitionType = visit.type;
|
||||
k += 1;
|
||||
|
@ -226,7 +226,7 @@ add_test(function test_invalid_records() {
|
||||
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
|
||||
]);
|
||||
|
||||
_("Make sure we report records with invalid visits, gracefully handle non-integer dates.");
|
||||
_("Make sure we handle records with invalid visit codes or visit dates, gracefully ignoring those visits.");
|
||||
let no_date_visit_guid = Utils.makeGUID();
|
||||
let no_type_visit_guid = Utils.makeGUID();
|
||||
let invalid_type_visit_guid = Utils.makeGUID();
|
||||
@ -235,11 +235,11 @@ add_test(function test_invalid_records() {
|
||||
{id: no_date_visit_guid,
|
||||
histUri: "http://no.date.visit/",
|
||||
title: "Visit has no date",
|
||||
visits: [{date: TIMESTAMP3}]},
|
||||
visits: [{type: Ci.nsINavHistoryService.TRANSITION_EMBED}]},
|
||||
{id: no_type_visit_guid,
|
||||
histUri: "http://no.type.visit/",
|
||||
title: "Visit has no type",
|
||||
visits: [{type: Ci.nsINavHistoryService.TRANSITION_EMBED}]},
|
||||
visits: [{date: TIMESTAMP3}]},
|
||||
{id: invalid_type_visit_guid,
|
||||
histUri: "http://invalid.type.visit/",
|
||||
title: "Visit has invalid type",
|
||||
@ -251,14 +251,7 @@ add_test(function test_invalid_records() {
|
||||
visits: [{date: 1234.567,
|
||||
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
|
||||
]);
|
||||
do_check_eq(failed.length, 3);
|
||||
failed.sort();
|
||||
let expected = [no_date_visit_guid,
|
||||
no_type_visit_guid,
|
||||
invalid_type_visit_guid].sort();
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
do_check_eq(failed[i], expected[i]);
|
||||
}
|
||||
do_check_eq(failed.length, 0);
|
||||
|
||||
_("Make sure we handle records with javascript: URLs gracefully.");
|
||||
applyEnsureNoFailures([
|
||||
|
@ -71,7 +71,7 @@ var HistoryEntry = {
|
||||
"SELECT id " +
|
||||
"FROM moz_places " +
|
||||
"WHERE url = :url) " +
|
||||
"ORDER BY date DESC LIMIT 10");
|
||||
"ORDER BY date DESC LIMIT 20");
|
||||
this.__defineGetter__("_visitStm", () => stm);
|
||||
return stm;
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ if (Cc === undefined) {
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
@ -74,10 +75,18 @@ function installAddon(url) {
|
||||
resolve(addon);
|
||||
},
|
||||
|
||||
onDownloadCancelled: function(install) {
|
||||
reject("Download cancelled: " + install.error);
|
||||
},
|
||||
|
||||
onDownloadFailed: function(install) {
|
||||
reject("Download failed: " + install.error);
|
||||
},
|
||||
|
||||
onInstallCancelled: function(install) {
|
||||
reject("Install cancelled: " + install.error);
|
||||
},
|
||||
|
||||
onInstallFailed: function(install) {
|
||||
reject("Install failed: " + install.error);
|
||||
}
|
||||
@ -96,10 +105,11 @@ function uninstallAddon(oldAddon) {
|
||||
if (addon.id != oldAddon.id)
|
||||
return;
|
||||
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Uninstalled test add-on " + addon.id + "\n");
|
||||
|
||||
// Some add-ons do async work on uninstall, we must wait for that to
|
||||
// complete
|
||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
timer.init(resolve, 500, timer.TYPE_ONE_SHOT);
|
||||
setTimeout(resolve, 500);
|
||||
}
|
||||
});
|
||||
|
||||
@ -119,13 +129,13 @@ function waitForResults() {
|
||||
}
|
||||
|
||||
// Runs tests for the add-on available at URL.
|
||||
let testAddon = Task.async(function*({ url, expected }) {
|
||||
let testAddon = Task.async(function*({ url }) {
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Installing test add-on " + realPath(url) + "\n");
|
||||
let addon = yield installAddon(url);
|
||||
|
||||
let results = yield waitForResults();
|
||||
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Uninstalling test add-on " + realPath(url) + "\n");
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Uninstalling test add-on " + addon.id + "\n");
|
||||
yield uninstallAddon(addon);
|
||||
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Testing add-on " + realPath(url) + " is complete\n");
|
||||
@ -197,6 +207,8 @@ function testInit() {
|
||||
}
|
||||
|
||||
if (config.closeWhenDone) {
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Shutting down.\n");
|
||||
|
||||
const appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
|
||||
getService(Ci.nsIAppStartup);
|
||||
appStartup.quit(appStartup.eAttemptQuit);
|
||||
@ -208,6 +220,7 @@ function testInit() {
|
||||
return finish();
|
||||
|
||||
let filename = fileNames.shift();
|
||||
dump("TEST-INFO | jetpack-addon-harness.js | Starting test add-on " + realPath(filename.url) + "\n");
|
||||
testAddon(filename).then(results => {
|
||||
passed += results.passed;
|
||||
failed += results.failed;
|
||||
|
@ -51,15 +51,6 @@ const PING_FORMAT_VERSION = 4;
|
||||
const TELEMETRY_DELAY = 60000;
|
||||
// Delay before initializing telemetry if we're testing (ms)
|
||||
const TELEMETRY_TEST_DELAY = 100;
|
||||
// Timeout after which we consider a ping submission failed.
|
||||
const PING_SUBMIT_TIMEOUT_MS = 2 * 60 * 1000;
|
||||
|
||||
// For midnight fuzzing we want to affect pings around midnight with this tolerance.
|
||||
const MIDNIGHT_TOLERANCE_FUZZ_MS = 5 * 60 * 1000;
|
||||
// We try to spread "midnight" pings out over this interval.
|
||||
const MIDNIGHT_FUZZING_INTERVAL_MS = 60 * 60 * 1000;
|
||||
// We delay sending "midnight" pings on this client by this interval.
|
||||
const MIDNIGHT_FUZZING_DELAY_MS = Math.random() * MIDNIGHT_FUZZING_INTERVAL_MS;
|
||||
|
||||
// Ping types.
|
||||
const PING_TYPE_MAIN = "main";
|
||||
@ -89,6 +80,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetryArchive",
|
||||
"resource://gre/modules/TelemetryArchive.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySession",
|
||||
"resource://gre/modules/TelemetrySession.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetrySend",
|
||||
"resource://gre/modules/TelemetrySend.jsm");
|
||||
|
||||
/**
|
||||
* Setup Telemetry logging. This function also gets called when loggin related
|
||||
@ -123,34 +116,11 @@ function configureLogging() {
|
||||
}
|
||||
}
|
||||
|
||||
function generateUUID() {
|
||||
let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
|
||||
// strip {}
|
||||
return str.substring(1, str.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the ping has new ping format or a legacy one.
|
||||
*/
|
||||
function isNewPingFormat(aPing) {
|
||||
return ("id" in aPing) && ("application" in aPing) &&
|
||||
("version" in aPing) && (aPing.version >= 2);
|
||||
}
|
||||
|
||||
function tomorrow(date) {
|
||||
let d = new Date(date);
|
||||
d.setDate(d.getDate() + 1);
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a policy object used to override behavior for testing.
|
||||
*/
|
||||
let Policy = {
|
||||
now: () => new Date(),
|
||||
midnightPingFuzzingDelay: () => MIDNIGHT_FUZZING_DELAY_MS,
|
||||
setPingSendTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
|
||||
clearPingSendTimeout: (id) => clearTimeout(id),
|
||||
}
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TelemetryController"];
|
||||
@ -174,6 +144,8 @@ this.TelemetryController = Object.freeze({
|
||||
reset: function() {
|
||||
Impl._clientID = null;
|
||||
TelemetryStorage.reset();
|
||||
TelemetrySend.reset();
|
||||
|
||||
return this.setup();
|
||||
},
|
||||
/**
|
||||
@ -190,13 +162,6 @@ this.TelemetryController = Object.freeze({
|
||||
return Impl.observe(aSubject, aTopic, aData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets a server to send pings to.
|
||||
*/
|
||||
setServer: function(aServer) {
|
||||
return Impl.setServer(aServer);
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit ping payloads to Telemetry. This will assemble a complete ping, adding
|
||||
* environment data, client id and some general info.
|
||||
@ -318,15 +283,6 @@ this.TelemetryController = Object.freeze({
|
||||
return Impl.savePing(aType, aPayload, aFilePath, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the persisted pings to the server.
|
||||
*
|
||||
* @return Promise A promise that is resolved when all pings finished sending or failed.
|
||||
*/
|
||||
sendPersistedPings: function() {
|
||||
return Impl.sendPersistedPings();
|
||||
},
|
||||
|
||||
/**
|
||||
* The client id send with the telemetry ping.
|
||||
*
|
||||
@ -374,8 +330,6 @@ let Impl = {
|
||||
_delayedInitTask: null,
|
||||
// The deferred promise resolved when the initialization task completes.
|
||||
_delayedInitTaskDeferred: null,
|
||||
// Timer for scheduled ping sends.
|
||||
_pingSendTimer: null,
|
||||
|
||||
// The session recorder, shared with FHR and the Data Reporting Service.
|
||||
_sessionRecorder: null,
|
||||
@ -388,9 +342,6 @@ let Impl = {
|
||||
// This is true when running in the test infrastructure.
|
||||
_testMode: false,
|
||||
|
||||
// This tracks all pending ping requests to the server.
|
||||
_pendingPingRequests: new Map(),
|
||||
|
||||
get _log() {
|
||||
if (!this._logger) {
|
||||
this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
|
||||
@ -446,8 +397,7 @@ let Impl = {
|
||||
* @returns Promise<Object> A promise that resolves when the ping is completely assembled.
|
||||
*/
|
||||
assemblePing: function assemblePing(aType, aPayload, aOptions = {}) {
|
||||
this._log.trace("assemblePing - Type " + aType + ", Server " + this._server +
|
||||
", aOptions " + JSON.stringify(aOptions));
|
||||
this._log.trace("assemblePing - Type " + aType + ", aOptions " + JSON.stringify(aOptions));
|
||||
|
||||
// Clone the payload data so we don't race against unexpected changes in subobjects that are
|
||||
// still referenced by other code.
|
||||
@ -457,7 +407,7 @@ let Impl = {
|
||||
// Fill the common ping fields.
|
||||
let pingData = {
|
||||
type: aType,
|
||||
id: generateUUID(),
|
||||
id: Utils.generateUUID(),
|
||||
creationDate: (Policy.now()).toISOString(),
|
||||
version: PING_FORMAT_VERSION,
|
||||
application: this._getApplicationSection(),
|
||||
@ -475,26 +425,6 @@ let Impl = {
|
||||
return pingData;
|
||||
},
|
||||
|
||||
popPayloads: function popPayloads() {
|
||||
this._log.trace("popPayloads");
|
||||
function payloadIter() {
|
||||
let iterator = TelemetryStorage.popPendingPings();
|
||||
for (let data of iterator) {
|
||||
yield data;
|
||||
}
|
||||
}
|
||||
|
||||
let payloadIterWithThis = payloadIter.bind(this);
|
||||
return { __iterator__: payloadIterWithThis };
|
||||
},
|
||||
|
||||
/**
|
||||
* Only used in tests.
|
||||
*/
|
||||
setServer: function (aServer) {
|
||||
this._server = aServer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Track any pending ping send and save tasks through the promise passed here.
|
||||
* This is needed to block shutdown on any outstanding ping activity.
|
||||
@ -503,43 +433,6 @@ let Impl = {
|
||||
this._connectionsBarrier.client.addBlocker("Waiting for ping task", aPromise);
|
||||
},
|
||||
|
||||
/**
|
||||
* This helper calculates the next time that we can send pings at.
|
||||
* Currently this mostly redistributes ping sends around midnight to avoid submission
|
||||
* spikes around local midnight for daily pings.
|
||||
*
|
||||
* @param now Date The current time.
|
||||
* @return Number The next time (ms from UNIX epoch) when we can send pings.
|
||||
*/
|
||||
_getNextPingSendTime: function(now) {
|
||||
// 1. First we check if the time is between 11pm and 1am. If it's not, we send
|
||||
// immediately.
|
||||
// 2. If we confirmed the time is indeed between 11pm and 1am in step 1, we
|
||||
// then check if the time is also 11:55pm or later. If it's not, we send
|
||||
// immediately.
|
||||
// 3. Finally, if the time is between 11:55pm and 1am, we disallow sending
|
||||
// before (midnight + fuzzing delay), which is a random time between 12am-1am
|
||||
// (decided at startup).
|
||||
|
||||
const midnight = Utils.getNearestMidnight(now, MIDNIGHT_FUZZING_INTERVAL_MS);
|
||||
|
||||
// Don't delay ping if we are not close to midnight.
|
||||
if (!midnight) {
|
||||
return now.getTime();
|
||||
}
|
||||
|
||||
// Delay ping send if we are within the midnight fuzzing range.
|
||||
// This is from: |midnight - MIDNIGHT_TOLERANCE_FUZZ_MS|
|
||||
// to: |midnight + MIDNIGHT_FUZZING_INTERVAL_MS|
|
||||
const midnightRangeStart = midnight.getTime() - MIDNIGHT_TOLERANCE_FUZZ_MS;
|
||||
if (now.getTime() >= midnightRangeStart) {
|
||||
// We spread those ping sends out between |midnight| and |midnight + midnightPingFuzzingDelay|.
|
||||
return midnight.getTime() + Policy.midnightPingFuzzingDelay();
|
||||
}
|
||||
|
||||
return now.getTime();
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit ping payloads to Telemetry. This will assemble a complete ping, adding
|
||||
* environment data, client id and some general info.
|
||||
@ -557,8 +450,7 @@ let Impl = {
|
||||
* @returns {Promise} A promise that is resolved with the ping id once the ping is stored or sent.
|
||||
*/
|
||||
submitExternalPing: function send(aType, aPayload, aOptions) {
|
||||
this._log.trace("submitExternalPing - type: " + aType + ", server: " + this._server +
|
||||
", aOptions: " + JSON.stringify(aOptions));
|
||||
this._log.trace("submitExternalPing - type: " + aType + ", aOptions: " + JSON.stringify(aOptions));
|
||||
|
||||
// Enforce the type string to only contain sane characters.
|
||||
const typeUuid = /^[a-z0-9][a-z0-9-]+[a-z0-9]$/i;
|
||||
@ -577,67 +469,13 @@ let Impl = {
|
||||
.catch(e => this._log.error("submitExternalPing - Failed to archive ping " + pingData.id, e));
|
||||
let p = [ archivePromise ];
|
||||
|
||||
// Check if we can send pings now.
|
||||
const now = Policy.now();
|
||||
const nextPingSendTime = this._getNextPingSendTime(now);
|
||||
const throttled = (nextPingSendTime > now.getTime());
|
||||
|
||||
// We can't send pings now, schedule a later send.
|
||||
if (throttled) {
|
||||
this._log.trace("submitExternalPing - throttled, delaying ping send to " + new Date(nextPingSendTime));
|
||||
this._reschedulePingSendTimer(nextPingSendTime);
|
||||
}
|
||||
|
||||
if (!this._initialized || throttled) {
|
||||
// We can't send because we are still initializing or throttled, add this to the pending pings.
|
||||
this._log.trace("submitExternalPing - ping is pending, initialized: " + this._initialized +
|
||||
", throttled: " + throttled);
|
||||
p.push(TelemetryStorage.addPendingPing(pingData));
|
||||
} else {
|
||||
// Try to send the ping, persist it if sending it fails.
|
||||
this._log.trace("submitExternalPing - already initialized, ping will be sent");
|
||||
p.push(this.doPing(pingData, false)
|
||||
.catch(() => TelemetryStorage.savePing(pingData, true)));
|
||||
p.push(this.sendPersistedPings());
|
||||
}
|
||||
p.push(TelemetrySend.submitPing(pingData));
|
||||
|
||||
let promise = Promise.all(p);
|
||||
this._trackPendingPingTask(promise);
|
||||
return promise.then(() => pingData.id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the persisted pings to the server.
|
||||
*
|
||||
* @return Promise A promise that is resolved when all pings finished sending or failed.
|
||||
*/
|
||||
sendPersistedPings: function sendPersistedPings() {
|
||||
this._log.trace("sendPersistedPings - Can send: " + this._canSend());
|
||||
if (!this._canSend()) {
|
||||
this._log.trace("sendPersistedPings - Telemetry is not allowed to send pings.");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Check if we can send pings now - otherwise schedule a later send.
|
||||
const now = Policy.now();
|
||||
const nextPingSendTime = this._getNextPingSendTime(now);
|
||||
if (nextPingSendTime > now.getTime()) {
|
||||
this._log.trace("sendPersistedPings - delaying ping send to " + new Date(nextPingSendTime));
|
||||
this._reschedulePingSendTimer(nextPingSendTime);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// We can send now.
|
||||
let pingsIterator = Iterator(this.popPayloads());
|
||||
let p = [for (data of pingsIterator) this.doPing(data, true).catch((e) => {
|
||||
this._log.error("sendPersistedPings - doPing rejected", e);
|
||||
})];
|
||||
|
||||
let promise = Promise.all(p);
|
||||
this._trackPendingPingTask(promise);
|
||||
return promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a ping to disk.
|
||||
*
|
||||
@ -655,12 +493,11 @@ let Impl = {
|
||||
* disk.
|
||||
*/
|
||||
addPendingPing: function addPendingPing(aType, aPayload, aOptions) {
|
||||
this._log.trace("addPendingPing - Type " + aType + ", Server " + this._server +
|
||||
", aOptions " + JSON.stringify(aOptions));
|
||||
this._log.trace("addPendingPing - Type " + aType + ", aOptions " + JSON.stringify(aOptions));
|
||||
|
||||
let pingData = this.assemblePing(aType, aPayload, aOptions);
|
||||
|
||||
let savePromise = TelemetryStorage.savePing(pingData, aOptions.overwrite);
|
||||
let savePromise = TelemetryStorage.savePendingPing(pingData);
|
||||
let archivePromise = TelemetryArchive.promiseArchivePing(pingData).catch(e => {
|
||||
this._log.error("addPendingPing - Failed to archive ping " + pingData.id, e);
|
||||
});
|
||||
@ -692,8 +529,8 @@ let Impl = {
|
||||
* disk.
|
||||
*/
|
||||
savePing: function savePing(aType, aPayload, aFilePath, aOptions) {
|
||||
this._log.trace("savePing - Type " + aType + ", Server " + this._server +
|
||||
", File Path " + aFilePath + ", aOptions " + JSON.stringify(aOptions));
|
||||
this._log.trace("savePing - Type " + aType + ", File Path " + aFilePath +
|
||||
", aOptions " + JSON.stringify(aOptions));
|
||||
let pingData = this.assemblePing(aType, aPayload, aOptions);
|
||||
return TelemetryStorage.savePingToFile(pingData, aFilePath, aOptions.overwrite)
|
||||
.then(() => pingData.id);
|
||||
@ -738,182 +575,6 @@ let Impl = {
|
||||
return TelemetryStorage.removeAbortedSessionPing();
|
||||
},
|
||||
|
||||
onPingRequestFinished: function(success, startTime, ping, isPersisted) {
|
||||
this._log.trace("onPingRequestFinished - success: " + success + ", persisted: " + isPersisted);
|
||||
|
||||
Telemetry.getHistogramById("TELEMETRY_SEND").add(new Date() - startTime);
|
||||
let hping = Telemetry.getHistogramById("TELEMETRY_PING");
|
||||
let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS");
|
||||
|
||||
hsuccess.add(success);
|
||||
hping.add(new Date() - startTime);
|
||||
|
||||
if (success && isPersisted) {
|
||||
return TelemetryStorage.cleanupPingFile(ping);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
submissionPath: function submissionPath(ping) {
|
||||
// The new ping format contains an "application" section, the old one doesn't.
|
||||
let pathComponents;
|
||||
if (isNewPingFormat(ping)) {
|
||||
// We insert the Ping id in the URL to simplify server handling of duplicated
|
||||
// pings.
|
||||
let app = ping.application;
|
||||
pathComponents = [
|
||||
ping.id, ping.type, app.name, app.version, app.channel, app.buildId
|
||||
];
|
||||
} else {
|
||||
// This is a ping in the old format.
|
||||
if (!("slug" in ping)) {
|
||||
// That's odd, we don't have a slug. Generate one so that TelemetryStorage.jsm works.
|
||||
ping.slug = generateUUID();
|
||||
}
|
||||
|
||||
// Do we have enough info to build a submission URL?
|
||||
let payload = ("payload" in ping) ? ping.payload : null;
|
||||
if (payload && ("info" in payload)) {
|
||||
let info = ping.payload.info;
|
||||
pathComponents = [ ping.slug, info.reason, info.appName, info.appVersion,
|
||||
info.appUpdateChannel, info.appBuildID ];
|
||||
} else {
|
||||
// Only use the UUID as the slug.
|
||||
pathComponents = [ ping.slug ];
|
||||
}
|
||||
}
|
||||
|
||||
let slug = pathComponents.join("/");
|
||||
return "/submit/telemetry/" + slug;
|
||||
},
|
||||
|
||||
doPing: function doPing(ping, isPersisted) {
|
||||
if (!this._canSend()) {
|
||||
// We can't send the pings to the server, so don't try to.
|
||||
this._log.trace("doPing - Sending is disabled.");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this._log.trace("doPing - Server " + this._server + ", Persisted " + isPersisted);
|
||||
const isNewPing = isNewPingFormat(ping);
|
||||
const version = isNewPing ? PING_FORMAT_VERSION : 1;
|
||||
const url = this._server + this.submissionPath(ping) + "?v=" + version;
|
||||
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
request.mozBackgroundRequest = true;
|
||||
request.timeout = PING_SUBMIT_TIMEOUT_MS;
|
||||
|
||||
request.open("POST", url, true);
|
||||
request.overrideMimeType("text/plain");
|
||||
request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
|
||||
|
||||
this._pendingPingRequests.set(url, request);
|
||||
|
||||
let startTime = new Date();
|
||||
let deferred = PromiseUtils.defer();
|
||||
|
||||
let onRequestFinished = (success, event) => {
|
||||
let onCompletion = () => {
|
||||
if (success) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(event);
|
||||
}
|
||||
};
|
||||
|
||||
this._pendingPingRequests.delete(url);
|
||||
this.onPingRequestFinished(success, startTime, ping, isPersisted)
|
||||
.then(() => onCompletion(),
|
||||
(error) => {
|
||||
this._log.error("doPing - request success: " + success + ", error" + error);
|
||||
onCompletion();
|
||||
});
|
||||
};
|
||||
|
||||
let errorhandler = (event) => {
|
||||
this._log.error("doPing - error making request to " + url + ": " + event.type);
|
||||
onRequestFinished(false, event);
|
||||
};
|
||||
request.onerror = errorhandler;
|
||||
request.ontimeout = errorhandler;
|
||||
request.onabort = errorhandler;
|
||||
|
||||
request.onload = (event) => {
|
||||
let status = request.status;
|
||||
let statusClass = status - (status % 100);
|
||||
let success = false;
|
||||
|
||||
if (statusClass === 200) {
|
||||
// We can treat all 2XX as success.
|
||||
this._log.info("doPing - successfully loaded, status: " + status);
|
||||
success = true;
|
||||
} else if (statusClass === 400) {
|
||||
// 4XX means that something with the request was broken.
|
||||
this._log.error("doPing - error submitting to " + url + ", status: " + status
|
||||
+ " - ping request broken?");
|
||||
// TODO: we should handle this better, but for now we should avoid resubmitting
|
||||
// broken requests by pretending success.
|
||||
success = true;
|
||||
} else if (statusClass === 500) {
|
||||
// 5XX means there was a server-side error and we should try again later.
|
||||
this._log.error("doPing - error submitting to " + url + ", status: " + status
|
||||
+ " - server error, should retry later");
|
||||
} else {
|
||||
// We received an unexpected status codes.
|
||||
this._log.error("doPing - error submitting to " + url + ", status: " + status
|
||||
+ ", type: " + event.type);
|
||||
}
|
||||
|
||||
onRequestFinished(success, event);
|
||||
};
|
||||
|
||||
// If that's a legacy ping format, just send its payload.
|
||||
let networkPayload = isNewPing ? ping : ping.payload;
|
||||
request.setRequestHeader("Content-Encoding", "gzip");
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
startTime = new Date();
|
||||
let utf8Payload = converter.ConvertFromUnicode(JSON.stringify(networkPayload));
|
||||
utf8Payload += converter.Finish();
|
||||
Telemetry.getHistogramById("TELEMETRY_STRINGIFY").add(new Date() - startTime);
|
||||
let payloadStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
startTime = new Date();
|
||||
payloadStream.data = this.gzipCompressString(utf8Payload);
|
||||
Telemetry.getHistogramById("TELEMETRY_COMPRESS").add(new Date() - startTime);
|
||||
startTime = new Date();
|
||||
request.send(payloadStream);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
gzipCompressString: function gzipCompressString(string) {
|
||||
let observer = {
|
||||
buffer: "",
|
||||
onStreamComplete: function(loader, context, status, length, result) {
|
||||
this.buffer = String.fromCharCode.apply(this, result);
|
||||
}
|
||||
};
|
||||
|
||||
let scs = Cc["@mozilla.org/streamConverters;1"]
|
||||
.getService(Ci.nsIStreamConverterService);
|
||||
let listener = Cc["@mozilla.org/network/stream-loader;1"]
|
||||
.createInstance(Ci.nsIStreamLoader);
|
||||
listener.init(observer);
|
||||
let converter = scs.asyncConvertData("uncompressed", "gzip",
|
||||
listener, null);
|
||||
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stringStream.data = string;
|
||||
converter.onStartRequest(null, null);
|
||||
converter.onDataAvailable(null, null, stringStream, 0, string.length);
|
||||
converter.onStopRequest(null, null, null);
|
||||
return observer.buffer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Perform telemetry initialization for either chrome or content process.
|
||||
* @return {Boolean} True if Telemetry is allowed to record at least base (FHR) data,
|
||||
@ -935,7 +596,6 @@ let Impl = {
|
||||
}
|
||||
#endif
|
||||
|
||||
this._server = Preferences.get(PREF_SERVER, undefined);
|
||||
if (!enabled || !Telemetry.canRecordBase) {
|
||||
// Turn off extended telemetry recording if disabled by preferences or if base/telemetry
|
||||
// telemetry recording is off.
|
||||
@ -947,7 +607,8 @@ let Impl = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry.
|
||||
* This triggers basic telemetry initialization and schedules a full initialized for later
|
||||
* for performance reasons.
|
||||
*
|
||||
* This delayed initialization means TelemetryController init can be in the following states:
|
||||
* 1) setupTelemetry was never called
|
||||
@ -998,21 +659,10 @@ let Impl = {
|
||||
this._delayedInitTaskDeferred = Promise.defer();
|
||||
this._delayedInitTask = new DeferredTask(function* () {
|
||||
try {
|
||||
// TODO: This should probably happen after all the delayed init here.
|
||||
this._initialized = true;
|
||||
|
||||
// If any pings were submitted before the delayed init finished
|
||||
// we will send them now.
|
||||
yield this.sendPersistedPings();
|
||||
|
||||
// Load pending pings from disk.
|
||||
yield TelemetryStorage.loadSavedPings();
|
||||
// If we have any overdue pings lying around, we'll be aggressive
|
||||
// and try to send them all off ASAP.
|
||||
if (TelemetryStorage.pingsOverdue > 0) {
|
||||
this._log.trace("setupChromeProcess - Sending " + TelemetryStorage.pingsOverdue +
|
||||
" overdue pings now.");
|
||||
yield this.sendPersistedPings();
|
||||
}
|
||||
yield TelemetrySend.setup(this._testMode);
|
||||
|
||||
// Load the ClientID and update the cache.
|
||||
this._clientID = yield ClientID.getClientID();
|
||||
@ -1047,24 +697,13 @@ let Impl = {
|
||||
|
||||
Preferences.ignore(PREF_BRANCH_LOG, configureLogging);
|
||||
|
||||
// Abort any pending ping XHRs.
|
||||
for (let [url, request] of this._pendingPingRequests) {
|
||||
this._log.trace("_cleanupOnShutdown - aborting ping request for " + url);
|
||||
try {
|
||||
request.abort();
|
||||
} catch (e) {
|
||||
this._log.error("_cleanupOnShutdown - failed to abort request to " + url, e);
|
||||
}
|
||||
}
|
||||
this._pendingPingRequests.clear();
|
||||
|
||||
// Now do an orderly shutdown.
|
||||
try {
|
||||
// First wait for clients processing shutdown.
|
||||
yield this._shutdownBarrier.wait();
|
||||
|
||||
// Then clear scheduled ping sends...
|
||||
this._clearPingSendTimer();
|
||||
// Stop any ping sending.
|
||||
yield TelemetrySend.shutdown();
|
||||
|
||||
// ... and wait for any outstanding async ping activity.
|
||||
yield this._connectionsBarrier.wait();
|
||||
@ -1136,29 +775,6 @@ let Impl = {
|
||||
return this._clientID;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if pings can be sent to the server. If FHR is not allowed to upload,
|
||||
* pings are not sent to the server (Telemetry is a sub-feature of FHR).
|
||||
* If unified telemetry is off, don't send pings if Telemetry is disabled.
|
||||
*
|
||||
* @return {Boolean} True if pings can be send to the servers, false otherwise.
|
||||
*/
|
||||
_canSend: function() {
|
||||
// We only send pings from official builds, but allow overriding this for tests.
|
||||
if (!Telemetry.isOfficialTelemetry && !this._testMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// With unified Telemetry, the FHR upload setting controls whether we can send pings.
|
||||
// The Telemetry pref enables sending extended data sets instead.
|
||||
if (IS_UNIFIED_TELEMETRY) {
|
||||
return Preferences.get(PREF_FHR_UPLOAD_ENABLED, false);
|
||||
}
|
||||
|
||||
// Without unified Telemetry, the Telemetry enabled pref controls ping sending.
|
||||
return Preferences.get(PREF_ENABLED, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an object describing the current state of this module for AsyncShutdown diagnostics.
|
||||
*/
|
||||
@ -1172,19 +788,6 @@ let Impl = {
|
||||
};
|
||||
},
|
||||
|
||||
_reschedulePingSendTimer: function(timestamp) {
|
||||
this._clearPingSendTimer();
|
||||
const interval = timestamp - Policy.now();
|
||||
this._pingSendTimer = Policy.setPingSendTimeout(() => this.sendPersistedPings(), interval);
|
||||
},
|
||||
|
||||
_clearPingSendTimer: function() {
|
||||
if (this._pingSendTimer) {
|
||||
Policy.clearPingSendTimeout(this._pingSendTimer);
|
||||
this._pingSendTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Allows waiting for TelemetryControllers delayed initialization to complete.
|
||||
* This will complete before TelemetryController is shutting down.
|
||||
|
710
toolkit/components/telemetry/TelemetrySend.jsm
Normal file
710
toolkit/components/telemetry/TelemetrySend.jsm
Normal file
@ -0,0 +1,710 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/*
|
||||
* This module is responsible for uploading pings to the server and persisting
|
||||
* pings that can't be send now.
|
||||
* Those pending pings are persisted on disk and sent at the next opportunity,
|
||||
* newest first.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"TelemetrySend",
|
||||
];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/Log.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/PromiseUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
|
||||
"resource://gre/modules/AsyncShutdown.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStorage",
|
||||
"resource://gre/modules/TelemetryStorage.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
|
||||
"@mozilla.org/base/telemetry;1",
|
||||
"nsITelemetry");
|
||||
|
||||
const Utils = TelemetryUtils;
|
||||
|
||||
const LOGGER_NAME = "Toolkit.Telemetry";
|
||||
const LOGGER_PREFIX = "TelemetrySend::";
|
||||
|
||||
const PREF_BRANCH = "toolkit.telemetry.";
|
||||
const PREF_SERVER = PREF_BRANCH + "server";
|
||||
const PREF_UNIFIED = PREF_BRANCH + "unified";
|
||||
const PREF_TELEMETRY_ENABLED = PREF_BRANCH + "enabled";
|
||||
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
|
||||
|
||||
const TOPIC_IDLE_DAILY = "idle-daily";
|
||||
const TOPIC_QUIT_APPLICATION = "quit-application";
|
||||
|
||||
// Whether the FHR/Telemetry unification features are enabled.
|
||||
// Changing this pref requires a restart.
|
||||
const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_UNIFIED, false);
|
||||
|
||||
const PING_FORMAT_VERSION = 4;
|
||||
|
||||
// For midnight fuzzing we want to affect pings around midnight with this tolerance.
|
||||
const MIDNIGHT_TOLERANCE_FUZZ_MS = 5 * 60 * 1000;
|
||||
// We try to spread "midnight" pings out over this interval.
|
||||
const MIDNIGHT_FUZZING_INTERVAL_MS = 60 * 60 * 1000;
|
||||
// We delay sending "midnight" pings on this client by this interval.
|
||||
const MIDNIGHT_FUZZING_DELAY_MS = Math.random() * MIDNIGHT_FUZZING_INTERVAL_MS;
|
||||
|
||||
// Timeout after which we consider a ping submission failed.
|
||||
const PING_SUBMIT_TIMEOUT_MS = 2 * 60 * 1000;
|
||||
|
||||
// Files that have been lying around for longer than MAX_PING_FILE_AGE are
|
||||
// deleted without being loaded.
|
||||
const MAX_PING_FILE_AGE = 14 * 24 * 60 * 60 * 1000; // 2 weeks
|
||||
|
||||
// Files that are older than OVERDUE_PING_FILE_AGE, but younger than
|
||||
// MAX_PING_FILE_AGE indicate that we need to send all of our pings ASAP.
|
||||
const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week
|
||||
|
||||
// Maximum number of pings to save.
|
||||
const MAX_LRU_PINGS = 50;
|
||||
|
||||
/**
|
||||
* This is a policy object used to override behavior within this module.
|
||||
* Tests override properties on this object to allow for control of behavior
|
||||
* that would otherwise be very hard to cover.
|
||||
*/
|
||||
let Policy = {
|
||||
now: () => new Date(),
|
||||
midnightPingFuzzingDelay: () => MIDNIGHT_FUZZING_DELAY_MS,
|
||||
setPingSendTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
|
||||
clearPingSendTimeout: (id) => clearTimeout(id),
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if the ping has the new v4 ping format or the legacy v2 one or earlier.
|
||||
*/
|
||||
function isV4PingFormat(aPing) {
|
||||
return ("id" in aPing) && ("application" in aPing) &&
|
||||
("version" in aPing) && (aPing.version >= 2);
|
||||
}
|
||||
|
||||
function tomorrow(date) {
|
||||
let d = new Date(date);
|
||||
d.setDate(d.getDate() + 1);
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {String} This returns a string with the gzip compressed data.
|
||||
*/
|
||||
function gzipCompressString(string) {
|
||||
let observer = {
|
||||
buffer: "",
|
||||
onStreamComplete: function(loader, context, status, length, result) {
|
||||
this.buffer = String.fromCharCode.apply(this, result);
|
||||
}
|
||||
};
|
||||
|
||||
let scs = Cc["@mozilla.org/streamConverters;1"]
|
||||
.getService(Ci.nsIStreamConverterService);
|
||||
let listener = Cc["@mozilla.org/network/stream-loader;1"]
|
||||
.createInstance(Ci.nsIStreamLoader);
|
||||
listener.init(observer);
|
||||
let converter = scs.asyncConvertData("uncompressed", "gzip",
|
||||
listener, null);
|
||||
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
stringStream.data = string;
|
||||
converter.onStartRequest(null, null);
|
||||
converter.onDataAvailable(null, null, stringStream, 0, string.length);
|
||||
converter.onStopRequest(null, null, null);
|
||||
return observer.buffer;
|
||||
}
|
||||
|
||||
this.TelemetrySend = {
|
||||
/**
|
||||
* Maximum age in ms of a pending ping file before it gets evicted.
|
||||
*/
|
||||
get MAX_PING_FILE_AGE() {
|
||||
return MAX_PING_FILE_AGE;
|
||||
},
|
||||
|
||||
/**
|
||||
* Age in ms of a pending ping to be considered overdue.
|
||||
*/
|
||||
get OVERDUE_PING_FILE_AGE() {
|
||||
return OVERDUE_PING_FILE_AGE;
|
||||
},
|
||||
|
||||
/**
|
||||
* The maximum number of pending pings we keep in the backlog.
|
||||
*/
|
||||
get MAX_LRU_PINGS() {
|
||||
return MAX_LRU_PINGS;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes this module.
|
||||
*
|
||||
* @param {Boolean} testing Whether this is run in a test. This changes some behavior
|
||||
* to enable proper testing.
|
||||
* @return {Promise} Resolved when setup is finished.
|
||||
*/
|
||||
setup: function(testing = false) {
|
||||
return TelemetrySendImpl.setup(testing);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shutdown this module - this will cancel any pending ping tasks and wait for
|
||||
* outstanding async activity like network and disk I/O.
|
||||
*
|
||||
* @return {Promise} Promise that is resolved when shutdown is finished.
|
||||
*/
|
||||
shutdown: function() {
|
||||
return TelemetrySendImpl.shutdown();
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit a ping for sending. This will:
|
||||
* - send the ping right away if possible or
|
||||
* - save the ping to disk and send it at the next opportunity
|
||||
*
|
||||
* @param {Object} ping The ping data to send, must be serializable to JSON.
|
||||
* @return {Promise} A promise that is resolved when the ping is sent or saved.
|
||||
*/
|
||||
submitPing: function(ping) {
|
||||
return TelemetrySendImpl.submitPing(ping);
|
||||
},
|
||||
|
||||
/**
|
||||
* Count of pending pings that were discarded at startup due to being too old.
|
||||
*/
|
||||
get discardedPingsCount() {
|
||||
return TelemetrySendImpl.discardedPingsCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Count of pending pings that were found to be overdue at startup.
|
||||
*/
|
||||
get overduePingsCount() {
|
||||
return TelemetrySendImpl.overduePingsCount;
|
||||
},
|
||||
|
||||
/**
|
||||
* Only used in tests. Used to reset the module data to emulate a restart.
|
||||
*/
|
||||
reset: function() {
|
||||
return TelemetrySendImpl.reset();
|
||||
},
|
||||
|
||||
/**
|
||||
* Only used in tests.
|
||||
*/
|
||||
setServer: function(server) {
|
||||
return TelemetrySendImpl.setServer(server);
|
||||
},
|
||||
};
|
||||
|
||||
let TelemetrySendImpl = {
|
||||
_sendingEnabled: false,
|
||||
_logger: null,
|
||||
// Timer for scheduled ping sends.
|
||||
_pingSendTimer: null,
|
||||
// This tracks all pending ping requests to the server.
|
||||
_pendingPingRequests: new Map(),
|
||||
// This is a private barrier blocked by pending async ping activity (sending & saving).
|
||||
_connectionsBarrier: new AsyncShutdown.Barrier("TelemetrySend: Waiting for pending ping activity"),
|
||||
// This is true when running in the test infrastructure.
|
||||
_testMode: false,
|
||||
|
||||
// Count of pending pings we discarded for age on startup.
|
||||
_discardedPingsCount: 0,
|
||||
// Count of pending pings we evicted for being over the limit on startup.
|
||||
_evictedPingsCount: 0,
|
||||
// Count of pending pings that were overdue.
|
||||
_overduePingCount: 0,
|
||||
|
||||
OBSERVER_TOPICS: [
|
||||
TOPIC_IDLE_DAILY,
|
||||
],
|
||||
|
||||
get _log() {
|
||||
if (!this._logger) {
|
||||
this._logger = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
|
||||
}
|
||||
|
||||
return this._logger;
|
||||
},
|
||||
|
||||
get discardedPingsCount() {
|
||||
return this._discardedPingsCount;
|
||||
},
|
||||
|
||||
get overduePingsCount() {
|
||||
return this._overduePingCount;
|
||||
},
|
||||
|
||||
setup: Task.async(function*(testing) {
|
||||
this._log.trace("setup");
|
||||
|
||||
this._testMode = testing;
|
||||
this._sendingEnabled = true;
|
||||
|
||||
this._discardedPingsCount = 0;
|
||||
this._evictedPingsCount = 0;
|
||||
|
||||
Services.obs.addObserver(this, TOPIC_IDLE_DAILY, false);
|
||||
|
||||
this._server = Preferences.get(PREF_SERVER, undefined);
|
||||
|
||||
// If any pings were submitted before the delayed init finished
|
||||
// we will send them now.
|
||||
yield this._sendPersistedPings();
|
||||
|
||||
// Check the pending pings on disk now.
|
||||
yield this._checkPendingPings();
|
||||
}),
|
||||
|
||||
_checkPendingPings: Task.async(function*() {
|
||||
// Scan the pending pings - that gives us a list sorted by last modified, descending.
|
||||
let infos = yield TelemetryStorage.loadPendingPingList();
|
||||
this._log.info("_checkPendingPings - pending ping count: " + infos.length);
|
||||
if (infos.length == 0) {
|
||||
this._log.trace("_checkPendingPings - no pending pings");
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove old pings that we haven't been able to send yet.
|
||||
const now = new Date();
|
||||
const tooOld = (info) => (now.getTime() - info.lastModificationDate) > MAX_PING_FILE_AGE;
|
||||
|
||||
const oldPings = infos.filter((info) => tooOld(info));
|
||||
infos = infos.filter((info) => !tooOld(info));
|
||||
this._log.info("_checkPendingPings - clearing out " + oldPings.length + " old pings");
|
||||
|
||||
for (let info of oldPings) {
|
||||
try {
|
||||
yield TelemetryStorage.removePendingPing(info.id);
|
||||
++this._discardedPingsCount;
|
||||
} catch(ex) {
|
||||
this._log.error("_checkPendingPings - failed to remove old ping", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// Keep only the last MAX_LRU_PINGS entries to avoid that the backlog overgrows.
|
||||
const shouldEvict = infos.splice(MAX_LRU_PINGS, infos.length);
|
||||
let evictedCount = 0;
|
||||
this._log.info("_checkPendingPings - evicting " + shouldEvict.length + " pings to " +
|
||||
"avoid overgrowing the backlog");
|
||||
|
||||
for (let info of shouldEvict) {
|
||||
try {
|
||||
yield TelemetryStorage.removePendingPing(info.id);
|
||||
++this._evictedPingsCount;
|
||||
} catch(ex) {
|
||||
this._log.error("_checkPendingPings - failed to evict ping", ex);
|
||||
}
|
||||
}
|
||||
|
||||
Services.telemetry.getHistogramById('TELEMETRY_FILES_EVICTED')
|
||||
.add(evictedCount);
|
||||
|
||||
// Check for overdue pings.
|
||||
const overduePings = infos.filter((info) =>
|
||||
(now.getTime() - info.lastModificationDate) > OVERDUE_PING_FILE_AGE);
|
||||
this._overduePingCount = overduePings.length;
|
||||
|
||||
|
||||
if (overduePings.length > 0) {
|
||||
this._log.trace("_checkForOverduePings - Have " + overduePings.length +
|
||||
" overdue pending pings, sending " + infos.length +
|
||||
" pings now.");
|
||||
yield this._sendPersistedPings();
|
||||
}
|
||||
}),
|
||||
|
||||
shutdown: Task.async(function*() {
|
||||
for (let topic of this.OBSERVER_TOPICS) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
}
|
||||
|
||||
// We can't send anymore now.
|
||||
this._sendingEnabled = false;
|
||||
|
||||
// Clear scheduled ping sends.
|
||||
this._clearPingSendTimer();
|
||||
// Cancel any outgoing requests.
|
||||
yield this._cancelOutgoingRequests();
|
||||
// ... and wait for any outstanding async ping activity.
|
||||
yield this._connectionsBarrier.wait();
|
||||
}),
|
||||
|
||||
reset: function() {
|
||||
this._log.trace("reset");
|
||||
|
||||
this._overduePingCount = 0;
|
||||
this._discardedPingsCount = 0;
|
||||
this._evictedPingsCount = 0;
|
||||
|
||||
const histograms = [
|
||||
"TELEMETRY_SUCCESS",
|
||||
"TELEMETRY_FILES_EVICTED",
|
||||
"TELEMETRY_SEND",
|
||||
"TELEMETRY_PING",
|
||||
];
|
||||
|
||||
histograms.forEach(h => Telemetry.getHistogramById(h).clear());
|
||||
},
|
||||
|
||||
observe: function(subject, topic, data) {
|
||||
switch(topic) {
|
||||
case TOPIC_IDLE_DAILY:
|
||||
this._sendPersistedPings();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
submitPing: function(ping) {
|
||||
if (!this._canSend()) {
|
||||
this._log.trace("submitPing - Telemetry is not allowed to send pings.");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Check if we can send pings now.
|
||||
const now = Policy.now();
|
||||
const nextPingSendTime = this._getNextPingSendTime(now);
|
||||
const throttled = (nextPingSendTime > now.getTime());
|
||||
|
||||
// We can't send pings now, schedule a later send.
|
||||
if (throttled) {
|
||||
this._log.trace("submitPing - throttled, delaying ping send to " + new Date(nextPingSendTime));
|
||||
this._reschedulePingSendTimer(nextPingSendTime);
|
||||
}
|
||||
|
||||
if (!this._sendingEnabled || throttled) {
|
||||
// Sending is disabled or throttled, add this to the pending pings.
|
||||
this._log.trace("submitPing - ping is pending, sendingEnabled: " + this._sendingEnabled +
|
||||
", throttled: " + throttled);
|
||||
return TelemetryStorage.savePendingPing(ping);
|
||||
}
|
||||
|
||||
// Try to send the ping, persist it if sending it fails.
|
||||
this._log.trace("submitPing - already initialized, ping will be sent");
|
||||
let ps = [];
|
||||
ps.push(this._doPing(ping, ping.id, false)
|
||||
.catch((ex) => {
|
||||
this._log.info("submitPing - ping not sent, saving to disk", ex);
|
||||
TelemetryStorage.savePendingPing(ping);
|
||||
}));
|
||||
ps.push(this._sendPersistedPings());
|
||||
|
||||
return Promise.all([for (p of ps) p.catch((ex) => {
|
||||
this._log.error("submitPing - ping activity had an error", ex);
|
||||
})]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Only used in tests.
|
||||
*/
|
||||
setServer: function (server) {
|
||||
this._log.trace("setServer", server);
|
||||
this._server = server;
|
||||
},
|
||||
|
||||
_cancelOutgoingRequests: function() {
|
||||
// Abort any pending ping XHRs.
|
||||
for (let [url, request] of this._pendingPingRequests) {
|
||||
this._log.trace("_cancelOutgoingRequests - aborting ping request for " + url);
|
||||
try {
|
||||
request.abort();
|
||||
} catch (e) {
|
||||
this._log.error("_cancelOutgoingRequests - failed to abort request to " + url, e);
|
||||
}
|
||||
}
|
||||
this._pendingPingRequests.clear();
|
||||
},
|
||||
|
||||
/**
|
||||
* This helper calculates the next time that we can send pings at.
|
||||
* Currently this mostly redistributes ping sends around midnight to avoid submission
|
||||
* spikes around local midnight for daily pings.
|
||||
*
|
||||
* @param now Date The current time.
|
||||
* @return Number The next time (ms from UNIX epoch) when we can send pings.
|
||||
*/
|
||||
_getNextPingSendTime: function(now) {
|
||||
// 1. First we check if the time is between 11pm and 1am. If it's not, we send
|
||||
// immediately.
|
||||
// 2. If we confirmed the time is indeed between 11pm and 1am in step 1, we
|
||||
// then check if the time is also 11:55pm or later. If it's not, we send
|
||||
// immediately.
|
||||
// 3. Finally, if the time is between 11:55pm and 1am, we disallow sending
|
||||
// before (midnight + fuzzing delay), which is a random time between 12am-1am
|
||||
// (decided at startup).
|
||||
|
||||
const midnightDate = Utils.getNearestMidnight(now, MIDNIGHT_FUZZING_INTERVAL_MS);
|
||||
|
||||
// Don't delay ping if we are not close to midnight.
|
||||
if (!midnightDate) {
|
||||
return now.getTime();
|
||||
}
|
||||
|
||||
// Delay ping send if we are within the midnight fuzzing range.
|
||||
// This is from: |midnight - MIDNIGHT_TOLERANCE_FUZZ_MS|
|
||||
// to: |midnight + MIDNIGHT_FUZZING_INTERVAL_MS|
|
||||
const midnightRangeStart = midnightDate.getTime() - MIDNIGHT_TOLERANCE_FUZZ_MS;
|
||||
if (now.getTime() >= midnightRangeStart) {
|
||||
// We spread those ping sends out between |midnight| and |midnight + midnightPingFuzzingDelay|.
|
||||
return midnightDate.getTime() + Policy.midnightPingFuzzingDelay();
|
||||
}
|
||||
|
||||
return now.getTime();
|
||||
},
|
||||
|
||||
/**
|
||||
* Send the persisted pings to the server.
|
||||
*
|
||||
* @return Promise A promise that is resolved when all pings finished sending or failed.
|
||||
*/
|
||||
_sendPersistedPings: Task.async(function*() {
|
||||
this._log.trace("_sendPersistedPings - Can send: " + this._canSend());
|
||||
|
||||
if (TelemetryStorage.pendingPingCount < 1) {
|
||||
this._log.trace("_sendPersistedPings - no pings to send");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
if (!this._canSend()) {
|
||||
this._log.trace("_sendPersistedPings - Telemetry is not allowed to send pings.");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Check if we can send pings now - otherwise schedule a later send.
|
||||
const now = Policy.now();
|
||||
const nextPingSendTime = this._getNextPingSendTime(now);
|
||||
if (nextPingSendTime > now.getTime()) {
|
||||
this._log.trace("_sendPersistedPings - delaying ping send to " + new Date(nextPingSendTime));
|
||||
this._reschedulePingSendTimer(nextPingSendTime);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// We can send now.
|
||||
const pendingPings = TelemetryStorage.getPendingPingList();
|
||||
this._log.trace("_sendPersistedPings - sending " + pendingPings.length + " pings");
|
||||
let pingSendPromises = [];
|
||||
for (let ping of pendingPings) {
|
||||
let p = ping;
|
||||
pingSendPromises.push(
|
||||
TelemetryStorage.loadPendingPing(p.id)
|
||||
.then((data) => this._doPing(data, p.id, true)
|
||||
.catch(e => this._log.error("_sendPersistedPings - _doPing rejected", e))));
|
||||
}
|
||||
|
||||
let promise = Promise.all(pingSendPromises);
|
||||
this._trackPendingPingTask(promise);
|
||||
yield promise;
|
||||
}),
|
||||
|
||||
_onPingRequestFinished: function(success, startTime, id, isPersisted) {
|
||||
this._log.trace("_onPingRequestFinished - success: " + success + ", persisted: " + isPersisted);
|
||||
|
||||
Telemetry.getHistogramById("TELEMETRY_SEND").add(new Date() - startTime);
|
||||
let hping = Telemetry.getHistogramById("TELEMETRY_PING");
|
||||
let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS");
|
||||
|
||||
hsuccess.add(success);
|
||||
hping.add(new Date() - startTime);
|
||||
|
||||
if (success && isPersisted) {
|
||||
return TelemetryStorage.removePendingPing(id);
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
_getSubmissionPath: function(ping) {
|
||||
// The new ping format contains an "application" section, the old one doesn't.
|
||||
let pathComponents;
|
||||
if (isV4PingFormat(ping)) {
|
||||
// We insert the Ping id in the URL to simplify server handling of duplicated
|
||||
// pings.
|
||||
let app = ping.application;
|
||||
pathComponents = [
|
||||
ping.id, ping.type, app.name, app.version, app.channel, app.buildId
|
||||
];
|
||||
} else {
|
||||
// This is a ping in the old format.
|
||||
if (!("slug" in ping)) {
|
||||
// That's odd, we don't have a slug. Generate one so that TelemetryStorage.jsm works.
|
||||
ping.slug = Utils.generateUUID();
|
||||
}
|
||||
|
||||
// Do we have enough info to build a submission URL?
|
||||
let payload = ("payload" in ping) ? ping.payload : null;
|
||||
if (payload && ("info" in payload)) {
|
||||
let info = ping.payload.info;
|
||||
pathComponents = [ ping.slug, info.reason, info.appName, info.appVersion,
|
||||
info.appUpdateChannel, info.appBuildID ];
|
||||
} else {
|
||||
// Only use the UUID as the slug.
|
||||
pathComponents = [ ping.slug ];
|
||||
}
|
||||
}
|
||||
|
||||
let slug = pathComponents.join("/");
|
||||
return "/submit/telemetry/" + slug;
|
||||
},
|
||||
|
||||
_doPing: function(ping, id, isPersisted) {
|
||||
if (!this._canSend()) {
|
||||
// We can't send the pings to the server, so don't try to.
|
||||
this._log.trace("_doPing - Sending is disabled.");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
this._log.trace("_doPing - server: " + this._server + ", persisted: " + isPersisted +
|
||||
", id: " + id);
|
||||
const isNewPing = isV4PingFormat(ping);
|
||||
const version = isNewPing ? PING_FORMAT_VERSION : 1;
|
||||
const url = this._server + this._getSubmissionPath(ping) + "?v=" + version;
|
||||
|
||||
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Ci.nsIXMLHttpRequest);
|
||||
request.mozBackgroundRequest = true;
|
||||
request.timeout = PING_SUBMIT_TIMEOUT_MS;
|
||||
|
||||
request.open("POST", url, true);
|
||||
request.overrideMimeType("text/plain");
|
||||
request.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
|
||||
|
||||
this._pendingPingRequests.set(url, request);
|
||||
|
||||
let startTime = new Date();
|
||||
let deferred = PromiseUtils.defer();
|
||||
|
||||
let onRequestFinished = (success, event) => {
|
||||
let onCompletion = () => {
|
||||
if (success) {
|
||||
deferred.resolve();
|
||||
} else {
|
||||
deferred.reject(event);
|
||||
}
|
||||
};
|
||||
|
||||
this._pendingPingRequests.delete(url);
|
||||
this._onPingRequestFinished(success, startTime, id, isPersisted)
|
||||
.then(() => onCompletion(),
|
||||
(error) => {
|
||||
this._log.error("_doPing - request success: " + success + ", error" + error);
|
||||
onCompletion();
|
||||
});
|
||||
};
|
||||
|
||||
let errorhandler = (event) => {
|
||||
this._log.error("_doPing - error making request to " + url + ": " + event.type);
|
||||
onRequestFinished(false, event);
|
||||
};
|
||||
request.onerror = errorhandler;
|
||||
request.ontimeout = errorhandler;
|
||||
request.onabort = errorhandler;
|
||||
|
||||
request.onload = (event) => {
|
||||
let status = request.status;
|
||||
let statusClass = status - (status % 100);
|
||||
let success = false;
|
||||
|
||||
if (statusClass === 200) {
|
||||
// We can treat all 2XX as success.
|
||||
this._log.info("_doPing - successfully loaded, status: " + status);
|
||||
success = true;
|
||||
} else if (statusClass === 400) {
|
||||
// 4XX means that something with the request was broken.
|
||||
this._log.error("_doPing - error submitting to " + url + ", status: " + status
|
||||
+ " - ping request broken?");
|
||||
// TODO: we should handle this better, but for now we should avoid resubmitting
|
||||
// broken requests by pretending success.
|
||||
success = true;
|
||||
} else if (statusClass === 500) {
|
||||
// 5XX means there was a server-side error and we should try again later.
|
||||
this._log.error("_doPing - error submitting to " + url + ", status: " + status
|
||||
+ " - server error, should retry later");
|
||||
} else {
|
||||
// We received an unexpected status codes.
|
||||
this._log.error("_doPing - error submitting to " + url + ", status: " + status
|
||||
+ ", type: " + event.type);
|
||||
}
|
||||
|
||||
onRequestFinished(success, event);
|
||||
};
|
||||
|
||||
// If that's a legacy ping format, just send its payload.
|
||||
let networkPayload = isNewPing ? ping : ping.payload;
|
||||
request.setRequestHeader("Content-Encoding", "gzip");
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
startTime = new Date();
|
||||
let utf8Payload = converter.ConvertFromUnicode(JSON.stringify(networkPayload));
|
||||
utf8Payload += converter.Finish();
|
||||
Telemetry.getHistogramById("TELEMETRY_STRINGIFY").add(new Date() - startTime);
|
||||
let payloadStream = Cc["@mozilla.org/io/string-input-stream;1"]
|
||||
.createInstance(Ci.nsIStringInputStream);
|
||||
startTime = new Date();
|
||||
payloadStream.data = gzipCompressString(utf8Payload);
|
||||
Telemetry.getHistogramById("TELEMETRY_COMPRESS").add(new Date() - startTime);
|
||||
startTime = new Date();
|
||||
request.send(payloadStream);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if pings can be sent to the server. If FHR is not allowed to upload,
|
||||
* pings are not sent to the server (Telemetry is a sub-feature of FHR).
|
||||
* If unified telemetry is off, don't send pings if Telemetry is disabled.
|
||||
*
|
||||
* @return {Boolean} True if pings can be send to the servers, false otherwise.
|
||||
*/
|
||||
_canSend: function() {
|
||||
// We only send pings from official builds, but allow overriding this for tests.
|
||||
if (!Telemetry.isOfficialTelemetry && !this._testMode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// With unified Telemetry, the FHR upload setting controls whether we can send pings.
|
||||
// The Telemetry pref enables sending extended data sets instead.
|
||||
if (IS_UNIFIED_TELEMETRY) {
|
||||
return Preferences.get(PREF_FHR_UPLOAD_ENABLED, false);
|
||||
}
|
||||
|
||||
// Without unified Telemetry, the Telemetry enabled pref controls ping sending.
|
||||
return Preferences.get(PREF_TELEMETRY_ENABLED, false);
|
||||
},
|
||||
|
||||
_reschedulePingSendTimer: function(timestamp) {
|
||||
this._clearPingSendTimer();
|
||||
const interval = timestamp - Policy.now();
|
||||
this._pingSendTimer = Policy.setPingSendTimeout(() => this._sendPersistedPings(), interval);
|
||||
},
|
||||
|
||||
_clearPingSendTimer: function() {
|
||||
if (this._pingSendTimer) {
|
||||
Policy.clearPingSendTimeout(this._pingSendTimer);
|
||||
this._pingSendTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Track any pending ping send and save tasks through the promise passed here.
|
||||
* This is needed to block shutdown on any outstanding ping activity.
|
||||
*/
|
||||
_trackPendingPingTask: function (promise) {
|
||||
this._connectionsBarrier.client.addBlocker("Waiting for ping task", promise);
|
||||
},
|
||||
};
|
@ -20,6 +20,7 @@ Cu.import("resource://gre/modules/DeferredTask.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
|
||||
|
||||
const Utils = TelemetryUtils;
|
||||
@ -705,13 +706,11 @@ this.TelemetrySession = Object.freeze({
|
||||
return Impl.requestChildPayloads();
|
||||
},
|
||||
/**
|
||||
* Save histograms to a file.
|
||||
* Save the session state to a pending file.
|
||||
* Used only for testing purposes.
|
||||
*
|
||||
* @param {nsIFile} aFile The file to load from.
|
||||
*/
|
||||
testSaveHistograms: function(aFile) {
|
||||
return Impl.testSaveHistograms(aFile);
|
||||
testSavePendingPing: function() {
|
||||
return Impl.testSavePendingPing();
|
||||
},
|
||||
/**
|
||||
* Collect and store information about startup.
|
||||
@ -923,9 +922,8 @@ let Impl = {
|
||||
hasPingBeenSent = Telemetry.getHistogramById("TELEMETRY_SUCCESS").snapshot().sum > 0;
|
||||
} catch(e) {
|
||||
}
|
||||
if (!forSavedSession || hasPingBeenSent) {
|
||||
ret.savedPings = TelemetryStorage.pingsLoaded;
|
||||
}
|
||||
|
||||
ret.savedPings = TelemetryStorage.pendingPingCount;
|
||||
|
||||
ret.activeTicks = -1;
|
||||
let sr = TelemetryController.getSessionRecorder();
|
||||
@ -942,8 +940,8 @@ let Impl = {
|
||||
ret.activeTicks = activeTicks;
|
||||
}
|
||||
|
||||
ret.pingsOverdue = TelemetryStorage.pingsOverdue;
|
||||
ret.pingsDiscarded = TelemetryStorage.pingsDiscarded;
|
||||
ret.pingsOverdue = TelemetrySend.overduePingsCount;
|
||||
ret.pingsDiscarded = TelemetrySend.discardedPingsCount;
|
||||
|
||||
return ret;
|
||||
},
|
||||
@ -1669,15 +1667,15 @@ let Impl = {
|
||||
}),
|
||||
|
||||
|
||||
testSaveHistograms: function testSaveHistograms(file) {
|
||||
this._log.trace("testSaveHistograms - Path: " + file.path);
|
||||
testSavePendingPing: function testSaveHistograms() {
|
||||
this._log.trace("testSaveHistograms");
|
||||
let payload = this.getSessionPayload(REASON_SAVED_SESSION, false);
|
||||
let options = {
|
||||
addClientId: true,
|
||||
addEnvironment: true,
|
||||
overwrite: true,
|
||||
};
|
||||
return TelemetryController.savePing(getPingType(payload), payload, file.path, options);
|
||||
return TelemetryController.addPendingPing(getPingType(payload), payload, options);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1798,9 +1796,6 @@ let Impl = {
|
||||
// bug 1127907 lands.
|
||||
Services.obs.notifyObservers(null, "gather-telemetry", null);
|
||||
}).bind(this), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
// TODO: This is just a fallback for now. Remove this when we have ping send
|
||||
// scheduling properly factored out and driven independently of this module.
|
||||
TelemetryController.sendPersistedPings();
|
||||
break;
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
|
@ -33,6 +33,7 @@ const Utils = TelemetryUtils;
|
||||
const DATAREPORTING_DIR = "datareporting";
|
||||
const PINGS_ARCHIVE_DIR = "archived";
|
||||
const ABORTED_SESSION_FILE_NAME = "aborted-session-ping";
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gDataReportingDir", function() {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, DATAREPORTING_DIR);
|
||||
});
|
||||
@ -43,17 +44,6 @@ XPCOMUtils.defineLazyGetter(this, "gAbortedSessionFilePath", function() {
|
||||
return OS.Path.join(gDataReportingDir, ABORTED_SESSION_FILE_NAME);
|
||||
});
|
||||
|
||||
// Files that have been lying around for longer than MAX_PING_FILE_AGE are
|
||||
// deleted without being loaded.
|
||||
const MAX_PING_FILE_AGE = 14 * 24 * 60 * 60 * 1000; // 2 weeks
|
||||
|
||||
// Files that are older than OVERDUE_PING_FILE_AGE, but younger than
|
||||
// MAX_PING_FILE_AGE indicate that we need to send all of our pings ASAP.
|
||||
const OVERDUE_PING_FILE_AGE = 7 * 24 * 60 * 60 * 1000; // 1 week
|
||||
|
||||
// Maximum number of pings to save.
|
||||
const MAX_LRU_PINGS = 50;
|
||||
|
||||
// Maxmimum time, in milliseconds, archive pings should be retained.
|
||||
const MAX_ARCHIVED_PINGS_RETENTION_MS = 180 * 24 * 60 * 60 * 1000; // 180 days
|
||||
|
||||
@ -63,20 +53,7 @@ const ARCHIVE_QUOTA_BYTES = 120 * 1024 * 1024; // 120 MB
|
||||
// This special value is submitted when the archive is outside of the quota.
|
||||
const ARCHIVE_SIZE_PROBE_SPECIAL_VALUE = 300;
|
||||
|
||||
// The number of outstanding saved pings that we have issued loading
|
||||
// requests for.
|
||||
let pingsLoaded = 0;
|
||||
|
||||
// The number of pings that we have destroyed due to being older
|
||||
// than MAX_PING_FILE_AGE.
|
||||
let pingsDiscarded = 0;
|
||||
|
||||
// The number of pings that are older than OVERDUE_PING_FILE_AGE
|
||||
// but younger than MAX_PING_FILE_AGE.
|
||||
let pingsOverdue = 0;
|
||||
|
||||
// Data that has neither been saved nor sent by ping
|
||||
let pendingPings = [];
|
||||
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
|
||||
let isPingDirectoryCreated = false;
|
||||
|
||||
@ -112,18 +89,6 @@ function waitForAll(it) {
|
||||
}
|
||||
|
||||
this.TelemetryStorage = {
|
||||
get MAX_PING_FILE_AGE() {
|
||||
return MAX_PING_FILE_AGE;
|
||||
},
|
||||
|
||||
get OVERDUE_PING_FILE_AGE() {
|
||||
return OVERDUE_PING_FILE_AGE;
|
||||
},
|
||||
|
||||
get MAX_LRU_PINGS() {
|
||||
return MAX_LRU_PINGS;
|
||||
},
|
||||
|
||||
get pingDirectoryPath() {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, "saved-telemetry-pings");
|
||||
},
|
||||
@ -157,6 +122,17 @@ this.TelemetryStorage = {
|
||||
return TelemetryStorageImpl.loadArchivedPing(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of info on the archived pings.
|
||||
* This will scan the archive directory and grab basic data about the existing
|
||||
* pings out of their filename.
|
||||
*
|
||||
* @return {promise<sequence<object>>}
|
||||
*/
|
||||
loadArchivedPingList: function() {
|
||||
return TelemetryStorageImpl.loadArchivedPingList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean the pings archive by removing old pings.
|
||||
* This will scan the archive directory.
|
||||
@ -182,16 +158,63 @@ this.TelemetryStorage = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of info on the archived pings.
|
||||
* This will scan the archive directory and grab basic data about the existing
|
||||
* pings out of their filename.
|
||||
* Save a pending - outgoing - ping to disk and track it.
|
||||
*
|
||||
* @return {promise<sequence<object>>}
|
||||
* @param {Object} ping The ping data.
|
||||
* @return {Promise} Resolved when the ping was saved.
|
||||
*/
|
||||
loadArchivedPingList: function() {
|
||||
return TelemetryStorageImpl.loadArchivedPingList();
|
||||
savePendingPing: function(ping) {
|
||||
return TelemetryStorageImpl.savePendingPing(ping);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a pending ping from disk by id.
|
||||
*
|
||||
* @param {String} id The pings id.
|
||||
* @return {Promise} Resolved with the loaded ping data.
|
||||
*/
|
||||
loadPendingPing: function(id) {
|
||||
return TelemetryStorageImpl.loadPendingPing(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a pending ping from disk by id.
|
||||
*
|
||||
* @param {String} id The pings id.
|
||||
* @return {Promise} Resolved when the ping was removed.
|
||||
*/
|
||||
removePendingPing: function(id) {
|
||||
return TelemetryStorageImpl.removePendingPing(id);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a list of the currently pending pings in the format:
|
||||
* {
|
||||
* id: <string>, // The pings UUID.
|
||||
* lastModificationDate: <number>, // Timestamp of the pings last modification.
|
||||
* }
|
||||
* This populates the list by scanning the disk.
|
||||
*
|
||||
* @return {Promise<sequence>} Resolved with the ping list.
|
||||
*/
|
||||
loadPendingPingList: function() {
|
||||
return TelemetryStorageImpl.loadPendingPingList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a list of the currently pending pings in the format:
|
||||
* {
|
||||
* id: <string>, // The pings UUID.
|
||||
* lastModificationDate: <number>, // Timestamp of the pings last modification.
|
||||
* }
|
||||
* This does not scan pending pings on disk.
|
||||
*
|
||||
* @return {sequence} The current pending ping list.
|
||||
*/
|
||||
getPendingPingList: function() {
|
||||
return TelemetryStorageImpl.getPendingPingList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Save an aborted-session ping to disk. This goes to a special location so
|
||||
* it is not picked up as a pending ping.
|
||||
@ -282,24 +305,9 @@ this.TelemetryStorage = {
|
||||
return TelemetryStorageImpl.cleanupPingFile(ping);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load all saved pings.
|
||||
*
|
||||
* Once loaded, the saved pings can be accessed (destructively only)
|
||||
* through |popPendingPings|.
|
||||
*
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadSavedPings: function() {
|
||||
return TelemetryStorageImpl.loadSavedPings();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the histograms from a file.
|
||||
*
|
||||
* Once loaded, the saved pings can be accessed (destructively only)
|
||||
* through |popPendingPings|.
|
||||
*
|
||||
* @param {string} file The file to load.
|
||||
* @returns {promise}
|
||||
*/
|
||||
@ -308,38 +316,10 @@ this.TelemetryStorage = {
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pings loaded since the beginning of time.
|
||||
* The number of pending pings on disk.
|
||||
*/
|
||||
get pingsLoaded() {
|
||||
return TelemetryStorageImpl.pingsLoaded;
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pings loaded that are older than OVERDUE_PING_FILE_AGE
|
||||
* but younger than MAX_PING_FILE_AGE.
|
||||
*/
|
||||
get pingsOverdue() {
|
||||
return TelemetryStorageImpl.pingsOverdue;
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pings that we just tossed out for being older than
|
||||
* MAX_PING_FILE_AGE.
|
||||
*/
|
||||
get pingsDiscarded() {
|
||||
return TelemetryStorageImpl.pingsDiscarded;
|
||||
},
|
||||
|
||||
/**
|
||||
* Iterate destructively through the pending pings.
|
||||
*
|
||||
* @return {iterator}
|
||||
*/
|
||||
popPendingPings: function*() {
|
||||
while (pendingPings.length > 0) {
|
||||
let data = pendingPings.pop();
|
||||
yield data;
|
||||
}
|
||||
get pendingPingCount() {
|
||||
return TelemetryStorageImpl.pendingPingCount;
|
||||
},
|
||||
|
||||
testLoadHistograms: function(file) {
|
||||
@ -489,6 +469,10 @@ let TelemetryStorageImpl = {
|
||||
// Whether we already scanned the archived pings on disk.
|
||||
_scannedArchiveDirectory: false,
|
||||
|
||||
// Tracks the pending pings in a Map of (id -> {timestampCreated, type}).
|
||||
// We use this to cache info on pending pings to avoid scanning the disk more than once.
|
||||
_pendingPings: new Map(),
|
||||
|
||||
// Track the shutdown process to bail out of the clean up task quickly.
|
||||
_shutdown: false,
|
||||
|
||||
@ -508,7 +492,6 @@ let TelemetryStorageImpl = {
|
||||
shutdown: Task.async(function*() {
|
||||
this._shutdown = true;
|
||||
yield this._abortedSessionSerializer.flushTasks();
|
||||
yield this.savePendingPings();
|
||||
// If the archive cleaning task is running, block on it. It should bail out as soon
|
||||
// as possible.
|
||||
yield this._cleanArchiveTask;
|
||||
@ -819,6 +802,8 @@ let TelemetryStorageImpl = {
|
||||
this._shutdown = false;
|
||||
this._scannedArchiveDirectory = false;
|
||||
this._archivedPings = new Map();
|
||||
this._scannedPendingDirectory = false;
|
||||
this._pendingPings = new Map();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -946,25 +931,12 @@ let TelemetryStorageImpl = {
|
||||
* if it exists.
|
||||
* @returns {promise}
|
||||
*/
|
||||
savePing: function(ping, overwrite) {
|
||||
return Task.spawn(function*() {
|
||||
yield getPingDirectory();
|
||||
let file = pingFilePath(ping);
|
||||
yield this.savePingToFile(ping, file, overwrite);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Save all pending pings.
|
||||
*
|
||||
* @returns {promise}
|
||||
*/
|
||||
savePendingPings: function() {
|
||||
let p = [for (ping of pendingPings) this.savePing(ping, false).catch(ex => {
|
||||
this._log.error("savePendingPings - failed to save pending pings.");
|
||||
})];
|
||||
return Promise.all(p);
|
||||
},
|
||||
savePing: Task.async(function*(ping, overwrite) {
|
||||
yield getPingDirectory();
|
||||
let file = pingFilePath(ping);
|
||||
yield this.savePingToFile(ping, file, overwrite);
|
||||
return file;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Add a ping from an existing file to the saved pings directory so that it gets saved
|
||||
@ -995,10 +967,7 @@ let TelemetryStorageImpl = {
|
||||
* @return {Promise} A promise resolved when the ping is saved to the pings directory.
|
||||
*/
|
||||
addPendingPing: function(ping) {
|
||||
// Append the ping to the pending list.
|
||||
pendingPings.push(ping);
|
||||
// Save the ping to the saved pings directory.
|
||||
return this.savePing(ping, false);
|
||||
return this.savePendingPing(ping);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1011,59 +980,111 @@ let TelemetryStorageImpl = {
|
||||
return OS.File.remove(pingFilePath(ping));
|
||||
},
|
||||
|
||||
/**
|
||||
* Load all saved pings.
|
||||
*
|
||||
* Once loaded, the saved pings can be accessed (destructively only)
|
||||
* through |popPendingPings|.
|
||||
*
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadSavedPings: function() {
|
||||
return Task.spawn(function*() {
|
||||
let directory = TelemetryStorage.pingDirectoryPath;
|
||||
let iter = new OS.File.DirectoryIterator(directory);
|
||||
let exists = yield iter.exists();
|
||||
savePendingPing: function(ping) {
|
||||
return this.savePing(ping, true).then((path) => {
|
||||
this._pendingPings.set(ping.id, {
|
||||
path: path,
|
||||
lastModificationDate: Policy.now().getTime(),
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
if (exists) {
|
||||
let entries = yield iter.nextBatch();
|
||||
let sortedEntries = [];
|
||||
loadPendingPing: function(id) {
|
||||
this._log.trace("loadPendingPing - id: " + id);
|
||||
let info = this._pendingPings.get(id);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let entry of entries) {
|
||||
if (entry.isDir) {
|
||||
continue;
|
||||
}
|
||||
return this.loadPingFile(info.path, false);
|
||||
},
|
||||
|
||||
let info = yield OS.File.stat(entry.path);
|
||||
sortedEntries.push({entry:entry, lastModificationDate: info.lastModificationDate});
|
||||
}
|
||||
removePendingPing: function(id) {
|
||||
let info = this._pendingPings.get(id);
|
||||
if (!info) {
|
||||
this._log.trace("removePendingPing - unknown id " + id);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
sortedEntries.sort(function compare(a, b) {
|
||||
return b.lastModificationDate - a.lastModificationDate;
|
||||
});
|
||||
this._log.trace("removePendingPing - deleting ping with id: " + id +
|
||||
", path: " + info.path);
|
||||
this._pendingPings.delete(id);
|
||||
return OS.File.remove(info.path).catch((ex) =>
|
||||
this._log.error("removePendingPing - failed to remove ping", ex));
|
||||
},
|
||||
|
||||
let count = 0;
|
||||
let result = [];
|
||||
loadPendingPingList: function() {
|
||||
// If we already have a pending scanning task active, return that.
|
||||
if (this._scanPendingPingsTask) {
|
||||
return this._scanPendingPingsTask;
|
||||
}
|
||||
|
||||
// Keep only the last MAX_LRU_PINGS entries to avoid that the backlog overgrows.
|
||||
for (let i = 0; i < MAX_LRU_PINGS && i < sortedEntries.length; i++) {
|
||||
let entry = sortedEntries[i].entry;
|
||||
result.push(this.loadHistograms(entry.path))
|
||||
}
|
||||
if (this._scannedPendingDirectory) {
|
||||
this._log.trace("loadPendingPingList - Pending already scanned, hitting cache.");
|
||||
return Promise.resolve(this._buildPingList());
|
||||
}
|
||||
|
||||
for (let i = MAX_LRU_PINGS; i < sortedEntries.length; i++) {
|
||||
let entry = sortedEntries[i].entry;
|
||||
OS.File.remove(entry.path);
|
||||
}
|
||||
// Make sure to clear the task once done.
|
||||
let clear = pings => {
|
||||
this._scanPendingPingsTask = null;
|
||||
return pings;
|
||||
};
|
||||
|
||||
yield Promise.all(result);
|
||||
// Since there's no pending pings scan task running, start it.
|
||||
this._scanPendingPingsTask = this._scanPendingPings().then(clear, clear);
|
||||
return this._scanPendingPingsTask;
|
||||
},
|
||||
|
||||
Services.telemetry.getHistogramById('TELEMETRY_FILES_EVICTED').
|
||||
add(sortedEntries.length - MAX_LRU_PINGS);
|
||||
getPendingPingList: function() {
|
||||
return this._buildPingList();
|
||||
},
|
||||
|
||||
_scanPendingPings: Task.async(function*() {
|
||||
this._log.trace("_scanPendingPings");
|
||||
|
||||
let directory = TelemetryStorage.pingDirectoryPath;
|
||||
let iter = new OS.File.DirectoryIterator(directory);
|
||||
let exists = yield iter.exists();
|
||||
|
||||
if (!exists) {
|
||||
yield iter.close();
|
||||
return [];
|
||||
}
|
||||
|
||||
let files = (yield iter.nextBatch()).filter(e => !e.isDir);
|
||||
|
||||
for (let file of files) {
|
||||
if (this._shutdown) {
|
||||
yield iter.close();
|
||||
return [];
|
||||
}
|
||||
|
||||
yield iter.close();
|
||||
}.bind(this));
|
||||
let info = yield OS.File.stat(file.path);
|
||||
let id = OS.Path.basename(file.path);
|
||||
if (!UUID_REGEX.test(id)) {
|
||||
this._log.trace("_scanPendingPings - unknown filename is not a UUID: " + id);
|
||||
id = Utils.generateUUID();
|
||||
}
|
||||
|
||||
this._pendingPings.set(id, {
|
||||
path: file.path,
|
||||
lastModificationDate: info.lastModificationDate,
|
||||
});
|
||||
}
|
||||
|
||||
yield iter.close();
|
||||
this._scannedPendingDirectory = true;
|
||||
return this._buildPingList();
|
||||
}),
|
||||
|
||||
_buildPingList: function() {
|
||||
const list = [for (p of this._pendingPings) {
|
||||
id: p[0],
|
||||
lastModificationDate: p[1].lastModificationDate,
|
||||
}];
|
||||
|
||||
list.sort((a, b) => b.lastModificationDate - a.lastModificationDate);
|
||||
return list;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1075,50 +1096,25 @@ let TelemetryStorageImpl = {
|
||||
* @param {string} file The file to load.
|
||||
* @returns {promise}
|
||||
*/
|
||||
loadHistograms: function loadHistograms(file) {
|
||||
return OS.File.stat(file).then(function(info){
|
||||
let now = Date.now();
|
||||
if (now - info.lastModificationDate > MAX_PING_FILE_AGE) {
|
||||
// We haven't had much luck in sending this file; delete it.
|
||||
pingsDiscarded++;
|
||||
return OS.File.remove(file);
|
||||
}
|
||||
loadHistograms: Task.async(function*(file) {
|
||||
let success = true;
|
||||
try {
|
||||
const ping = yield this.loadPingfile(file);
|
||||
return ping;
|
||||
} catch (ex) {
|
||||
success = false;
|
||||
yield OS.File.remove(file);
|
||||
} finally {
|
||||
const success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS");
|
||||
success_histogram.add(success);
|
||||
}
|
||||
}),
|
||||
|
||||
// This file is a bit stale, and overdue for sending.
|
||||
if (now - info.lastModificationDate > OVERDUE_PING_FILE_AGE) {
|
||||
pingsOverdue++;
|
||||
}
|
||||
|
||||
pingsLoaded++;
|
||||
return addToPendingPings(file);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pings loaded since the beginning of time.
|
||||
*/
|
||||
get pingsLoaded() {
|
||||
return pingsLoaded;
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pings loaded that are older than OVERDUE_PING_FILE_AGE
|
||||
* but younger than MAX_PING_FILE_AGE.
|
||||
*/
|
||||
get pingsOverdue() {
|
||||
return pingsOverdue;
|
||||
},
|
||||
|
||||
/**
|
||||
* The number of pings that we just tossed out for being older than
|
||||
* MAX_PING_FILE_AGE.
|
||||
*/
|
||||
get pingsDiscarded() {
|
||||
return pingsDiscarded;
|
||||
get pendingPingCount() {
|
||||
return this._pendingPings.size;
|
||||
},
|
||||
|
||||
testLoadHistograms: function(file) {
|
||||
pingsLoaded = 0;
|
||||
return this.loadHistograms(file.path);
|
||||
},
|
||||
|
||||
@ -1180,8 +1176,7 @@ let TelemetryStorageImpl = {
|
||||
}
|
||||
|
||||
// Check for a valid UUID.
|
||||
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||
if (!uuidRegex.test(uuid)) {
|
||||
if (!UUID_REGEX.test(uuid)) {
|
||||
this._log.trace("_getArchivedPingDataFromFileName - should have a valid id");
|
||||
return null;
|
||||
}
|
||||
@ -1255,22 +1250,6 @@ function getPingDirectory() {
|
||||
});
|
||||
}
|
||||
|
||||
function addToPendingPings(file) {
|
||||
function onLoad(success) {
|
||||
let success_histogram = Telemetry.getHistogramById("READ_SAVED_PING_SUCCESS");
|
||||
success_histogram.add(success);
|
||||
}
|
||||
|
||||
return TelemetryStorage.loadPingFile(file).then(ping => {
|
||||
pendingPings.push(ping);
|
||||
onLoad(true);
|
||||
},
|
||||
() => {
|
||||
onLoad(false);
|
||||
return OS.File.remove(file);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the path to the archived ping.
|
||||
* @param {String} aPingId The ping id.
|
||||
|
@ -76,6 +76,12 @@ this.TelemetryUtils = {
|
||||
return null;
|
||||
},
|
||||
|
||||
generateUUID: function() {
|
||||
let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
|
||||
// strip {}
|
||||
return str.substring(1, str.length - 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Find how many months passed between two dates.
|
||||
* @param {Object} aStartDate The starting date.
|
||||
|
@ -32,6 +32,7 @@ EXTRA_COMPONENTS += [
|
||||
EXTRA_JS_MODULES += [
|
||||
'TelemetryArchive.jsm',
|
||||
'TelemetryLog.jsm',
|
||||
'TelemetrySend.jsm',
|
||||
'TelemetryStopwatch.jsm',
|
||||
'TelemetryStorage.jsm',
|
||||
'TelemetryUtils.jsm',
|
||||
|
@ -149,6 +149,7 @@ function fakeNow(...args) {
|
||||
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm"),
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm"),
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm"),
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm"),
|
||||
];
|
||||
|
||||
for (let m of modules) {
|
||||
@ -160,14 +161,14 @@ function fakeNow(...args) {
|
||||
|
||||
// Fake the timeout functions for TelemetryController sending.
|
||||
function fakePingSendTimer(set, clear) {
|
||||
let ping = Cu.import("resource://gre/modules/TelemetryController.jsm");
|
||||
ping.Policy.setPingSendTimeout = set;
|
||||
ping.Policy.clearPingSendTimeout = clear;
|
||||
let module = Cu.import("resource://gre/modules/TelemetrySend.jsm");
|
||||
module.Policy.setPingSendTimeout = set;
|
||||
module.Policy.clearPingSendTimeout = clear;
|
||||
}
|
||||
|
||||
function fakeMidnightPingFuzzingDelay(delayMs) {
|
||||
let ping = Cu.import("resource://gre/modules/TelemetryController.jsm");
|
||||
ping.Policy.midnightPingFuzzingDelay = () => delayMs;
|
||||
let module = Cu.import("resource://gre/modules/TelemetrySend.jsm");
|
||||
module.Policy.midnightPingFuzzingDelay = () => delayMs;
|
||||
}
|
||||
|
||||
// Return a date that is |offset| ms in the future from |date|.
|
||||
|
@ -14,6 +14,7 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryArchive.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
@ -42,9 +43,9 @@ let gClientID = null;
|
||||
|
||||
function sendPing(aSendClientId, aSendEnvironment) {
|
||||
if (gServerStarted) {
|
||||
TelemetryController.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
} else {
|
||||
TelemetryController.setServer("http://doesnotexist");
|
||||
TelemetrySend.setServer("http://doesnotexist");
|
||||
}
|
||||
|
||||
let options = {
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
Cu.import("resource://gre/modules/Timer.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
|
||||
@ -59,8 +60,8 @@ add_task(function* test_sendTimeout() {
|
||||
httpServer.start(-1);
|
||||
|
||||
yield TelemetryController.setup();
|
||||
TelemetryController.setServer("http://localhost:" + httpServer.identity.primaryPort);
|
||||
TelemetryController.submitExternalPing("test-ping-type", {});
|
||||
TelemetrySend.setServer("http://localhost:" + httpServer.identity.primaryPort);
|
||||
yield TelemetryController.submitExternalPing("test-ping-type", {});
|
||||
|
||||
// Trigger the AsyncShutdown phase TelemetryController hangs off.
|
||||
AsyncShutdown.profileBeforeChange._trigger();
|
||||
|
@ -10,6 +10,7 @@ Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
|
||||
const PREF_ENABLED = "toolkit.telemetry.enabled";
|
||||
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
|
||||
@ -43,7 +44,7 @@ add_task(function* testSendPendingOnIdleDaily() {
|
||||
|
||||
// Telemetry will not send this ping at startup, because it's not overdue.
|
||||
yield TelemetryController.setup();
|
||||
TelemetryController.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
|
||||
let pendingPromise = new Promise(resolve =>
|
||||
gHttpServer.registerPrefixHandler("/submit/telemetry/", request => resolve(request)));
|
||||
@ -59,6 +60,9 @@ add_task(function* testSendPendingOnIdleDaily() {
|
||||
Services.obs.removeObserver(gatherPromise.resolve, "gather-telemetry");
|
||||
|
||||
// Check that the pending ping is correctly received.
|
||||
let ns = {};
|
||||
let module = Cu.import("resource://gre/modules/TelemetrySend.jsm", ns);
|
||||
module.TelemetrySendImpl.observe(null, "idle-daily", null);
|
||||
let request = yield pendingPromise;
|
||||
let ping = decodeRequestPayload(request);
|
||||
|
||||
|
@ -18,6 +18,7 @@ Cu.import("resource://testing-common/httpd.js", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
@ -31,8 +32,8 @@ XPCOMUtils.defineLazyGetter(this, "gDatareportingService",
|
||||
// OVERDUE_PING_FILE_AGE by 1 minute so that our test pings exceed
|
||||
// those points in time, even taking into account file system imprecision.
|
||||
const ONE_MINUTE_MS = 60 * 1000;
|
||||
const EXPIRED_PING_FILE_AGE = TelemetryStorage.MAX_PING_FILE_AGE + ONE_MINUTE_MS;
|
||||
const OVERDUE_PING_FILE_AGE = TelemetryStorage.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS;
|
||||
const EXPIRED_PING_FILE_AGE = TelemetrySend.MAX_PING_FILE_AGE + ONE_MINUTE_MS;
|
||||
const OVERDUE_PING_FILE_AGE = TelemetrySend.OVERDUE_PING_FILE_AGE + ONE_MINUTE_MS;
|
||||
|
||||
const PING_SAVE_FOLDER = "saved-telemetry-pings";
|
||||
const PING_TIMEOUT_LENGTH = 5000;
|
||||
@ -40,7 +41,7 @@ const EXPIRED_PINGS = 5;
|
||||
const OVERDUE_PINGS = 6;
|
||||
const OLD_FORMAT_PINGS = 4;
|
||||
const RECENT_PINGS = 4;
|
||||
const LRU_PINGS = TelemetryStorage.MAX_LRU_PINGS;
|
||||
const LRU_PINGS = TelemetrySend.MAX_LRU_PINGS;
|
||||
|
||||
const TOTAL_EXPECTED_PINGS = OVERDUE_PINGS + RECENT_PINGS + OLD_FORMAT_PINGS;
|
||||
|
||||
@ -91,8 +92,7 @@ let createSavedPings = Task.async(function* (aPingInfos) {
|
||||
*/
|
||||
let clearPings = Task.async(function* (aPingIds) {
|
||||
for (let pingId of aPingIds) {
|
||||
let filePath = getSavePathForPingId(pingId);
|
||||
yield File.remove(filePath);
|
||||
yield TelemetryStorage.removePendingPing(pingId);
|
||||
}
|
||||
});
|
||||
|
||||
@ -161,14 +161,14 @@ function stopHttpServer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Telemetry state.
|
||||
* Clear out all pending pings.
|
||||
*/
|
||||
function resetTelemetry() {
|
||||
// Quick and dirty way to clear TelemetryStorage's pendingPings
|
||||
// collection, and put it back in its initial state.
|
||||
let gen = TelemetryStorage.popPendingPings();
|
||||
for (let item of gen) {};
|
||||
}
|
||||
let clearPendingPings = Task.async(function*() {
|
||||
const pending = yield TelemetryStorage.loadPendingPingList();
|
||||
for (let p of pending) {
|
||||
yield TelemetryStorage.removePendingPing(p.id);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Creates and returns a TelemetryController instance in "testing"
|
||||
@ -205,7 +205,7 @@ add_task(function* setupEnvironment() {
|
||||
let directory = TelemetryStorage.pingDirectoryPath;
|
||||
yield File.makeDir(directory, { ignoreExisting: true, unixMode: OS.Constants.S_IRWXU });
|
||||
|
||||
yield resetTelemetry();
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -215,10 +215,12 @@ add_task(function* setupEnvironment() {
|
||||
add_task(function* test_expired_pings_are_deleted() {
|
||||
let pingTypes = [{ num: EXPIRED_PINGS, age: EXPIRED_PING_FILE_AGE }];
|
||||
let expiredPings = yield createSavedPings(pingTypes);
|
||||
yield startTelemetry();
|
||||
|
||||
yield TelemetryController.reset();
|
||||
assertReceivedPings(0);
|
||||
yield assertNotSaved(expiredPings);
|
||||
yield resetTelemetry();
|
||||
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -227,10 +229,11 @@ add_task(function* test_expired_pings_are_deleted() {
|
||||
add_task(function* test_recent_pings_not_sent() {
|
||||
let pingTypes = [{ num: RECENT_PINGS }];
|
||||
let recentPings = yield createSavedPings(pingTypes);
|
||||
yield startTelemetry();
|
||||
|
||||
yield TelemetryController.reset();
|
||||
assertReceivedPings(0);
|
||||
yield resetTelemetry();
|
||||
yield clearPings(recentPings);
|
||||
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -245,18 +248,16 @@ add_task(function* test_most_recent_pings_kept() {
|
||||
let head = pings.slice(0, LRU_PINGS);
|
||||
let tail = pings.slice(-3);
|
||||
|
||||
yield startTelemetry();
|
||||
let gen = TelemetryStorage.popPendingPings();
|
||||
yield TelemetryController.reset();
|
||||
const pending = yield TelemetryStorage.loadPendingPingList();
|
||||
|
||||
for (let item of gen) {
|
||||
for (let id of tail) {
|
||||
do_check_neq(id, item.id);
|
||||
}
|
||||
for (let id of tail) {
|
||||
const found = pending.some(p => p.id == id);
|
||||
Assert.ok(!found, "Should have discarded the oldest pings");
|
||||
}
|
||||
|
||||
assertNotSaved(tail);
|
||||
yield resetTelemetry();
|
||||
yield clearPings(pings);
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -301,10 +302,10 @@ add_task(function* test_overdue_old_format() {
|
||||
};
|
||||
|
||||
const PING_FILES_PATHS = [
|
||||
Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, PING_OLD_FORMAT.slug),
|
||||
Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, PING_NO_INFO.slug),
|
||||
Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, PING_NO_PAYLOAD.slug),
|
||||
Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, "no-slug-file"),
|
||||
getSavePathForPingId(PING_OLD_FORMAT.slug),
|
||||
getSavePathForPingId(PING_NO_INFO.slug),
|
||||
getSavePathForPingId(PING_NO_PAYLOAD.slug),
|
||||
getSavePathForPingId("no-slug-file"),
|
||||
];
|
||||
|
||||
// Write the ping to file and make it overdue.
|
||||
@ -317,14 +318,14 @@ add_task(function* test_overdue_old_format() {
|
||||
yield File.setDates(PING_FILES_PATHS[f], null, Date.now() - OVERDUE_PING_FILE_AGE);
|
||||
}
|
||||
|
||||
yield startTelemetry();
|
||||
yield TelemetryController.reset();
|
||||
assertReceivedPings(OLD_FORMAT_PINGS);
|
||||
|
||||
// |TelemetryStorage.cleanup| doesn't know how to remove a ping with no slug or id,
|
||||
// so remove it manually so that the next test doesn't fail.
|
||||
yield OS.File.remove(PING_FILES_PATHS[3]);
|
||||
|
||||
yield resetTelemetry();
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -343,13 +344,14 @@ add_task(function* test_overdue_pings_trigger_send() {
|
||||
let expiredPings = pings.slice(RECENT_PINGS, RECENT_PINGS + EXPIRED_PINGS);
|
||||
let overduePings = pings.slice(-OVERDUE_PINGS);
|
||||
|
||||
yield startTelemetry();
|
||||
yield TelemetryController.reset();
|
||||
assertReceivedPings(TOTAL_EXPECTED_PINGS);
|
||||
|
||||
yield assertNotSaved(recentPings);
|
||||
yield assertNotSaved(expiredPings);
|
||||
yield assertNotSaved(overduePings);
|
||||
yield resetTelemetry();
|
||||
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
/**
|
||||
@ -395,10 +397,10 @@ add_task(function* test_overdue_old_format() {
|
||||
receivedPings++;
|
||||
});
|
||||
|
||||
yield startTelemetry();
|
||||
yield TelemetryController.reset();
|
||||
Assert.equal(receivedPings, 1, "We must receive a ping in the old format.");
|
||||
|
||||
yield resetTelemetry();
|
||||
yield clearPendingPings();
|
||||
});
|
||||
|
||||
add_task(function* teardown() {
|
||||
|
@ -18,6 +18,7 @@ Cu.import("resource://gre/modules/TelemetryController.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryStorage.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
Cu.import("resource://gre/modules/Promise.jsm", this);
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
@ -97,14 +98,21 @@ function truncateDateToDays(date) {
|
||||
function sendPing() {
|
||||
TelemetrySession.gatherStartup();
|
||||
if (gServerStarted) {
|
||||
TelemetryController.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
return TelemetrySession.testPing();
|
||||
} else {
|
||||
TelemetryController.setServer("http://doesnotexist");
|
||||
TelemetrySend.setServer("http://doesnotexist");
|
||||
return TelemetrySession.testPing();
|
||||
}
|
||||
}
|
||||
|
||||
let clearPendingPings = Task.async(function*() {
|
||||
const pending = yield TelemetryStorage.loadPendingPingList();
|
||||
for (let p of pending) {
|
||||
yield TelemetryStorage.removePendingPing(p.id);
|
||||
}
|
||||
});
|
||||
|
||||
function wrapWithExceptionHandler(f) {
|
||||
function wrapper(...args) {
|
||||
try {
|
||||
@ -267,7 +275,7 @@ function checkPayloadInfo(data) {
|
||||
Assert.ok(data.timezoneOffset <= 12*60, "The timezone must be in a valid range.");
|
||||
}
|
||||
|
||||
function checkPayload(payload, reason, successfulPings) {
|
||||
function checkPayload(payload, reason, successfulPings, savedPings) {
|
||||
Assert.ok("info" in payload, "Payload must contain an info section.");
|
||||
checkPayloadInfo(payload.info);
|
||||
|
||||
@ -275,7 +283,7 @@ function checkPayload(payload, reason, successfulPings) {
|
||||
Assert.ok(payload.simpleMeasurements.uptime >= 0);
|
||||
Assert.equal(payload.simpleMeasurements.startupInterrupted, 1);
|
||||
Assert.equal(payload.simpleMeasurements.shutdownDuration, SHUTDOWN_TIME);
|
||||
Assert.equal(payload.simpleMeasurements.savedPings, 1);
|
||||
Assert.equal(payload.simpleMeasurements.savedPings, savedPings);
|
||||
Assert.ok("maximalNumberOfConcurrentThreads" in payload.simpleMeasurements);
|
||||
Assert.ok(payload.simpleMeasurements.maximalNumberOfConcurrentThreads >= gNumberOfThreadsLaunched);
|
||||
|
||||
@ -304,7 +312,9 @@ function checkPayload(payload, reason, successfulPings) {
|
||||
const TELEMETRY_TEST_KEYED_COUNT = "TELEMETRY_TEST_KEYED_COUNT";
|
||||
const READ_SAVED_PING_SUCCESS = "READ_SAVED_PING_SUCCESS";
|
||||
|
||||
Assert.ok(TELEMETRY_PING in payload.histograms);
|
||||
if (successfulPings > 0) {
|
||||
Assert.ok(TELEMETRY_PING in payload.histograms);
|
||||
}
|
||||
Assert.ok(READ_SAVED_PING_SUCCESS in payload.histograms);
|
||||
Assert.ok(TELEMETRY_TEST_FLAG in payload.histograms);
|
||||
Assert.ok(TELEMETRY_TEST_COUNT in payload.histograms);
|
||||
@ -346,17 +356,19 @@ function checkPayload(payload, reason, successfulPings) {
|
||||
Assert.equal(uneval(count), uneval(expected_count));
|
||||
|
||||
// There should be one successful report from the previous telemetry ping.
|
||||
const expected_tc = {
|
||||
range: [1, 2],
|
||||
bucket_count: 3,
|
||||
histogram_type: 2,
|
||||
values: {0:2, 1:successfulPings, 2:0},
|
||||
sum: successfulPings,
|
||||
sum_squares_lo: successfulPings,
|
||||
sum_squares_hi: 0
|
||||
};
|
||||
let tc = payload.histograms[TELEMETRY_SUCCESS];
|
||||
Assert.equal(uneval(tc), uneval(expected_tc));
|
||||
if (successfulPings > 0) {
|
||||
const expected_tc = {
|
||||
range: [1, 2],
|
||||
bucket_count: 3,
|
||||
histogram_type: 2,
|
||||
values: {0:2, 1:successfulPings, 2:0},
|
||||
sum: successfulPings,
|
||||
sum_squares_lo: successfulPings,
|
||||
sum_squares_hi: 0
|
||||
};
|
||||
let tc = payload.histograms[TELEMETRY_SUCCESS];
|
||||
Assert.equal(uneval(tc), uneval(expected_tc));
|
||||
}
|
||||
|
||||
let h = payload.histograms[READ_SAVED_PING_SUCCESS];
|
||||
Assert.equal(h.values[0], 1);
|
||||
@ -562,42 +574,42 @@ add_task(function* test_simplePing() {
|
||||
|
||||
// Saves the current session histograms, reloads them, performs a ping
|
||||
// and checks that the dummy http server received both the previously
|
||||
// saved histograms and the new ones.
|
||||
// saved ping and the new one.
|
||||
add_task(function* test_saveLoadPing() {
|
||||
let histogramsFile = getSavedPingFile("saved-histograms.dat");
|
||||
// Let's start out with a defined state.
|
||||
yield clearPendingPings();
|
||||
yield TelemetryController.reset();
|
||||
gRequestIterator = Iterator(new Request());
|
||||
|
||||
// Setup test data and trigger pings.
|
||||
setupTestData();
|
||||
yield TelemetrySession.testSaveHistograms(histogramsFile);
|
||||
yield TelemetryStorage.testLoadHistograms(histogramsFile);
|
||||
yield TelemetrySession.testSavePendingPing();
|
||||
yield sendPing();
|
||||
|
||||
// Get requests received by dummy server.
|
||||
let request1 = yield gRequestIterator.next();
|
||||
let request2 = yield gRequestIterator.next();
|
||||
let requests = [
|
||||
yield gRequestIterator.next(),
|
||||
yield gRequestIterator.next(),
|
||||
];
|
||||
|
||||
Assert.equal(request1.getHeader("content-type"), "application/json; charset=UTF-8",
|
||||
"The request must have the correct content-type.");
|
||||
Assert.equal(request2.getHeader("content-type"), "application/json; charset=UTF-8",
|
||||
"The request must have the correct content-type.");
|
||||
for (let req of requests) {
|
||||
Assert.equal(req.getHeader("content-type"), "application/json; charset=UTF-8",
|
||||
"The request must have the correct content-type.");
|
||||
}
|
||||
|
||||
// We decode both requests to check for the |reason|.
|
||||
let ping1 = decodeRequestPayload(request1);
|
||||
let ping2 = decodeRequestPayload(request2);
|
||||
let pings = [for (req of requests) decodeRequestPayload(req)];
|
||||
|
||||
// Check we have the correct two requests. Ordering is not guaranteed. The ping type
|
||||
// is encoded in the URL.
|
||||
let requestTypeComponent = request1.path.split("/")[4];
|
||||
if (requestTypeComponent === PING_TYPE_MAIN) {
|
||||
checkPingFormat(ping1, PING_TYPE_MAIN, true, true);
|
||||
checkPayload(ping1.payload, REASON_TEST_PING, 1);
|
||||
checkPingFormat(ping2, PING_TYPE_SAVED_SESSION, true, true);
|
||||
checkPayload(ping2.payload, REASON_SAVED_SESSION, 1);
|
||||
} else {
|
||||
checkPingFormat(ping1, PING_TYPE_SAVED_SESSION, true, true);
|
||||
checkPayload(ping1.payload, REASON_SAVED_SESSION, 1);
|
||||
checkPingFormat(ping2, PING_TYPE_MAIN, true, true);
|
||||
checkPayload(ping2.payload, REASON_TEST_PING, 1);
|
||||
if (pings[0].type != PING_TYPE_MAIN) {
|
||||
pings.reverse();
|
||||
}
|
||||
|
||||
checkPingFormat(pings[0], PING_TYPE_MAIN, true, true);
|
||||
checkPayload(pings[0].payload, REASON_TEST_PING, 0, 1);
|
||||
checkPingFormat(pings[1], PING_TYPE_SAVED_SESSION, true, true);
|
||||
checkPayload(pings[1].payload, REASON_SAVED_SESSION, 0, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_checkSubsessionHistograms() {
|
||||
@ -855,7 +867,7 @@ add_task(function* test_dailyCollection() {
|
||||
|
||||
// Init and check timer.
|
||||
yield TelemetrySession.setup();
|
||||
TelemetryController.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
|
||||
// Set histograms to expected state.
|
||||
const COUNT_ID = "TELEMETRY_TEST_COUNT";
|
||||
@ -1066,7 +1078,7 @@ add_task(function* test_environmentChange() {
|
||||
|
||||
// Setup.
|
||||
yield TelemetrySession.setup();
|
||||
TelemetryController.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
TelemetrySend.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
|
||||
TelemetryEnvironment._watchPreferences(PREFS_TO_WATCH);
|
||||
|
||||
// Set histograms to expected state.
|
||||
@ -1121,16 +1133,18 @@ add_task(function* test_environmentChange() {
|
||||
});
|
||||
|
||||
// Checks that an expired histogram file is deleted when loaded.
|
||||
add_task(function* test_runOldPingFile() {
|
||||
let histogramsFile = getSavedPingFile("old-histograms.dat");
|
||||
add_task(function* test_pruneOldPingFile() {
|
||||
const id = generateUUID();
|
||||
const path = OS.Path.join(TelemetryStorage.pingDirectoryPath, id);
|
||||
yield OS.File.writeAtomic(path, "{}", {noOverwrite: false});
|
||||
|
||||
yield TelemetrySession.testSaveHistograms(histogramsFile);
|
||||
do_check_true(histogramsFile.exists());
|
||||
let mtime = histogramsFile.lastModifiedTime;
|
||||
histogramsFile.lastModifiedTime = mtime - (14 * 24 * 60 * 60 * 1000 + 60000); // 14 days, 1m
|
||||
// fake a time 14 days & 1m earlier
|
||||
const now = new Date().getTime();
|
||||
const fakeMtime = now - (14 * 24 * 60 * 60 * 1000 + 60000);
|
||||
OS.File.setDates(path, null, fakeMtime);
|
||||
|
||||
yield TelemetryStorage.testLoadHistograms(histogramsFile);
|
||||
do_check_false(histogramsFile.exists());
|
||||
yield TelemetryController.reset();
|
||||
Assert.ok(!(yield OS.File.exists(path)), "File should have been removed.");
|
||||
});
|
||||
|
||||
add_task(function* test_savedPingsOnShutdown() {
|
||||
@ -1144,11 +1158,13 @@ add_task(function* test_savedPingsOnShutdown() {
|
||||
yield OS.File.makeDir(dir);
|
||||
yield TelemetrySession.shutdown();
|
||||
|
||||
yield TelemetryStorage.loadSavedPings();
|
||||
Assert.equal(TelemetryStorage.pingsLoaded, expectedPings);
|
||||
yield TelemetryController.reset();
|
||||
const pending = yield TelemetryStorage.loadPendingPingList();
|
||||
Assert.equal(pending.length, expectedPings,
|
||||
"Should have the correct number of pending pings.");
|
||||
|
||||
let pingsIterator = TelemetryStorage.popPendingPings();
|
||||
for (let ping of pingsIterator) {
|
||||
const pings = [for (p of pending) yield TelemetryStorage.loadPendingPing(p.id)];
|
||||
for (let ping of pings) {
|
||||
Assert.ok("type" in ping);
|
||||
|
||||
let expectedReason =
|
||||
@ -1349,8 +1365,10 @@ add_task(function* test_abortedSession() {
|
||||
// saved pings directory when it starts.
|
||||
yield TelemetryStorage.savePingToFile(abortedSessionPing, ABORTED_FILE, false);
|
||||
|
||||
yield clearPendingPings();
|
||||
gRequestIterator = Iterator(new Request());
|
||||
yield TelemetrySession.reset();
|
||||
yield TelemetryController.reset();
|
||||
|
||||
Assert.ok(!(yield OS.File.exists(ABORTED_FILE)),
|
||||
"The aborted session ping must be removed from the aborted session ping directory.");
|
||||
@ -1362,16 +1380,20 @@ add_task(function* test_abortedSession() {
|
||||
Assert.ok((yield OS.File.exists(PENDING_PING_FILE)),
|
||||
"The aborted session ping must exist in the saved pings directory.");
|
||||
|
||||
// Trick: make the aborted ping file overdue so that it gets sent immediately when
|
||||
// resetting TelemetryController.
|
||||
const OVERDUE_PING_FILE_AGE = TelemetryStorage.OVERDUE_PING_FILE_AGE + 60 * 1000;
|
||||
yield OS.File.setDates(PENDING_PING_FILE, null, Date.now() - OVERDUE_PING_FILE_AGE);
|
||||
yield TelemetryController.reset();
|
||||
// Trigger sending the pending pings.
|
||||
yield sendPing();
|
||||
|
||||
// Wait for the aborted-session ping.
|
||||
let request = yield gRequestIterator.next();
|
||||
let receivedPing = decodeRequestPayload(request);
|
||||
Assert.equal(receivedPing.payload.info.reason, REASON_ABORTED_SESSION);
|
||||
// We should receive two pings, one of them an aborted-session ping.
|
||||
let requests = [
|
||||
yield gRequestIterator.next(),
|
||||
yield gRequestIterator.next(),
|
||||
];
|
||||
let pings = [for (req of requests) decodeRequestPayload(req)].filter((p) => {
|
||||
return p.type == PING_TYPE_MAIN && p.payload.info.reason == REASON_ABORTED_SESSION;
|
||||
});
|
||||
|
||||
Assert.equal(pings.length, 1, "Should have received one aborted-session ping.");
|
||||
let receivedPing = pings[0];
|
||||
Assert.equal(receivedPing.id, abortedSessionPing.id);
|
||||
|
||||
yield TelemetrySession.shutdown();
|
||||
@ -1512,6 +1534,7 @@ add_task(function* test_schedulerEnvironmentReschedules() {
|
||||
[PREF_TEST, TelemetryEnvironment.RECORD_PREF_VALUE],
|
||||
]);
|
||||
|
||||
yield clearPendingPings();
|
||||
gRequestIterator = Iterator(new Request());
|
||||
|
||||
// Set a fake current date and start Telemetry.
|
||||
@ -1554,6 +1577,7 @@ add_task(function* test_schedulerNothingDue() {
|
||||
|
||||
// Remove any aborted-session ping from the previous tests.
|
||||
yield OS.File.removeDir(DATAREPORTING_PATH, { ignoreAbsent: true });
|
||||
yield clearPendingPings();
|
||||
|
||||
// We don't expect to receive any ping in this test, so assert if we do.
|
||||
registerPingHandler((req, res) => {
|
||||
@ -1590,6 +1614,7 @@ add_task(function* test_pingExtendedStats() {
|
||||
// Disable sending extended statistics.
|
||||
Telemetry.canRecordExtended = false;
|
||||
|
||||
yield clearPendingPings();
|
||||
gRequestIterator = Iterator(new Request());
|
||||
yield TelemetrySession.reset();
|
||||
yield sendPing();
|
||||
@ -1653,6 +1678,7 @@ add_task(function* test_schedulerUserIdle() {
|
||||
schedulerTimeout = timeout;
|
||||
}, () => {});
|
||||
yield TelemetrySession.reset();
|
||||
yield clearPendingPings();
|
||||
gRequestIterator = Iterator(new Request());
|
||||
|
||||
// When not idle, the scheduler should have a 5 minutes tick interval.
|
||||
@ -1693,7 +1719,9 @@ add_task(function* test_sendDailyOnIdle() {
|
||||
fakeSchedulerTimer((callback, timeout) => {
|
||||
schedulerTickCallback = callback;
|
||||
}, () => {});
|
||||
|
||||
yield TelemetrySession.reset();
|
||||
yield clearPendingPings();
|
||||
|
||||
// Make sure we are not sending a daily before midnight when active.
|
||||
now = new Date(2040, 1, 1, 23, 55, 0);
|
||||
|
@ -44,7 +44,7 @@ skip-if = android_version == "18"
|
||||
# Bug 1144395: crash on Android 4.3
|
||||
skip-if = android_version == "18"
|
||||
[test_TelemetrySendOldPings.js]
|
||||
skip-if = debug == true || os == "android" # Disabled due to intermittent orange on Android
|
||||
skip-if = os == "android" # Disabled due to intermittent orange on Android
|
||||
[test_TelemetrySession.js]
|
||||
# Bug 1144395: crash on Android 4.3
|
||||
skip-if = android_version == "18"
|
||||
|
@ -675,11 +675,17 @@ PopupNotifications.prototype = {
|
||||
_update: function PopupNotifications_update(notifications, anchors = new Set(), dismissShowing = false) {
|
||||
if (anchors instanceof Ci.nsIDOMXULElement)
|
||||
anchors = new Set([anchors]);
|
||||
|
||||
if (!notifications)
|
||||
notifications = this._currentNotifications;
|
||||
let haveNotifications = notifications.length > 0;
|
||||
if (!anchors.size && haveNotifications)
|
||||
anchors = this._getAnchorsForNotifications(notifications);
|
||||
let notificationsToShow = [];
|
||||
// Filter out notifications that have been dismissed.
|
||||
notificationsToShow = notifications.filter(function (n) {
|
||||
return !n.dismissed && !n.options.neverShow;
|
||||
});
|
||||
|
||||
if (!anchors.size && notificationsToShow.length)
|
||||
anchors = this._getAnchorsForNotifications(notificationsToShow);
|
||||
|
||||
let useIconBox = !!this.iconBox;
|
||||
if (useIconBox && anchors.size) {
|
||||
@ -696,13 +702,8 @@ PopupNotifications.prototype = {
|
||||
this._hideIcons();
|
||||
}
|
||||
|
||||
let notificationsToShow = [];
|
||||
let haveNotifications = notifications.length > 0;
|
||||
if (haveNotifications) {
|
||||
// Filter out notifications that have been dismissed.
|
||||
notificationsToShow = notifications.filter(function (n) {
|
||||
return !n.dismissed && !n.options.neverShow;
|
||||
});
|
||||
|
||||
if (useIconBox) {
|
||||
this._showIcons(notifications);
|
||||
this.iconBox.hidden = false;
|
||||
|
@ -36,4 +36,7 @@ toolkit.jar:
|
||||
+ skin/classic/mozapps/places/defaultFavicon.png (places/defaultFavicon.png)
|
||||
#endif
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://mozapps/skin/passwordmgr/key.png chrome://mozapps/skin/passwordmgr/key-16.png
|
||||
|
@ -215,4 +215,7 @@ toolkit.jar:
|
||||
skin/classic/global/tree/folder.png (tree/folder.png)
|
||||
skin/classic/global/tree/folder@2x.png (tree/folder@2x.png)
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://global/skin/dirListing/local.png chrome://global/skin/dirListing/folder.png
|
||||
|
@ -84,6 +84,9 @@ toolkit.jar:
|
||||
#endif
|
||||
skin/classic/mozapps/handling/handling.css (handling/handling.css)
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://mozapps/skin/extensions/category-extensions.png chrome://mozapps/skin/extensions/extensionGeneric.png
|
||||
% override chrome://mozapps/skin/extensions/category-languages.png chrome://mozapps/skin/extensions/localeGeneric.png
|
||||
% override chrome://mozapps/skin/extensions/category-themes.png chrome://mozapps/skin/extensions/themeGeneric.png
|
||||
|
@ -243,6 +243,9 @@ toolkit.jar:
|
||||
skin/classic/global/tree/twisty-clsd-XP.png (tree/twisty-clsd-XP.png)
|
||||
skin/classic/global/tree/twisty-open-XP.png (tree/twisty-open-XP.png)
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://global/skin/console/console-toolbar.png chrome://global/skin/console/console-toolbar-XP.png osversion<6
|
||||
% override chrome://global/skin/dirListing/folder.png chrome://global/skin/dirListing/folder-XP.png osversion<6
|
||||
% override chrome://global/skin/dirListing/local.png chrome://global/skin/dirListing/local-XP.png osversion<6
|
||||
@ -283,5 +286,8 @@ toolkit.jar:
|
||||
% override chrome://global/skin/tree/twisty-open.png chrome://global/skin/tree/twisty-open-XP.png osversion<6
|
||||
#endif
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://global/skin/arrow/arrow-lft-hov.gif chrome://global/skin/arrow/arrow-lft.gif
|
||||
% override chrome://global/skin/arrow/arrow-rit-hov.gif chrome://global/skin/arrow/arrow-rit.gif
|
||||
|
@ -94,6 +94,9 @@ toolkit.jar:
|
||||
skin/classic/mozapps/profile/profileicon-XP.png (profile/profileicon-XP.png)
|
||||
skin/classic/mozapps/update/downloadButtons-XP.png (update/downloadButtons-XP.png)
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://mozapps/skin/downloads/downloadButtons.png chrome://mozapps/skin/downloads/downloadButtons-XP.png osversion<6
|
||||
% override chrome://mozapps/skin/downloads/downloadIcon.png chrome://mozapps/skin/downloads/downloadIcon-XP.png osversion<6
|
||||
% override chrome://mozapps/skin/extensions/category-discover.png chrome://mozapps/skin/extensions/category-discover-XP.png osversion<6
|
||||
@ -112,6 +115,9 @@ toolkit.jar:
|
||||
% override chrome://mozapps/skin/update/downloadButtons.png chrome://mozapps/skin/update/downloadButtons-XP.png osversion<6
|
||||
#endif
|
||||
|
||||
#if MOZ_BUILD_APP == browser
|
||||
../browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}/chrome.jar:
|
||||
#endif
|
||||
% override chrome://mozapps/skin/extensions/category-dictionaries.png chrome://mozapps/skin/extensions/dictionaryGeneric.png
|
||||
% override chrome://mozapps/skin/extensions/category-experiments.png chrome://mozapps/skin/extensions/experimentGeneric.png
|
||||
% override chrome://mozapps/skin/extensions/category-extensions.png chrome://mozapps/skin/extensions/extensionGeneric.png
|
||||
|
@ -135,7 +135,9 @@ static const ManifestDirective kParsingTable[] = {
|
||||
nullptr, &nsChromeRegistry::ManifestStyle, nullptr
|
||||
},
|
||||
{
|
||||
"override", 2, false, true, true, true, false,
|
||||
// NB: note that while skin manifests can use this, they are only allowed
|
||||
// to use it for chrome://../skin/ URLs
|
||||
"override", 2, false, false, true, true, false,
|
||||
nullptr, &nsChromeRegistry::ManifestOverride, nullptr
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user