author | Ryan VanderMeulen <ryanvm@gmail.com> |
Fri, 27 Feb 2015 13:23:30 -0500 | |
changeset 231187 | b94bcbc389e828344479f700d3970537c4abfff2 |
parent 231155 | ff17afa674114c7ed0370e6da03e48a4e0ceecb6 (current diff) |
parent 231186 | ae4495c44a76a90b4dcc85f5b288a2bfe4c93893 (diff) |
child 231188 | b49a6d6505585cfb0fd091c07e4a197ae0d0d74f |
child 231191 | 5def1d193a0c6f6a8b0ae175ef2be25b46ee248a |
child 231321 | d97e4aee9a3a4d429c634cd2df5bf8585cc199b1 |
child 231361 | ee5d1f234412b31e665ed64079d5c883fa84c5d5 |
push id | 28346 |
push user | ryanvm@gmail.com |
push date | Fri, 27 Feb 2015 18:23:44 +0000 |
treeherder | mozilla-central@b94bcbc389e8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 39.0a1 |
first release with | nightly linux32
b94bcbc389e8
/
39.0a1
/
20150228030231
/
files
nightly linux64
b94bcbc389e8
/
39.0a1
/
20150228030231
/
files
nightly mac
b94bcbc389e8
/
39.0a1
/
20150228030231
/
files
nightly win32
b94bcbc389e8
/
39.0a1
/
20150228030231
/
files
nightly win64
b94bcbc389e8
/
39.0a1
/
20150228030231
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
39.0a1
/
20150228030231
/
pushlog to previous
nightly linux64
39.0a1
/
20150228030231
/
pushlog to previous
nightly mac
39.0a1
/
20150228030231
/
pushlog to previous
nightly win32
39.0a1
/
20150228030231
/
pushlog to previous
nightly win64
39.0a1
/
20150228030231
/
pushlog to previous
|
--- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -10,19 +10,19 @@ <!--original fetch url was git://codeaurora.org/--> <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <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="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- 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"/> @@ -129,16 +129,16 @@ <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/> <!-- dolphin specific things --> <project name="device/sprd" path="device/sprd" revision="8609ba49ab518822691528312b9c87b656164c58"/> <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/> <project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/> <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/> <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/> <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/> - <project name="kernel/common" path="kernel" revision="0b4249f88cb8faadfbf80be207af14198dec08e3"/> + <project name="kernel/common" path="kernel" revision="1ef12a094a59d966a2576d0991a3618b591a7da9"/> <project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/> - <project name="u-boot" path="u-boot" revision="6980cf8f8cf9c1d43ff92b7af13425a5ed531d12"/> + <project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/> <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/> <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="c985699a5140b6dd5c7a43295e4d4397ce9b1267"/> <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/> <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/> </manifest>
--- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -14,18 +14,18 @@ <!--original fetch url was git://github.com/apitrace/--> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="97c3d9b8b87774ca7a08c89145e95b55652459ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/> <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
--- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -12,18 +12,18 @@ <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <!-- Stock Android things --> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/> <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="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
--- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -10,19 +10,19 @@ <!--original fetch url was git://codeaurora.org/--> <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <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="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- 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"/>
--- a/b2g/config/emulator-l/sources.xml +++ b/b2g/config/emulator-l/sources.xml @@ -10,19 +10,19 @@ <!--original fetch url was git://codeaurora.org/--> <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <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="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- 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="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
--- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -14,18 +14,18 @@ <!--original fetch url was git://github.com/apitrace/--> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="97c3d9b8b87774ca7a08c89145e95b55652459ef"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/> <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
--- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -10,19 +10,19 @@ <!--original fetch url was git://codeaurora.org/--> <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <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="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- 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"/>
--- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -12,18 +12,18 @@ <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <!-- Stock Android things --> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/> <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"/>
--- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,9 +1,9 @@ { "git": { - "git_revision": "7512026a377271a0cade12d70846557f0bc7781c", + "git_revision": "f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f", "remote": "https://git.mozilla.org/releases/gaia.git", "branch": "" }, - "revision": "53713e514b92cccfd55404cebd2b627cb9804bc2", + "revision": "c00390d70e15726ba3e4c50bd2353fbf991f25c9", "repo_path": "integration/gaia-central" }
--- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -12,18 +12,18 @@ <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <!-- Stock Android things --> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/> <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="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
--- a/b2g/config/nexus-5-l/sources.xml +++ b/b2g/config/nexus-5-l/sources.xml @@ -10,19 +10,19 @@ <!--original fetch url was git://codeaurora.org/--> <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <!--original fetch url was https://git.mozilla.org/releases--> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3"> <copyfile dest="Makefile" src="core/root.mk"/> </project> - <project name="gaia" path="gaia" remote="mozillaorg" revision="7512026a377271a0cade12d70846557f0bc7781c"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="f34ce82a840ad3c0aed3bfff18517b3f6a0eb37f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3905180d9e2dbe03d7ce3b3a6debe4d4adc938d1"/> + <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2e85143db5d5f6edc4c2b97263c02b558fee757e"/> <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/> <project name="moztt" path="external/moztt" remote="b2g" revision="c42985975f2bbc42859b9136ed348186d989b93d"/> <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="a1ddea3133e0807350326cee5dcf0d06fad00c08"/> <!-- 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="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/> @@ -136,17 +136,17 @@ <project name="platform/system/vold" path="system/vold" revision="eb59d2afd5f6e1cbab2ef985a8dd1c7105b499e8"/> <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="ea531874885eed7f68802048218ed86dde927f58"/> <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="df7e0cfbbc7e954ed26c73ac17832a5ff035f046"/> <project name="platform_frameworks_wilhelm" path="frameworks/wilhelm" remote="b2g" revision="73f7e7f12c8c5459f7a39e2fa343f083c942864d"/> <project name="platform_system_core" path="system/core" remote="b2g" revision="4df51d9abf6cc9a6ec49b965e621699e0e6dc4fb"/> <default remote="caf" revision="refs/tags/android-5.0.0_r6" sync-j="4"/> <!-- Nexus 5 specific things --> <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="ba62cc8b78c30d36181b8060a2016cc8da166236"/> - <project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="dc525eeb67301a44f514a7ac40a59ec592b34e31"/> + <project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="a6f00b0d7519168329ca4a3f66d3be30d66e0c9e"/> <project name="device/qcom/common" path="device/qcom/common" remote="caf" revision="3697e5acf25629b82658334e3f8ee3b6df5becab"/> <project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="1268f640184df5ef759ada669f101a613451673a"/> <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0cb8574d338bf9f15b45ace7c08ad6deae9673ee"/> <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="16abda2258c9aa1ed78f00bb0a9b2b43b4cb919e"/> <project name="platform/hardware/broadcom/libbt" path="hardware/broadcom/libbt" revision="3e856528121ae0af0ca26c97cb563160c7e16d85"/> <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="d9e716e14e685f4fc124ae591a1cd0899bc4d7b5"/> <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="505aa92674337b76ce7d038800f2a6d7dc340ac9"/> <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="0a21f566d8c9b01b9754d639e13358bdcb6c7650"/>
--- a/dom/downloads/DownloadsAPI.js +++ b/dom/downloads/DownloadsAPI.js @@ -16,16 +16,22 @@ Cu.import("resource://gre/modules/Downlo XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", "nsIMessageSender"); XPCOMUtils.defineLazyServiceGetter(this, "volumeService", "@mozilla.org/telephony/volume-service;1", "nsIVolumeService"); +/** + * The content process implementations of navigator.mozDownloadManager and its + * DOMDownload download objects. Uses DownloadsIPC.jsm to communicate with + * DownloadsAPI.jsm in the parent process. + */ + function debug(aStr) { #ifdef MOZ_DEBUG dump("-*- DownloadsAPI.js : " + aStr + "\n"); #endif } function DOMDownloadManagerImpl() { debug("DOMDownloadManagerImpl constructor"); @@ -35,16 +41,25 @@ DOMDownloadManagerImpl.prototype = { __proto__: DOMRequestIpcHelper.prototype, // nsIDOMGlobalPropertyInitializer implementation init: function(aWindow) { debug("DownloadsManager init"); this.initDOMRequestHelper(aWindow, ["Downloads:Added", "Downloads:Removed"]); + + // Get the manifest URL if this is an installed app + let appsService = Cc["@mozilla.org/AppsService;1"] + .getService(Ci.nsIAppsService); + let principal = aWindow.document.nodePrincipal; + // This returns the empty string if we're not an installed app. Coerce to + // null. + this._manifestURL = appsService.getManifestURLByLocalId(principal.appId) || + null; }, uninit: function() { debug("uninit"); downloadsCache.evict(this._window); }, set ondownloadstart(aHandler) { @@ -74,33 +89,18 @@ DOMDownloadManagerImpl.prototype = { aReject("GetDownloadsError"); } ); }.bind(this)); }, clearAllDone: function() { debug("clearAllDone()"); - return this.createPromise(function (aResolve, aReject) { - DownloadsIPC.clearAllDone().then( - function(aDownloads) { - // Turn the list of download objects into DOM objects and - // send them. - let array = new this._window.Array(); - for (let id in aDownloads) { - let dom = createDOMDownloadObject(this._window, aDownloads[id]); - array.push(this._prepareForContent(dom)); - } - aResolve(array); - }.bind(this), - function() { - aReject("ClearAllDoneError"); - } - ); - }.bind(this)); + // This is a void function; we just kick it off. No promises, etc. + DownloadsIPC.clearAllDone(); }, remove: function(aDownload) { debug("remove " + aDownload.url + " " + aDownload.id); return this.createPromise(function (aResolve, aReject) { if (!downloadsCache.has(this._window, aDownload.id)) { debug("no download " + aDownload.id); aReject("InvalidDownload"); @@ -116,16 +116,77 @@ DOMDownloadManagerImpl.prototype = { }.bind(this), function() { aReject("RemoveError"); } ); }.bind(this)); }, + adoptDownload: function(aAdoptDownloadDict) { + // Our AdoptDownloadDict only includes simple types, which WebIDL enforces. + // We have no object/any types so we do not need to worry about invoking + // JSON.stringify (and it inheriting our security privileges). + debug("adoptDownload"); + return this.createPromise(function (aResolve, aReject) { + if (!aAdoptDownloadDict) { + debug("Download dictionary is required!"); + aReject("InvalidDownload"); + return; + } + if (!aAdoptDownloadDict.storageName || !aAdoptDownloadDict.storagePath || + !aAdoptDownloadDict.contentType) { + debug("Missing one of: storageName, storagePath, contentType"); + aReject("InvalidDownload"); + return; + } + + // Convert storageName/storagePath to a local filesystem path. + let volume; + // getVolumeByName throws if you give it something it doesn't like + // because XPConnect converts the NS_ERROR_NOT_AVAILABLE to an + // exception. So catch it. + try { + volume = volumeService.getVolumeByName(aAdoptDownloadDict.storageName); + } catch (ex) {} + if (!volume) { + debug("Invalid storage name: " + aAdoptDownloadDict.storageName); + aReject("InvalidDownload"); + return; + } + let computedPath = volume.mountPoint + '/' + + aAdoptDownloadDict.storagePath; + // We validate that there is actually a file at the given path in the + // parent process in DownloadsAPI.js because that's where the file + // access would actually occur either way. + + // Create a DownloadsAPI.jsm 'jsonDownload' style representation. + let jsonDownload = { + url: aAdoptDownloadDict.url, + path: computedPath, + contentType: aAdoptDownloadDict.contentType, + startTime: aAdoptDownloadDict.startTime.valueOf() || Date.now(), + sourceAppManifestURL: this._manifestURL + }; + + DownloadsIPC.adoptDownload(jsonDownload).then( + function(aResult) { + let domDownload = createDOMDownloadObject(this._window, aResult); + aResolve(this._prepareForContent(domDownload)); + }.bind(this), + function(aResult) { + // This will be one of: AdoptError (generic catch-all), + // AdoptNoSuchFile, AdoptFileIsDirectory + aReject(aResult.error); + } + ); + }.bind(this)); + }, + + /** * Turns a chrome download object into a content accessible one. * When we have __DOM_IMPL__ available we just use that, otherwise * we run _create() with the wrapped js object. */ _prepareForContent: function(aChromeObject) { if (aChromeObject.__DOM_IMPL__) { return aChromeObject.__DOM_IMPL__; @@ -290,16 +351,21 @@ DOMDownloadImpl.prototype = { if (["downloading", "stopped", "succeeded", "finalized"].indexOf(aState) != -1) { this._state = aState; } }, + /** + * Initialize a DOMDownload instance for the given window using the + * 'jsonDownload' serialized format of the download encoded by + * DownloadsAPI.jsm. + */ _init: function(aWindow, aDownload) { this._window = aWindow; this.id = aDownload.id; this._update(aDownload); Services.obs.addObserver(this, "downloads-state-change-" + this.id, /* ownsWeak */ true); debug("observer set for " + this.id); }, @@ -309,22 +375,23 @@ DOMDownloadImpl.prototype = { */ _update: function(aDownload) { debug("update " + uneval(aDownload)); if (this.id != aDownload.id) { return; } let props = ["totalBytes", "currentBytes", "url", "path", "storageName", - "storagePath", "state", "contentType", "startTime"]; + "storagePath", "state", "contentType", "startTime", + "sourceAppManifestURL"]; let changed = false; let changedProps = {}; props.forEach((prop) => { - if (aDownload[prop] && (aDownload[prop] != this[prop])) { + if (prop in aDownload && (aDownload[prop] != this[prop])) { this[prop] = aDownload[prop]; changedProps[prop] = changed = true; } }); // When the path changes, we should update the storage name and // storage path used for our downloaded file in case our download // was re-targetted to a different storage and/or filename.
--- a/dom/downloads/DownloadsAPI.jsm +++ b/dom/downloads/DownloadsAPI.jsm @@ -8,21 +8,29 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; this.EXPORTED_SYMBOLS = []; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Downloads.jsm"); Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/osfile.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "ppmm", "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster"); +/** + * Parent process logic that services download API requests from the + * DownloadAPI.js instances in content processeses. The actual work of managing + * downloads is done by Toolkit's Downloads.jsm. This module is loaded by B2G's + * shell.js + */ + function debug(aStr) { #ifdef MOZ_DEBUG dump("-*- DownloadsAPI.jsm : " + aStr + "\n"); #endif } function sendPromiseMessage(aMm, aMessageName, aData, aError) { debug("sendPromiseMessage " + aMessageName); @@ -44,17 +52,18 @@ let DownloadsAPI = { this._ids = new WeakMap(); // Maps toolkit download objects to ids. this._index = {}; // Maps ids to downloads. ["Downloads:GetList", "Downloads:ClearAllDone", "Downloads:Remove", "Downloads:Pause", - "Downloads:Resume"].forEach((msgName) => { + "Downloads:Resume", + "Downloads:Adopt"].forEach((msgName) => { ppmm.addMessageListener(msgName, this); }); let self = this; Task.spawn(function () { let list = yield Downloads.getList(Downloads.ALL); yield list.addView(self); @@ -87,17 +96,19 @@ let DownloadsAPI = { */ jsonDownload: function(aDownload) { let res = { totalBytes: aDownload.totalBytes, currentBytes: aDownload.currentBytes, url: aDownload.source.url, path: aDownload.target.path, contentType: aDownload.contentType, - startTime: aDownload.startTime.getTime() + startTime: aDownload.startTime.getTime(), + sourceAppManifestURL: aDownload._unknownProperties && + aDownload._unknownProperties.sourceAppManifestURL }; if (aDownload.error) { res.error = aDownload.error; } res.id = this.downloadId(aDownload); @@ -160,16 +171,19 @@ let DownloadsAPI = { this.remove(aMessage.data, aMessage.target); break; case "Downloads:Pause": this.pause(aMessage.data, aMessage.target); break; case "Downloads:Resume": this.resume(aMessage.data, aMessage.target); break; + case "Downloads:Adopt": + this.adoptDownload(aMessage.data, aMessage.target); + break; default: debug("Invalid message: " + aMessage.name); } }, getList: function(aData, aMm) { debug("getList called!"); let self = this; @@ -181,27 +195,19 @@ let DownloadsAPI = { res.push(self.jsonDownload(aDownload)); }); aMm.sendAsyncMessage("Downloads:GetList:Return", res); }).then(null, Components.utils.reportError); }, clearAllDone: function(aData, aMm) { debug("clearAllDone called!"); - let self = this; Task.spawn(function () { let list = yield Downloads.getList(Downloads.ALL); - yield list.removeFinished(); - list = yield Downloads.getList(Downloads.ALL); - let downloads = yield list.getAll(); - let res = []; - downloads.forEach((aDownload) => { - res.push(self.jsonDownload(aDownload)); - }); - aMm.sendAsyncMessage("Downloads:ClearAllDone:Return", res); + list.removeFinished(); }).then(null, Components.utils.reportError); }, remove: function(aData, aMm) { debug("remove id " + aData.id); let download = this.getDownloadById(aData.id); if (!download) { sendPromiseMessage(aMm, "Downloads:Remove:Return", @@ -257,12 +263,103 @@ let DownloadsAPI = { function() { sendPromiseMessage(aMm, "Downloads:Resume:Return", aData); }, function() { sendPromiseMessage(aMm, "Downloads:Resume:Return", aData, "ResumeError"); } ); + }, + + /** + * Receive a download to adopt in the same representation we produce from + * our "jsonDownload" normalizer and add it to the list of downloads. + */ + adoptDownload: function(aData, aMm) { + let adoptJsonRep = aData.jsonDownload; + debug("adoptDownload " + uneval(adoptJsonRep)); + + Task.spawn(function* () { + // Verify that the file exists on disk. This will result in a rejection + // if the file does not exist. We will also use this information for the + // file size to avoid weird inconsistencies. We ignore the filesystem + // timestamp in favor of whatever the caller is telling us. + let fileInfo = yield OS.File.stat(adoptJsonRep.path); + + // We also require that the file is not a directory. + if (fileInfo.isDir) { + throw new Error("AdoptFileIsDirectory"); + } + + // We need to create a Download instance to add to the list. Create a + // serialized representation and then from there the instance. + let serializedRep = { + // explicit initializations in toSerializable + source: { + url: adoptJsonRep.url + // This is where isPrivate would go if adoption supported private + // browsing. + }, + target: { + path: adoptJsonRep.path, + }, + startTime: adoptJsonRep.startTime, + // kPlainSerializableDownloadProperties propagations + succeeded: true, // (all adopted downloads are required to be completed) + totalBytes: fileInfo.size, + contentType: adoptJsonRep.contentType, + // unknown properties added/used by the DownloadsAPI + currentBytes: fileInfo.size, + sourceAppManifestURL: adoptJsonRep.sourceAppManifestURL + }; + + let download = yield Downloads.createDownload(serializedRep); + + // The ALL list is a DownloadCombinedList instance that combines the + // PUBLIC (persisted to disk) and PRIVATE (ephemeral) download lists.. + // When we call add on it, it dispatches to the appropriate list based on + // the 'isPrivate' field of the source. (Which we don't initialize and + // defaults to false.) + let allDownloadList = yield Downloads.getList(Downloads.ALL); + + // This add will automatically notify all views of the added download, + // including DownloadsAPI instances and the DownloadAutoSaveView that's + // subscribed to the PUBLIC list and will save the download. + yield allDownloadList.add(download); + + debug("download adopted"); + // The notification above occurred synchronously, and so we will have + // already dispatched an added notification for our download to the child + // process in question. As such, we only need to relay the download id + // since the download will already have been cached. + return download; + }.bind(this)).then( + (download) => { + sendPromiseMessage(aMm, "Downloads:Adopt:Return", + { + id: this.downloadId(download), + promiseId: aData.promiseId + }); + }, + (ex) => { + let reportAs = "AdoptError"; + // Provide better error codes for expected errors. + if (ex instanceof OS.File.Error && ex.becauseNoSuchFile) { + reportAs = "AdoptNoSuchFile"; + } else if (ex.message === "AdoptFileIsDirectory") { + reportAs = ex.message; + } else { + // Anything else is unexpected and should be reported to help track + // down what's going wrong. + debug("unexpected download error: " + ex); + Cu.reportError(ex); + } + sendPromiseMessage(aMm, "Downloads:Adopt:Return", + { + promiseId: aData.promiseId + }, + reportAs); + }); } }; DownloadsAPI.init();
--- a/dom/downloads/DownloadsIPC.jsm +++ b/dom/downloads/DownloadsIPC.jsm @@ -31,35 +31,34 @@ function debug(aStr) { dump("-*- DownloadsIPC.jsm : " + aStr + "\n"); #endif } const ipcMessages = ["Downloads:Added", "Downloads:Removed", "Downloads:Changed", "Downloads:GetList:Return", - "Downloads:ClearAllDone:Return", "Downloads:Remove:Return", "Downloads:Pause:Return", - "Downloads:Resume:Return"]; + "Downloads:Resume:Return", + "Downloads:Adopt:Return"]; this.DownloadsIPC = { downloads: {}, init: function() { debug("init"); Services.obs.addObserver(this, "xpcom-shutdown", false); ipcMessages.forEach((aMessage) => { cpmm.addMessageListener(aMessage, this); }); // We need to get the list of current downloads. this.ready = false; this.getListPromises = []; - this.clearAllPromises = []; this.downloadPromises = {}; cpmm.sendAsyncMessage("Downloads:GetList", {}); this._promiseId = 0; }, notifyChanges: function(aId) { // TODO: use the subject instead of stringifying. if (this.downloads[aId]) { @@ -88,22 +87,16 @@ this.DownloadsIPC = { if (!this.ready) { this.getListPromises.forEach(aPromise => aPromise.resolve(this.downloads)); this.getListPromises.length = 0; } this.ready = true; break; - case "Downloads:ClearAllDone:Return": - this._updateDownloadsArray(download); - this.clearAllPromises.forEach(aPromise => - aPromise.resolve(this.downloads)); - this.clearAllPromises.length = 0; - break; case "Downloads:Added": this.downloads[download.id] = download; this.notifyChanges(download.id); break; case "Downloads:Removed": if (this.downloads[download.id]) { this.downloads[download.id] = download; this.notifyChanges(download.id); @@ -134,16 +127,17 @@ this.DownloadsIPC = { if (changed) { this.notifyChanges(download.id); } break; case "Downloads:Remove:Return": case "Downloads:Pause:Return": case "Downloads:Resume:Return": + case "Downloads:Adopt:Return": if (this.downloadPromises[download.promiseId]) { if (!download.error) { this.downloadPromises[download.promiseId].resolve(download); } else { this.downloadPromises[download.promiseId].reject(download); } delete this.downloadPromises[download.promiseId]; } @@ -162,24 +156,21 @@ this.DownloadsIPC = { deferred.resolve(this.downloads); } else { this.getListPromises.push(deferred); } return deferred.promise; }, /** - * Returns a promise that is resolved with the list of current downloads. - */ + * Void function to trigger removal of completed downloads. + */ clearAllDone: function() { debug("clearAllDone"); - let deferred = Promise.defer(); - this.clearAllPromises.push(deferred); cpmm.sendAsyncMessage("Downloads:ClearAllDone", {}); - return deferred.promise; }, promiseId: function() { return this._promiseId++; }, remove: function(aId) { debug("remove " + aId); @@ -206,16 +197,26 @@ this.DownloadsIPC = { let deferred = Promise.defer(); let pId = this.promiseId(); this.downloadPromises[pId] = deferred; cpmm.sendAsyncMessage("Downloads:Resume", { id: aId, promiseId: pId }); return deferred.promise; }, + adoptDownload: function(aJsonDownload) { + debug("adoptDownload"); + let deferred = Promise.defer(); + let pId = this.promiseId(); + this.downloadPromises[pId] = deferred; + cpmm.sendAsyncMessage("Downloads:Adopt", + { jsonDownload: aJsonDownload, promiseId: pId }); + return deferred.promise; + }, + observe: function(aSubject, aTopic, aData) { if (aTopic == "xpcom-shutdown") { ipcMessages.forEach((aMessage) => { cpmm.removeMessageListener(aMessage, this); }); } } };
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/clear_all_done_helper.js @@ -0,0 +1,67 @@ +/** + * A helper to clear out the existing downloads known to the mozDownloadManager + * / downloads.js. + * + * It exists because previously mozDownloadManager.clearAllDone() thought that + * when it returned that all the completed downloads would be cleared out. It + * was wrong and this led to various intermittent test failurse. In discussion + * on https://bugzil.la/979446#c13 and onwards, it was decided that + * clearAllDone() was in the wrong and that the jsdownloads API it depends on + * was not going to change to make it be in the right. + * + * The existing uses of clearAllDone() in tests seemed to be about: + * - Exploding if there was somehow still a download in progress + * - Clearing out the download list at the start of a test so that calls to + * getDownloads() wouldn't have to worry about existing downloads, etc. + * + * From discussion, the right way to handle clearing is to wait for the expected + * removal events to occur for the existing downloads. So that's what we do. + * We still generate a test failure if there are any in-progress downloads. + * + * @param {Boolean} [getDownloads=false] + * If true, invoke getDownloads after clearing the download list and return + * its value. + */ +function clearAllDoneHelper(getDownloads) { + var clearedPromise = new Promise(function(resolve, reject) { + function gotDownloads(downloads) { + // If there are no downloads, we're already done. + if (downloads.length === 0) { + resolve(); + return; + } + + // Track the set of expected downloads that will be finalized. + var expectedIds = new Set(); + function changeHandler(evt) { + var download = evt.download; + if (download.state === "finalized") { + expectedIds.delete(download.id); + if (expectedIds.size === 0) { + resolve(); + } + } + } + downloads.forEach(function(download) { + if (download.state === "downloading") { + ok(false, "A download is still active: " + download.path); + reject("Active download"); + } + download.onstatechange = changeHandler; + expectedIds.add(download.id); + }); + navigator.mozDownloadManager.clearAllDone(); + } + function gotBadNews(err) { + ok(false, "Problem clearing all downloads: " + err); + reject(err); + } + navigator.mozDownloadManager.getDownloads().then(gotDownloads, gotBadNews); + }); + if (!getDownloads) { + return clearedPromise; + } + return clearedPromise.then(function() { + return navigator.mozDownloadManager.getDownloads(); + }); +}
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/common_app.js @@ -0,0 +1,19 @@ +function is(a, b, msg) { + alert((a === b ? 'OK' : 'KO') + ' ' + a + ' should equal ' + b + ': ' + msg); +} + +function ok(a, msg) { + alert((a ? 'OK' : 'KO')+ ' ' + msg); +} + +function info(msg) { + alert('INFO ' + msg); +} + +function cbError() { + alert('KO error'); +} + +function finish() { + alert('DONE'); +}
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/file_app.sjs @@ -0,0 +1,55 @@ +var gBasePath = "tests/dom/downloads/tests/"; +var gTemplate = "file_app.template.webapp"; + +function handleRequest(request, response) { + var query = getQuery(request); + + var testToken = query.testToken || ''; + var appType = query.appType || 'web'; + + var template = gBasePath + gTemplate; + response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); + var body = readTemplate(template) + .replace(/TESTTOKEN/g, testToken) + .replace(/APPTYPE/g, appType); + response.write(); +} + +// Copy-pasted incantations. There ought to be a better way to synchronously read +// a file into a string, but I guess we're trying to discourage that. +function readTemplate(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + var split = path.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + cis.init(fis, "UTF-8", 0, 0); + + var data = ""; + let (str = {}) { + let read = 0; + do { + read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value + data += str.value; + } while (read != 0); + } + cis.close(); + return data; +} + +function getQuery(request) { + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + return query; +} +
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/file_app.template.webapp @@ -0,0 +1,7 @@ +{ + "name": "Really Rapid Release (hosted)", + "description": "Updated even faster than <a href='http://mozilla.org'>Firefox</a>, just to annoy slashdotters.", + "type": "APPTYPE", + "launch_path": "/tests/dom/downloads/tests/TESTTOKEN", + "icons": { "128": "default_icon" } +}
--- a/dom/downloads/tests/mochitest.ini +++ b/dom/downloads/tests/mochitest.ini @@ -1,12 +1,24 @@ [DEFAULT] -skip-if = buildapp == 'mulet' || buildapp == 'b2g' # bug 979446, frequent failures +# The actual requirement for mozDownloadManager is MOZ_GONK because of +# the nsIVolumeService dependency. Until https://bugzil.la/1130264 is +# addressed, there is no way for mulet to run these tests. +run-if = toolkit == 'gonk' support-files = serve_file.sjs + clear_all_done_helper.js + file_app.template.webapp + file_app.sjs + common_app.js + shim_app_as_test.js + shim_app_as_test_chrome.js + testapp_downloads_adopt_download.html + testapp_downloads_adopt_download.js + testapp_downloads_adopt_download.manifest [test_downloads_navigator_object.html] [test_downloads_basic.html] [test_downloads_large.html] +[test_downloads_adopt_download.html] [test_downloads_bad_file.html] [test_downloads_pause_remove.html] [test_downloads_pause_resume.html] -skip-if = toolkit=='gonk' # b2g(bug 947167) b2g-debug(bug 947167)
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/shim_app_as_test.js @@ -0,0 +1,210 @@ +/** + * Support logic to run a test file as an installed app. This file is derived + * from dom/requestsync/tests/test_basic_app.html but uses + * DOMApplicationRegistry in a chrome script (shim_app_as_test_chrome.js) to + * directly install the apps instead of mozApps.install because mozApps.install + * can't install privileged/certified apps. (This is the same mechanism used by + * the Firefox OS Gaia email app's backend test runner.) + * + * You really only want to do this if your test cares about the app's origin + * or you REALLY want to double-check AvailableIn and other WebIDL-provided + * security mechanisms. + * + * If you trust WebIDL, your life may be made significantly easier by just + * setting the pref "dom.ignore_webidl_scope_checks" to true, which makes + * BindingUtils.cpp's IsInPrivilegedApp and IsInCertifiedApp return true no + * matter what *on the main thread*. You are potentially out of luck on + * workers since at the time of writing this since the values stored on + * WorkerPrivateParent are based on the app status and ignore the pref. + * + * TO USE THIS: + * + * Make sure you have the usual header boilerplate: + * <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + * <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + * + * You also want to add this file! + * <script type="application/javascript" src="shim_app_as_test.js"></script> + * + * In your script body, issue a call like so: + * runAppTest({ + * appFile: 'testapp_downloads_adopt_download.html', + * appManifest: 'testapp_downloads_adopt_download.manifest', + * appType: 'certified', + * extraPrefs: { + * set: [["dom.mozDownloads.enabled", true]] + * } + * }); + * + * You shouldn't be adding other stuff to that file. Instead, you want + * everything in your testapp_*.html file. And you probably just want to copy + * and paste from an existing one of those... + */ + + var gManifestURL; + var gApp; + var gOptions; + + // Load the chrome script. + var gChromeHelper = SpecialPowers.loadChromeScript( + SimpleTest.getTestFileURL('shim_app_as_test_chrome.js')); + + function installApp() { + info("installing app"); + var useOrigin = document.location.origin; + gChromeHelper.sendAsyncMessage( + 'install', + { + origin: useOrigin, + manifestURL: SimpleTest.getTestFileURL(gOptions.appManifest), + }); + } + + function installedApp(appInfo) { + gApp = appInfo; + ok(!!appInfo, 'installed app'); + runTests(); + } + gChromeHelper.addMessageListener('installed', installedApp); + + function uninstallApp() { + info('uninstalling app'); + gChromeHelper.sendAsyncMessage('uninstall', gApp); + } + + function uninstalledApp(success) { + ok(success, 'uninstalled app'); + runTests(); + } + gChromeHelper.addMessageListener('uninstalled', uninstalledApp); + + function testApp() { + var cleanupFrame; + var handleTestMessage = function(message) { + if (/^OK/.exec(message)) { + ok(true, "Message from app: " + message); + } else if (/^KO/.exec(message)) { + ok(false, "Message from app: " + message); + } else if (/^INFO/.exec(message)) { + info("Message from app: " + message.substring(5)); + } else if (/^DONE$/.exec(message)) { + ok(true, "Messaging from app complete"); + cleanupFrame(); + runTests(); + } + }; + + // Bug 1097479 means that embed-webapps does not work if you are already + // OOP, as we are for b2g. So we need to have the chrome script run our + // app in a sibling iframe to the one we're living in. When that bug is + // fixed or we are run in a non-b2g context, we can set this value to false + // or otherwise conditionalize based on behaviour. + var needSiblingIframeHack = true; + + if (needSiblingIframeHack) { + gChromeHelper.sendAsyncMessage('run', gApp); + + gChromeHelper.addMessageListener('appMessage', handleTestMessage); + gChromeHelper.addMessageListener('appError', function(data) { + ok(false, "Error in app frame: " + data.message); + }); + + cleanupFrame = function() { + gChromeHelper.sendAsyncMessage('close', {}); + }; + } else { + var ifr = document.createElement('iframe'); + ifr.setAttribute('mozbrowser', 'true'); + ifr.setAttribute('mozapp', gApp.manifestURL); + + cleanupFrame = function() { + ifr.removeEventListener('mozbrowsershowmodalprompt', listener); + domParent.removeChild(ifr); + }; + + // Set us up to listen for messages from the app. + var listener = function(e) { + var message = e.detail.message; // e.detail.message; + handleTestMessage(message); + }; + + // This event is triggered when the app calls "alert". + ifr.addEventListener('mozbrowsershowmodalprompt', listener, false); + ifr.addEventListener('mozbrowsererror', function(evt) { + ok(false, "Error in app frame: " + evt.detail); + }); + + ifr.setAttribute('src', gApp.manifest.launch_path); + var domParent = document.getElementById('content'); + if (!domParent) { + document.createElement('div'); + document.body.insertBefore(domParent, document.body.firstChild); + } + domParent.appendChild(ifr); + } + } + + var tests = [ + // Permissions + function() { + info("pushing permissions"); + SpecialPowers.pushPermissions( + [{ "type": "browser", "allow": 1, "context": document }, + { "type": "embed-apps", "allow": 1, "context": document }, + { "type": "webapps-manage", "allow": 1, "context": document } + ], + runTests); + }, + + // Preferences + function() { + info("pushing preferences: " + gOptions.extraPrefs.set); + SpecialPowers.pushPrefEnv({ + "set": gOptions.extraPrefs.set + }, runTests); + }, + + function() { + info("enabling use of mozbrowser"); + //SpecialPowers.setAllAppsLaunchable(true); + SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); + runTests(); + }, + + // No confirmation needed when an app is installed + function() { + SpecialPowers.autoConfirmAppInstall(function() { + SpecialPowers.autoConfirmAppUninstall(runTests); + }); + }, + + // Installing the app + installApp, + + // Run tests in app + testApp, + + // Uninstall the app + uninstallApp, + ]; + + function runTests() { + if (!tests.length) { + ok(true, 'DONE!'); + SimpleTest.finish(); + return; + } + + var test = tests.shift(); + test(); + } + + SimpleTest.waitForExplicitFinish(); + + function runAppTest(options) { + gOptions = options; + var href = document.location.href; + gManifestURL = href.substring(0, href.lastIndexOf('/') + 1) + + options.appManifest; + runTests(); + }
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/shim_app_as_test_chrome.js @@ -0,0 +1,178 @@ +/** + * This is the chrome helper for shim_app_as_test.js. Its load is triggered by + * shim_app_as_test.js by a call to SpecialPowers.loadChromeScript and runs + * in the parent process in a sandbox created with the system principal. (Which + * seems like it can never get collected because it's reachable via the + * apparently singleton SpecialPowersObserverAPI instance and there's no logic + * to support reaping. Wuh-oh.) + * + * It exists to help install fake privileged/certified applications. It needs + * to exist because: + * - We need to poke at DOMApplicationRegistry directly. + * - By using SpecialPowers.loadChromeScript we are able to ensure this file + * is run in the parent process. This is important because + * DOMApplicationRegistry only lives in the parent process! + * - By running entirely in a chrome privileged compartment, we avoid crazy + * wrapper problems that we would otherwise face with our shenanigans of + * directly meddling with DOMApplicationRegistry. (And hopefully save + * anyone changing DOMApplicationRegistry from frustration/hating us if + * things were just barely working.) + * - Bug 1097479 means that embed-webapps doesn't work when the content process + * that is telling us to do things is itself OOP. So it falls upon us to + * handle the running of the app by creating a sibling mozbrowser/mozapp + * iframe to the one running the mochitests. + * + * Note that in this file we try to do *only* those things that can't otherwise + * be cleanly done using SpecialPowers. + * + * Want to better understand our execution context? Check out + * SpecialPowersObserverAPI.js and search on SPLoadChromeScript. + */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const CC = Components.Constructor; + +Cu.import('resource://gre/modules/Webapps.jsm'); // for DOMApplicationRegistry +Cu.import('resource://gre/modules/AppsUtils.jsm'); // for AppUtils +Cu.import('resource://gre/modules/Services.jsm'); // for AppUtils + +// Yes, you would think there was something like this already exposed easily +// in a JSM somewhere. No. +function fetchManifest(manifestURL) { + return new Promise(function(resolve, reject) { + let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Ci.nsIXMLHttpRequest); + xhr.open("GET", manifestURL, true); + xhr.responseType = "json"; + + xhr.addEventListener("load", function() { + if (xhr.status == 200) { + resolve(xhr.response); + } else { + reject(); + } + }); + + xhr.addEventListener("error", function() { + reject(); + }); + + xhr.send(null); + }); +} + +/** + * Install an app using confirmInstall using pre-chewed data. This avoids the + * check in the normal installApp flow that gets all judgemental about the + * installation of privileged and certified apps. + */ +function installApp(req) { + fetchManifest(req.manifestURL).then(function(manifestObj) { + var data = { + // cloneAppObj normalizes the representation for us + app: AppsUtils.cloneAppObject({ + installOrigin: req.origin, + origin: req.origin, + manifestURL: req.manifestURL, + appStatus: AppsUtils.getAppManifestStatus(manifestObj), + receipts: [], + categories: [] + }), + + from: req.origin, // unused? + oid: 0, // unused? + requestID: 0, // unused-ish + appId: 0, // unused + isBrowser: false, + isPackage: false, // used + // magic to auto-ack... don't think we care about this... + forceSuccessAck: false + // stuff that probably doesn't matter: 'mm', 'apkInstall', + }; + // cloneAppObject does not propagate the manifest + data.app.manifest = manifestObj; + + return DOMApplicationRegistry.confirmInstall(data).then( + function() { + var appId = + DOMApplicationRegistry.getAppLocalIdByManifestURL(req.manifestURL); + // act like this is a privileged app having all of its permissions + // authorized at first run. + DOMApplicationRegistry.updatePermissionsForApp( + appId, + /* preinstalled */ true, + /* system update? */ true); + + sendAsyncMessage( + 'installed', + { + appId: appId, + manifestURL: req.manifestURL, + manifest: manifestObj + }); + }, + function(err) { + sendAsyncMessage('installed', false); + }); + }); +} + +function uninstallApp(appInfo) { + DOMApplicationRegistry.uninstall(appInfo.manifestURL).then( + function() { + sendAsyncMessage('uninstalled', true); + }, + function() { + sendAsyncMessage('uninstalled', false); + }); +} + +var activeIframe = null; + +/** + * Run our app in a sibling mozbrowser/mozapp iframe to the mochitest iframe. + * This is needed because we can't nest mozbrowser/mozapp iframes inside our + * already-OOP iframe until bug 1097479 is resolved. + */ +function runApp(appInfo) { + let shellDomWindow = Services.wm.getMostRecentWindow('navigator:browser'); + let sysAppFrame = shellDomWindow.document.body.querySelector('#systemapp'); + let sysAppDoc = sysAppFrame.contentDocument; + + let siblingFrame = sysAppDoc.body.querySelector('#test-container'); + + let ifr = activeIframe = sysAppDoc.createElement('iframe'); + ifr.setAttribute('mozbrowser', 'true'); + ifr.setAttribute('remote', 'true'); + ifr.setAttribute('mozapp', appInfo.manifestURL); + + ifr.addEventListener('mozbrowsershowmodalprompt', function(evt) { + var message = evt.detail.message; + // only send the message as long as we haven't been told to clean up. + if (activeIframe) { + sendAsyncMessage('appMessage', message); + } + }, false); + ifr.addEventListener('mozbrowsererror', function(evt) { + if (activeIframe) { + sendAsyncMessage('appError', { message: '' + evt.detail }); + } + }); + + ifr.setAttribute('src', appInfo.manifest.launch_path); + siblingFrame.parentElement.appendChild(ifr); +} + +function closeApp() { + if (activeIframe) { + activeIframe.parentElement.removeChild(activeIframe); + activeIframe = null; + } +} + +addMessageListener('install', installApp); +addMessageListener('uninstall', uninstallApp); +addMessageListener('run', runApp); +addMessageListener('close', closeApp);
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/test_downloads_adopt_download.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=825318 +--> +<head> + <title>Test for Bug 825318 mozDownloadManager.adoptDownload</title> + <script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.7" src="shim_app_as_test.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> + +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=825318">Mozilla Bug 825318</a> +<p id="display"></p> +<div id="content"> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript;version=1.7"> + +runAppTest({ + appFile: 'testapp_downloads_adopt_download.html', + appManifest: 'testapp_downloads_adopt_download.manifest', + appType: 'certified', + extraPrefs: { + set: [["dom.mozDownloads.enabled", true]] + } +}); + +</script> +</pre> +</body> +</html> +
--- a/dom/downloads/tests/test_downloads_basic.html +++ b/dom/downloads/tests/test_downloads_basic.html @@ -67,29 +67,34 @@ function downloadChange(evt) { var download = evt.download; checkConsistentDownloadAttributes(download); is(download.totalBytes, 1024, "Download total size is 1024 bytes"); if (download.state === "succeeded") { is(download.currentBytes, 1024, "Download current size is 1024 bytes"); SimpleTest.finish(); } else if (download.state === "downloading") { + // Note that this case may or may not trigger, depending on whether the + // download is initially reported with 0 bytes (we should happen) or with + // 1024 bytes (we should not happen). If we do happen, an additional 8 + // TEST-PASS events should be logged. ok(download.currentBytes > lastKnownCurrentBytes, "Download current size is larger than last download change event"); lastKnownCurrentBytes = download.currentBytes; } else { ok(false, "Unexpected download state = " + download.state); } } function downloadStart(evt) { var download = evt.download; checkConsistentDownloadAttributes(download); - is(download.currentBytes, 0, "Download current size is zero"); + // We used to check that the currentBytes was 0. This was incorrect. It + // is very common to first hear about the download already at 1024 bytes. is(download.state, "downloading", "Download state is downloading"); download.onstatechange = downloadChange; } var steps = [ // Start by setting the pref to true. function() {
--- a/dom/downloads/tests/test_downloads_large.html +++ b/dom/downloads/tests/test_downloads_large.html @@ -2,16 +2,17 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=938023 --> <head> <title>Test for Bug 938023 Downloads API</title> <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="clear_all_done_helper.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a> <p id="display"></p> <div id="content" style="display: none"> </div> @@ -41,17 +42,17 @@ function next(args) { // Catch all error function. function error() { ok(false, "API failure"); SimpleTest.finish(); } function getDownloads(downloads) { ok(downloads.length == 1, "One downloads after getDownloads"); - navigator.mozDownloadManager.clearAllDone().then(clearAllDone, error); + clearAllDoneHelper(true).then(clearAllDone, error); } function clearAllDone(downloads) { ok(downloads.length == 0, "No downloads after clearAllDone"); SimpleTest.finish(); } function downloadChange(evt) { @@ -71,17 +72,17 @@ var steps = [ }, next); }, // Setup permission and clear current list. function() { SpecialPowers.pushPermissions([ {type: "downloads", allow: true, context: document} ], function() { - navigator.mozDownloadManager.clearAllDone().then(next, error); + clearAllDoneHelper(true).then(next, error); }); }, function(downloads) { ok(downloads.length == 0, "Start with an empty download list."); next(); },
--- a/dom/downloads/tests/test_downloads_pause_remove.html +++ b/dom/downloads/tests/test_downloads_pause_remove.html @@ -2,16 +2,17 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=938023 --> <head> <title>Test for Bug 938023 Downloads API</title> <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="clear_all_done_helper.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a> <p id="display"></p> <div id="content" style="display: none"> </div> @@ -78,17 +79,17 @@ var steps = [ }, next); }, // Setup permission and clear current list. function() { SpecialPowers.pushPermissions([ {type: "downloads", allow: true, context: document} ], function() { - navigator.mozDownloadManager.clearAllDone().then(next, error); + clearAllDoneHelper(true).then(next, error); }); }, function(downloads) { ok(downloads.length == 0, "Start with an empty download list."); next(); },
--- a/dom/downloads/tests/test_downloads_pause_resume.html +++ b/dom/downloads/tests/test_downloads_pause_resume.html @@ -2,16 +2,17 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=938023 --> <head> <title>Test for Bug 938023 Downloads API</title> <script type="text/javascript" src="/MochiKit/MochiKit.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="clear_all_done_helper.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=938023">Mozilla Bug 938023</a> <p id="display"></p> <div id="content" style="display: none"> </div> @@ -49,18 +50,17 @@ function error() { function checkDownloadList(downloads) { ok(downloads.length == 0, "No downloads left"); SimpleTest.finish(); } function checkResumeSucceeded(download) { ok(download.state == "succeeded", "Download resumed successfully."); - navigator.mozDownloadManager.clearAllDone() - .then(checkDownloadList, error); + clearAllDoneHelper(true).then(checkDownloadList, error); } function downloadChange(evt) { var download = evt.download; if (download.state == "downloading" && !pausing) { pausing = true; download.pause(); @@ -80,17 +80,17 @@ var steps = [ }, next); }, // Setup permission and clear current list. function() { SpecialPowers.pushPermissions([ {type: "downloads", allow: true, context: document} ], function() { - navigator.mozDownloadManager.clearAllDone().then(next, error); + clearAllDoneHelper(true).then(next, error); }); }, function(downloads) { ok(downloads.length == 0, "Start with an empty download list."); next(); },
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/testapp_downloads_adopt_download.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> +<head> + <script type="application/javascript" src="common_app.js"></script> + <meta charset="utf-8"> +</head> +<body> +<div id="blah">initial text</div> +<pre id="test"> +<!-- because of certified CSP, this code must NOT be inline --> +<script class="testbody" type="text/javascript;version=1.7" src="testapp_downloads_adopt_download.js"></script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/testapp_downloads_adopt_download.js @@ -0,0 +1,218 @@ +/** + * Test the adoptDownload API. Specifically, we expect that when we call + * adoptDownload with a valid payload that: + * - The method will be resolved with a valid, fully populated DOMDownload + * instance, including an id. + * - An ondownloadstart notification will be generated and the DOMDownload + * instance it receives will be logically equivalent. + * + * We also explicitly verify that invalid adoptDownload payloads result in a + * rejection and that no download is added. + * + * This test explicitly does not test that the download is correctly persisted + * to the database. This is done because Downloads.jsm does not provide a means + * of safely restarting itself, so Firefox would need to be restarted. Because + * the adoptDownload code is using the Downloads API in a straightforward + * manner, it's not considered likely this would regress, and certainly not + * considered worth the automated testing overhead of a restart. + */ + +function checkInvalidResult(dict, expectedErr, explanation) { + navigator.mozDownloadManager.ondownloadstart = function() { + ok(false, "No download should have been added!"); + }; + navigator.mozDownloadManager.adoptDownload(dict).then( + function() { + ok(false, "Invalid adoptDownload did not reject!"); + runTests(); + }, + function(rejectedWith) { + is(rejectedWith, expectedErr, explanation + " rejection value"); + runTests(); + }); +} + +// Pick a date that Date.now() could not possibly return by picking a date in +// the past. (We want to make sure the date we provide works.) +var arbitraryDate = new Date(Date.now() - 60000); + +var blobContents = new Uint8Array(256); +var memBlob = new Blob([blobContents], { type: 'application/octet-stream' }); +var blobStorageName; +var blobStoragePath = 'blobby.blob'; + +function checkAdoptedDownload(download, validPayload) { + is(download.totalBytes, memBlob.size, 'size'); + is(download.url, validPayload.url, 'url'); + // The filesystem path is not practical to check since we can't hard-code it + // and the only way to check is to effectively duplicate the logic in + // DownloadsAPI.js. The good news, however, is that the value is + // round-tripped from storageName/storagePath to path and back again, and we + // also verify the file exists on disk, so we can be reasonably confident this + // is correct. We output it to aid in debugging if things should break, + // of course. + info('path (not checked): ' + download.path); + is(download.storageName, validPayload.storageName, 'storageName'); + is(download.storagePath, validPayload.storagePath, 'storagePath'); + is(download.state, 'succeeded', 'state'); + is(download.contentType, validPayload.contentType, 'contentType'); + is(download.startTime.valueOf(), arbitraryDate.valueOf(), 'startTime'); + is(download.sourceAppManifestURL, + 'http://mochi.test:8888/' + + 'tests/dom/downloads/tests/testapp_downloads_adopt_download.manifest', + 'app manifest'); +}; + +var tests = [ + function saveBlobToDeviceStorage() { + // Only sdcard can handle arbitrary MIME types and is guaranteed to be a + // thing. + var storage = navigator.getDeviceStorage('sdcard'); + // We used the non-array helper, so the name we get may be different than + // what we asked for. + blobStorageName = storage.storageName; + ok(!!storage, 'have storage'); + var req = storage.addNamed(memBlob, blobStoragePath); + req.onerror = function() { + ok(false, 'problem saving blob to storage: ' + req.error.name); + }; + req.onsuccess = function(evt) { + ok(true, 'saved blob: ' + evt.target.result); + runTests(); + }; + }, + function addValid() { + var validPayload = { + // All currently expected consumers are unable to provide a valid URL, and + // as a result need to provide an empty string. + url: "", + storageName: blobStorageName, + storagePath: blobStoragePath, + contentType: memBlob.type, + startTime: arbitraryDate + }; + // Wrap the notification in a check so we can force our logic to be + // consistently ordered in the test even if it's not in reality. + var notifiedPromise = new Promise(function(resolve, reject) { + navigator.mozDownloadManager.ondownloadstart = function(evt) { + resolve(evt.download); + }; + }); + + // Start the download + navigator.mozDownloadManager.adoptDownload(validPayload).then( + function(apiDownload) { + checkAdoptedDownload(apiDownload, validPayload); + ok(!!apiDownload.id, "Need a download id!"); + notifiedPromise.then(function(notifiedDownload) { + checkAdoptedDownload(notifiedDownload, validPayload); + is(apiDownload.id, notifiedDownload.id, + "Notification should be for the download we adopted"); + runTests(); + }); + }, + function() { + ok(false, "adoptDownload should not have rejected"); + runTests(); + }); + }, + + function dictionaryNotProvided() { + checkInvalidResult(undefined, "InvalidDownload"); + }, + // Missing fields immediately result in rejection with InvalidDownload + function missingStorageName() { + checkInvalidResult({ + url: "", + // no storageName + storagePath: "relpath/filename.txt", + contentType: "text/plain", + startTime: arbitraryDate + }, "InvalidDownload", "missing storage name"); + }, + function nullStorageName() { + checkInvalidResult({ + url: "", + storageName: null, + storagePath: "relpath/filename.txt", + contentType: "text/plain", + startTime: arbitraryDate + }, "InvalidDownload", "null storage name"); + }, + function missingStoragePath() { + checkInvalidResult({ + url: "", + storageName: blobStorageName, + // no storagePath + contentType: "text/plain", + startTime: arbitraryDate + }, "InvalidDownload", "missing storage path"); + }, + function nullStoragePath() { + checkInvalidResult({ + url: "", + storageName: blobStorageName, + storagePath: null, + contentType: "text/plain", + startTime: arbitraryDate + }, "InvalidDownload", "null storage path"); + }, + function missingContentType() { + checkInvalidResult({ + url: "", + storageName: "sdcard", + storagePath: "relpath/filename.txt", + // no contentType + startTime: arbitraryDate + }, "InvalidDownload", "missing content type"); + }, + function nullContentType() { + checkInvalidResult({ + url: "", + storageName: "sdcard", + storagePath: "relpath/filename.txt", + contentType: null, + startTime: arbitraryDate + }, "InvalidDownload", "null content type"); + }, + // Incorrect storage names are likewise immediately invalidated + function invalidStorageName() { + checkInvalidResult({ + url: "", + storageName: "ALMOST CERTAINLY DOES NOT EXIST", + storagePath: "relpath/filename.txt", + contentType: "text/plain", + startTime: arbitraryDate + }, "InvalidDownload", "invalid storage name"); + }, + // The existence of the file is validated in the parent process + function legitStorageInvalidPath() { + checkInvalidResult({ + url: "", + storageName: blobStorageName, + storagePath: "ALMOST CERTAINLY DOES NOT EXIST", + contentType: "text/plain", + startTime: arbitraryDate + }, "AdoptNoSuchFile", "invalid path"); + }, + function allDone() { + // Just in case, make sure no other mochitest could mess with us after we've + // finished. + navigator.mozDownloadManager.ondownloadstart = null; + runTests(); + } +]; + +function runTests() { + if (!tests.length) { + finish(); + return; + } + + var test = tests.shift(); + if (test.name) { + info('starting test: ' + test.name); + } + test(); +} +runTests();
new file mode 100644 --- /dev/null +++ b/dom/downloads/tests/testapp_downloads_adopt_download.manifest @@ -0,0 +1,10 @@ +{ + "name": "Downloads certified test fake app", + "description": "Test", + "launch_path": "http://mochi.test:8888/tests/dom/downloads/tests/testapp_downloads_adopt_download.html", + "type": "certified", + "permissions": { + "device-storage:sdcard":{ "access": "readcreate" }, + "downloads": {} + } +}
--- a/dom/webidl/Downloads.webidl +++ b/dom/webidl/Downloads.webidl @@ -31,20 +31,42 @@ interface DOMDownloadManager : EventTarg // download objects. Promise<sequence<DOMDownload>> getDownloads(); // Removes one download from the downloads set. Returns a promise resolved // with the finalized download. [UnsafeInPrerendering] Promise<DOMDownload> remove(DOMDownload download); - // Removes all the completed downloads from the set. Returns an - // array of the completed downloads that were removed. + // Removes all completed downloads. This kicks off an asynchronous process + // that will eventually complete, but will not have completed by the time this + // method returns. If you care about the side-effects of this method, know + // that each existing download will have its onstatechange method invoked and + // will have a new state of "finalized". (After the download is finalized, no + // further events will be generated on it.) [UnsafeInPrerendering] - Promise<sequence<DOMDownload>> clearAllDone(); + void clearAllDone(); + + // Add completed downloads from applications that must perform the download + // process themselves. For example, email. The method is resolved with a + // fully populated DOMDownload instance on success, or rejected in the + // event all required options were not provided. + // + // The adopted download will also be reported via the ondownloadstart event + // handler. + // + // Applications must currently be certified to use this, but it could be + // widened at a later time. + // + // Note that "download" is not actually optional, but WebIDL requires that it + // be marked as such because it is not followed by a required argument. The + // promise will be rejected if the dictionary is omitted or the specified + // file does not exist on disk. + [AvailableIn=CertifiedApps] + Promise<DOMDownload> adoptDownload(optional AdoptDownloadDict download); // Fires when a new download starts. attribute EventHandler ondownloadstart; }; [JSImplementation="@mozilla.org/downloads/download;1", Pref="dom.mozDownloads.enabled", CheckPermissions="downloads"] @@ -54,39 +76,48 @@ interface DOMDownload : EventTarget { // The number of bytes that we have currently downloaded. readonly attribute long long currentBytes; // The url of the resource. readonly attribute DOMString url; // The full path in local storage where the file will end up once the download - // is complete. + // is complete. This is equivalent to the concatenation of the 'storagePath' + // to the 'mountPoint' of the nsIVolume associated with the 'storageName' + // (with delimiter). readonly attribute DOMString path; // The DeviceStorage volume name on which the file is being downloaded. readonly attribute DOMString storageName; // The DeviceStorage path on the volume with 'storageName' of the file being // downloaded. readonly attribute DOMString storagePath; - // The state of the download. + // The state of the download. One of: downloading, stopped, succeeded, or + // finalized. A finalized download is a download that has been removed / + // cleared and is no longer tracked by the download manager and will not + // receive any further onstatechange updates. readonly attribute DownloadState state; // The mime type for this resource. readonly attribute DOMString contentType; // The timestamp this download started. readonly attribute Date startTime; // An opaque identifier for this download. All instances of the same // download (eg. in different windows) will have the same id. readonly attribute DOMString id; + // The manifestURL of the application that added this download. Only used for + // downloads added via the adoptDownload API call. + readonly attribute DOMString? sourceAppManifestURL; + // A DOM error object, that will be not null when a download is stopped // because something failed. readonly attribute DOMError? error; // Pauses the download. [UnsafeInPrerendering] Promise<DOMDownload> pause(); @@ -95,8 +126,46 @@ interface DOMDownload : EventTarget { [UnsafeInPrerendering] Promise<DOMDownload> resume(); // This event is triggered anytime a property of the object changes: // - when the transfer progresses, updating currentBytes. // - when the state and/or error attributes change. attribute EventHandler onstatechange; }; + +// Used to initialize the DOMDownload object for adopted downloads. +// fields directly maps to the DOMDownload fields. +dictionary AdoptDownloadDict { + // The URL of this resource if there is one available. An empty string if + // the download is not accessible via URL. An empty string is chosen over + // null so that existinc code does not need to null-check but the value is + // still falsey. (Note: If you do have a usable URL, you should probably not + // be using the adoptDownload API and instead be initiating downloads the + // normal way.) + DOMString url; + + // The storageName of the DeviceStorage instance the file was saved to. + // Required but marked as optional so the bindings don't auto-coerce the value + // null to "null". + DOMString? storageName; + // The path of the file within the DeviceStorage instance named by + // 'storageName'. This is used to automatically compute the 'path' of the + // download. Note that when DeviceStorage gives you a path to a file, the + // first path segment is the name of the specific device storage and you do + // *not* want to include this. For example, if DeviceStorage tells you the + // file has a path of '/sdcard1/actual/path/file.ext', then the storageName + // should be 'sdcard1' and the storagePath should be 'actual/path/file.ext'. + // + // The existence of the file will be validated will be validated with stat() + // and the size the file-system tells us will be what we use. + // + // Required but marked as optional so the bindings don't auto-coerce the value + // null to "null". + DOMString? storagePath; + + // The mime type for this resource. Required, but marked as optional because + // WebIDL otherwise auto-coerces the value null to "null". + DOMString? contentType; + + // The time the download was started. If omitted, the current time is used. + Date? startTime; +};
--- a/gfx/layers/LayerMetricsWrapper.h +++ b/gfx/layers/LayerMetricsWrapper.h @@ -304,16 +304,26 @@ public: MOZ_ASSERT(IsValid()); if (AtBottomLayer()) { return mLayer->GetEventRegions(); } return EventRegions(); } + bool HasTransformAnimation() const + { + MOZ_ASSERT(IsValid()); + + if (AtBottomLayer()) { + return mLayer->HasTransformAnimation(); + } + return false; + } + RefLayer* AsRefLayer() const { MOZ_ASSERT(IsValid()); if (AtBottomLayer()) { return mLayer->AsRefLayer(); } return nullptr;
--- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -757,16 +757,27 @@ Layer::GetLocalTransform() transform.PostScale(GetPostXScale(), GetPostYScale(), 1.0f); if (ContainerLayer* c = AsContainerLayer()) { transform.PreScale(c->GetPreXScale(), c->GetPreYScale(), 1.0f); } return transform; } +bool +Layer::HasTransformAnimation() const +{ + for (uint32_t i = 0; i < mAnimations.Length(); i++) { + if (mAnimations[i].property() == eCSSProperty_transform) { + return true; + } + } + return false; +} + void Layer::ApplyPendingUpdatesForThisTransaction() { if (mPendingTransform && *mPendingTransform != mTransform) { MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) PendingUpdatesForThisTransaction", this)); mTransform = *mPendingTransform; Mutated(); }
--- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -1279,16 +1279,18 @@ public: // Note that all lengths in animation data are either in CSS pixels or app // units and must be converted to device pixels by the compositor. AnimationArray& GetAnimations() { return mAnimations; } InfallibleTArray<AnimData>& GetAnimationData() { return mAnimationData; } uint64_t GetAnimationGeneration() { return mAnimationGeneration; } void SetAnimationGeneration(uint64_t aCount) { mAnimationGeneration = aCount; } + bool HasTransformAnimation() const; + /** * Returns the local transform for this layer: either mTransform or, * for shadow layers, GetShadowTransform() */ const gfx::Matrix4x4 GetLocalTransform(); /** * Returns the local opacity for this layer: either mOpacity or,
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp +++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp @@ -86,21 +86,24 @@ GetTransformToAncestorsParentLayer(Layer transform.PostScale(metrics.GetPresShellResolution(), metrics.GetPresShellResolution(), 1.f); } } return transform; } void ClientTiledPaintedLayer::GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor, - LayerMetricsWrapper* aOutDisplayPortAncestor) + LayerMetricsWrapper* aOutDisplayPortAncestor, + bool* aOutHasTransformAnimation) { LayerMetricsWrapper scrollAncestor; LayerMetricsWrapper displayPortAncestor; + bool hasTransformAnimation = false; for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) { + hasTransformAnimation |= ancestor.HasTransformAnimation(); const FrameMetrics& metrics = ancestor.Metrics(); if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) { scrollAncestor = ancestor; } if (!metrics.GetDisplayPort().IsEmpty()) { displayPortAncestor = ancestor; // Any layer that has a displayport must be scrollable, so we can break // here. @@ -108,16 +111,19 @@ ClientTiledPaintedLayer::GetAncestorLaye } } if (aOutScrollAncestor) { *aOutScrollAncestor = scrollAncestor; } if (aOutDisplayPortAncestor) { *aOutDisplayPortAncestor = displayPortAncestor; } + if (aOutHasTransformAnimation) { + *aOutHasTransformAnimation = hasTransformAnimation; + } } void ClientTiledPaintedLayer::BeginPaint() { mPaintData.mLowPrecisionPaintCount = 0; mPaintData.mPaintFinished = false; mPaintData.mCompositionBounds.SetEmpty(); @@ -129,47 +135,53 @@ ClientTiledPaintedLayer::BeginPaint() // given that it's a pretty rare scenario. return; } // Get the metrics of the nearest scrollable layer and the nearest layer // with a displayport. LayerMetricsWrapper scrollAncestor; LayerMetricsWrapper displayPortAncestor; - GetAncestorLayers(&scrollAncestor, &displayPortAncestor); + bool hasTransformAnimation; + GetAncestorLayers(&scrollAncestor, &displayPortAncestor, &hasTransformAnimation); if (!displayPortAncestor || !scrollAncestor) { // No displayport or scroll ancestor, so we can't do progressive rendering. #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_B2G) // Both Android and b2g are guaranteed to have a displayport set, so this // should never happen. NS_WARNING("Tiled PaintedLayer with no scrollable container ancestor"); #endif return; } - TILING_LOG("TILING %p: Found scrollAncestor %p and displayPortAncestor %p\n", this, - scrollAncestor.GetLayer(), displayPortAncestor.GetLayer()); + TILING_LOG("TILING %p: Found scrollAncestor %p, displayPortAncestor %p, transform %d\n", this, + scrollAncestor.GetLayer(), displayPortAncestor.GetLayer(), hasTransformAnimation); const FrameMetrics& scrollMetrics = scrollAncestor.Metrics(); const FrameMetrics& displayportMetrics = displayPortAncestor.Metrics(); // Calculate the transform required to convert ParentLayer space of our // display port ancestor to the Layer space of this layer. gfx::Matrix4x4 transformDisplayPortToLayer = GetTransformToAncestorsParentLayer(this, displayPortAncestor); transformDisplayPortToLayer.Invert(); // Compute the critical display port that applies to this layer in the - // LayoutDevice space of this layer. - ParentLayerRect criticalDisplayPort = - (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom()) - + displayportMetrics.mCompositionBounds.TopLeft(); - mPaintData.mCriticalDisplayPort = RoundedOut( - ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort)); + // LayoutDevice space of this layer, but only if there is no OMT animation + // on this layer. If there is an OMT animation then we need to draw the whole + // visible region of this layer as determined by layout, because we don't know + // what parts of it might move into view in the compositor. + if (!hasTransformAnimation) { + ParentLayerRect criticalDisplayPort = + (displayportMetrics.GetCriticalDisplayPort() * displayportMetrics.GetZoom()) + + displayportMetrics.mCompositionBounds.TopLeft(); + mPaintData.mCriticalDisplayPort = RoundedOut( + ApplyParentLayerToLayerTransform(transformDisplayPortToLayer, criticalDisplayPort)); + } TILING_LOG("TILING %p: Critical displayport %s\n", this, Stringify(mPaintData.mCriticalDisplayPort).c_str()); // Store the resolution from the displayport ancestor layer. Because this is Gecko-side, // before any async transforms have occurred, we can use the zoom for this. mPaintData.mResolution = displayportMetrics.GetZoom(); TILING_LOG("TILING %p: Resolution %f\n", this, mPaintData.mResolution.scale); // Store the applicable composition bounds in this layer's Layer units. @@ -212,61 +224,58 @@ ClientTiledPaintedLayer::IsScrollingOnCo aParentMetrics.GetScrollOffset().x, COORDINATE_EPSILON) || !FuzzyEqualsAdditive(compositorMetrics.GetScrollOffset().y, aParentMetrics.GetScrollOffset().y, COORDINATE_EPSILON); } bool -ClientTiledPaintedLayer::UseFastPath() -{ - // The fast path doesn't allow rendering at low resolution. It will draw the low-res - // area at full resolution and cause OOM. - if (gfxPrefs::UseLowPrecisionBuffer()) { +ClientTiledPaintedLayer::UseProgressiveDraw() { + if (!gfxPlatform::GetPlatform()->UseProgressivePaint()) { + // pref is disabled, so never do progressive + return false; + } + + if (ClientManager()->HasShadowTarget()) { + // This condition is true when we are in a reftest scenario. We don't want + // to draw progressively here because it can cause intermittent reftest + // failures because the harness won't wait for all the tiles to be drawn. return false; } - LayerMetricsWrapper scrollAncestor; - GetAncestorLayers(&scrollAncestor, nullptr); - if (!scrollAncestor) { - return true; + if (mPaintData.mCriticalDisplayPort.IsEmpty()) { + // This catches three scenarios: + // 1) This layer doesn't have a scrolling ancestor + // 2) This layer is subject to OMTA transforms + // 3) Low-precision painting is disabled + // In all of these cases, we don't want to draw this layer progressively. + return false; } - const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); - bool multipleTransactionsNeeded = gfxPlatform::GetPlatform()->UseProgressivePaint() - || !parentMetrics.GetCriticalDisplayPort().IsEmpty(); - bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition(); - bool isScrollable = parentMetrics.IsScrollable(); - - return !multipleTransactionsNeeded || isFixed || !isScrollable; -} - -bool -ClientTiledPaintedLayer::UseProgressiveDraw() { - // Don't draw progressively in a reftest scenario (that's what the HasShadowTarget() check is for). - if (!gfxPlatform::GetPlatform()->UseProgressivePaint() || ClientManager()->HasShadowTarget()) { + if (GetIsFixedPosition() || GetParent()->GetIsFixedPosition()) { + // This layer is fixed-position and so even if it does have a scrolling + // ancestor it will likely be entirely on-screen all the time, so we + // should draw it all at once return false; } // XXX We probably want to disable progressive drawing for non active APZ layers in the future // but we should wait for a proper test case before making this change. - #if 0 //!defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ) LayerMetricsWrapper scrollAncestor; - GetAncestorLayers(&scrollAncestor, nullptr); - if (!scrollAncestor) { - return true; - } + GetAncestorLayers(&scrollAncestor, nullptr, nullptr); + MOZ_ASSERT(scrollAncestor); // because mPaintData.mCriticalDisplayPort is non-empty const FrameMetrics& parentMetrics = scrollAncestor.Metrics(); + if (!IsScrollingOnCompositor(parentMetrics)) { + return false; + } +#endif - return !IsScrollingOnCompositor(parentMetrics); -#else return true; -#endif } bool ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion, const nsIntRegion& aVisibleRegion, LayerManager::DrawPaintedLayerCallback aCallback, void* aCallbackData) { @@ -436,26 +445,16 @@ ClientTiledPaintedLayer::RenderLayer() } if (!ClientManager()->IsRepeatTransaction()) { // Only paint the mask layer on the first transaction. if (GetMaskLayer()) { ToClientLayer(GetMaskLayer())->RenderLayer(); } - // In some cases we can take a fast path and just be done with it. - if (UseFastPath()) { - TILING_LOG("TILING %p: Taking fast-path\n", this); - mValidRegion = neededRegion; - mContentClient->mTiledBuffer.PaintThebes(mValidRegion, invalidRegion, callback, data); - ClientManager()->Hold(this); - mContentClient->UseTiledLayerBuffer(TiledContentClient::TILED_BUFFER); - return; - } - // For more complex cases we need to calculate a bunch of metrics before we // can do the paint. BeginPaint(); if (mPaintData.mPaintFinished) { return; } // Make sure that tiles that fall outside of the visible region or outside of the
--- a/gfx/layers/client/ClientTiledPaintedLayer.h +++ b/gfx/layers/client/ClientTiledPaintedLayer.h @@ -76,37 +76,32 @@ public: virtual void ClearCachedResources() MOZ_OVERRIDE; /** * Helper method to find the nearest ancestor layers which * scroll and have a displayport. The parameters are out-params * which hold the return values; the values passed in may be null. */ void GetAncestorLayers(LayerMetricsWrapper* aOutScrollAncestor, - LayerMetricsWrapper* aOutDisplayPortAncestor); + LayerMetricsWrapper* aOutDisplayPortAncestor, + bool* aOutHasTransformAnimation); private: ClientLayerManager* ClientManager() { return static_cast<ClientLayerManager*>(mManager); } /** * For the initial PaintThebes of a transaction, calculates all the data * needed for that paint and any repeated transactions. */ void BeginPaint(); /** - * Determine if we can use a fast path to just do a single high-precision, - * non-progressive paint. - */ - bool UseFastPath(); - - /** * Check if the layer is being scrolled by APZ on the compositor. */ bool IsScrollingOnCompositor(const FrameMetrics& aParentMetrics); /** * Check if we should use progressive draw on this layer. We will * disable progressive draw based on a preference or if the layer * is not being scrolled.
--- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -1355,17 +1355,17 @@ ClientTiledLayerBuffer::ComputeProgressi // Find out if we have any non-stale content to update. nsIntRegion staleRegion; staleRegion.And(aInvalidRegion, aOldValidRegion); TILING_LOG("TILING %p: Progressive update stale region %s\n", mPaintedLayer, Stringify(staleRegion).c_str()); LayerMetricsWrapper scrollAncestor; - mPaintedLayer->GetAncestorLayers(&scrollAncestor, nullptr); + mPaintedLayer->GetAncestorLayers(&scrollAncestor, nullptr, nullptr); // Find out the current view transform to determine which tiles to draw // first, and see if we should just abort this paint. Aborting is usually // caused by there being an incoming, more relevant paint. ViewTransform viewTransform; #if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_ANDROID_APZ) FrameMetrics contentMetrics = scrollAncestor.Metrics(); bool abortPaint = false;
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3234,17 +3234,17 @@ nsLayoutUtils::PaintFrame(nsRenderingCon gPaintCount++; #endif } #ifdef MOZ_DUMP_PAINTING if (gfxPrefs::DumpClientLayers()) { std::stringstream ss; FrameLayerBuilder::DumpRetainedLayerTree(layerManager, ss, false); - printf_stderr("%s", ss.str().c_str()); + print_stderr(ss); } #endif // Update the widget's opaque region information. This sets // glass boundaries on Windows. Also set up the window dragging region // and plugin clip regions and bounds. if ((aFlags & PAINT_WIDGET_LAYERS) && !willFlushRetainedLayers &&