Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2016-01-19 15:23:49 +01:00
Родитель 99a8e0dd46 6d5ed63674
Коммит 0fc1cfad3f
157 изменённых файлов: 6653 добавлений и 5773 удалений

Просмотреть файл

@ -236,7 +236,7 @@ two words
<ul id="ul1">
<li id="li1">Item</li>
<li id="li2"></li>
<li id="li3" style="width:10ex; font-family:monospace; font-size:10pt;">a long and winding road that lead me to your door</li>
<li id="li3" style="font-family:monospace; font-size:10pt; width:8ch;">a long and winding road that lead me to your door</li>
<li id="li4">a <a href=''>b</a> c</li>
<li id="li5"><br>hello</li>
</ul>
@ -244,7 +244,7 @@ two words
<ol id="ol1">
<li id="li6">Item</li>
<li id="li7"></li>
<li id="li8" style="width:10ex; font-family:monospace; font-size:10pt;">a long and winding road that lead me to your door</li>
<li id="li8" style="font-family:monospace; font-size:10pt; width:8ch;">a long and winding road that lead me to your door</li>
<li id="li9">a <a href=''>b</a> c</li>
<li id="li10"><br>hello</li>
</ol>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -35,7 +35,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- 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="0f86914b89cf8a069533e66b218533a17bad6b43"/>
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="6b1fb5b730b1299f99f9194c1fcf088579cc7977"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -35,7 +35,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -35,7 +35,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -31,7 +31,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<!-- Stock Android things -->

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -34,7 +34,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -34,7 +34,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -35,7 +35,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>

Просмотреть файл

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4",
"git_revision": "43852628a9d506c65525cceb5789b257cc939fe8",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "5dec1b4e9d9c827e70b3f2018f1857b8ed1e1068",
"revision": "c1f3c74a949f3d10f802aae79a705d440b08b091",
"repo_path": "integration/gaia-central"
}

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -35,7 +35,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -32,7 +32,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<!-- Stock Android things -->

Просмотреть файл

@ -21,7 +21,7 @@
<!--
B2G repositories for all targets
-->
<project name="gaia" path="gaia" remote="mozillaorg" revision="6164cd7af92ec2a3422d48f17f9577fc9b3f7ff4"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="43852628a9d506c65525cceb5789b257cc939fe8"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="35dccb3127db8f39f20b985ad312d2cd44780669"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
<project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
@ -35,7 +35,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eb1475f0ec4b6512cf43206845b504d61c989b2"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="833b5d767b5e2f683b3c9525e38c58402e394f3e"/>
<!-- Stock Android things -->
<project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
<project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>

Просмотреть файл

@ -401,12 +401,34 @@ nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel,
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(aChannel, loadContext);
if (loadContext) {
return GetLoadContextCodebasePrincipal(uri, loadContext, aPrincipal);
nsCOMPtr<nsILoadInfo> loadInfo;
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
nsContentPolicyType contentPolicyType = nsIContentPolicy::TYPE_INVALID;
if (loadInfo) {
contentPolicyType = loadInfo->GetExternalContentPolicyType();
}
PrincipalOriginAttributes attrs;
if (nsIContentPolicy::TYPE_DOCUMENT == contentPolicyType ||
nsIContentPolicy::TYPE_SUBDOCUMENT == contentPolicyType) {
// If it's document or sub-document, inherit originAttributes from
// the document.
if (loadContext) {
DocShellOriginAttributes docShellAttrs;
loadContext->GetOriginAttributes(docShellAttrs);
attrs.InheritFromDocShellToDoc(docShellAttrs, uri);
}
} else {
// Inherit origin attributes from loading principal if any.
nsCOMPtr<nsIPrincipal> loadingPrincipal;
if (loadInfo) {
loadInfo->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal));
}
if (loadingPrincipal) {
attrs = BasePrincipal::Cast(loadingPrincipal)->OriginAttributesRef();
}
}
//TODO: Bug 1211590. inherit Origin Attributes from LoadInfo.
PrincipalOriginAttributes attrs(UNKNOWN_APP_ID, false);
rv = MaybeSetAddonIdFromURI(attrs, uri);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(uri, attrs);

Просмотреть файл

@ -4011,6 +4011,7 @@ nsDocShell::AddChild(nsIDocShellTreeItem* aChild)
}
aChild->SetTreeOwner(mTreeOwner);
childDocShell->SetUserContextId(mUserContextId);
nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
if (!childAsDocShell) {
@ -13858,6 +13859,17 @@ NS_IMETHODIMP
nsDocShell::SetUserContextId(uint32_t aUserContextId)
{
mUserContextId = aUserContextId;
nsTObserverArray<nsDocLoader*>::ForwardIterator iter(mChildList);
while (iter.HasMore()) {
nsCOMPtr<nsIDocShell> docshell = do_QueryObject(iter.GetNext());
if (!docshell || docshell->ItemType() != ItemType()) {
continue;
}
docshell->SetUserContextId(aUserContextId);
}
return NS_OK;
}

Просмотреть файл

@ -796,7 +796,6 @@ Animation::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
// immediately before updating the style rule and then restore it immediately
// afterwards. This is purely to prevent visual flicker. Other behavior
// such as dispatching events continues to rely on the regular timeline time.
bool updatedHoldTime = false;
AnimationPlayState playState = PlayState();
{
AutoRestore<Nullable<TimeDuration>> restoreHoldTime(mHoldTime);
@ -813,20 +812,12 @@ Animation::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
if (!timeToUse.IsNull()) {
mHoldTime.SetValue((timeToUse.Value() - mStartTime.Value())
.MultDouble(mPlaybackRate));
// Push the change down to the effect
UpdateEffect();
updatedHoldTime = true;
}
}
mEffect->ComposeStyle(aStyleRule, aSetProperties);
}
// Now that the hold time has been restored, update the effect
if (updatedHoldTime) {
UpdateEffect();
}
MOZ_ASSERT(playState == PlayState(),
"Play state should not change during the course of compositing");
mFinishedAtLastComposeStyle = (playState == AnimationPlayState::Finished);

Просмотреть файл

@ -565,6 +565,9 @@ EffectCompositor::ComposeAnimationRule(dom::Element* aElement,
effect->GetAnimation()->ComposeStyle(animationRule, properties);
}
MOZ_ASSERT(effects == EffectSet::GetEffectSet(aElement, aPseudoType),
"EffectSet should not change while composing style");
effects->UpdateAnimationRuleRefreshTime(aCascadeLevel, aRefreshTime);
}

Просмотреть файл

@ -1780,8 +1780,7 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
// We need to delete the properties while we're still in document
// (if we were in document).
// FIXME (Bug 522599): Need a test for this.
//XXXsmaug this looks slow.
if (HasFlag(NODE_HAS_PROPERTIES)) {
if (MayHaveAnimations()) {
DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
DeleteProperty(nsGkAtoms::transitionsProperty);

Просмотреть файл

@ -8243,6 +8243,24 @@ nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
}
}
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return StorageAccess::eDeny;
}
// check the permission manager for any allow or deny permissions
// for cookies for the window.
uint32_t perm;
permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
if (perm == nsIPermissionManager::DENY_ACTION) {
return StorageAccess::eDeny;
} else if (perm == nsICookiePermission::ACCESS_SESSION) {
return std::min(access, StorageAccess::eSessionScoped);
} else if (perm == nsIPermissionManager::ALLOW_ACTION) {
return access;
}
// Check if we should only allow storage for the session, and record that fact
if (sCookiesLifetimePolicy == nsICookieService::ACCEPT_SESSION) {
// Storage could be StorageAccess::ePrivateBrowsing or StorageAccess::eAllow
@ -8284,24 +8302,6 @@ nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
}
}
nsCOMPtr<nsIPermissionManager> permissionManager =
services::GetPermissionManager();
if (!permissionManager) {
return StorageAccess::eDeny;
}
// check the permission manager for any allow or deny permissions
// for cookies for the window.
uint32_t perm;
permissionManager->TestPermissionFromPrincipal(aPrincipal, "cookie", &perm);
if (perm == nsIPermissionManager::DENY_ACTION) {
return StorageAccess::eDeny;
} else if (perm == nsICookiePermission::ACCESS_SESSION) {
return std::min(access, StorageAccess::eSessionScoped);
} else if (perm == nsIPermissionManager::ALLOW_ACTION) {
return access;
}
// We don't want to prompt for every attempt to access permissions.
if (sCookiesBehavior == nsICookieService::BEHAVIOR_REJECT) {
return StorageAccess::eDeny;

Просмотреть файл

@ -1222,6 +1222,7 @@ GK_ATOM(tr, "tr")
GK_ATOM(track, "track")
GK_ATOM(trailing, "trailing")
GK_ATOM(transform, "transform")
GK_ATOM(transform_3d, "transform-3d")
GK_ATOM(transformiix, "transformiix")
GK_ATOM(translate, "translate")
GK_ATOM(transparent, "transparent")

Просмотреть файл

@ -529,7 +529,7 @@ nsDOMCameraControl::TrackCreated(TrackID aTrackID) {
// This track is not connected through a port.
MediaInputPort* inputPort = nullptr;
dom::VideoStreamTrack* track =
new dom::VideoStreamTrack(this, aTrackID);
new dom::VideoStreamTrack(this, aTrackID, nsString());
RefPtr<TrackPort> port =
new TrackPort(inputPort, track,
TrackPort::InputPortOwnership::OWNED);

Просмотреть файл

@ -682,7 +682,7 @@ HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
return nullptr;
}
stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO);
stream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString());
rv = RegisterFrameCaptureListener(stream->FrameCaptureListener());
if (NS_FAILED(rv)) {

Просмотреть файл

@ -1890,11 +1890,11 @@ HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded,
// Expose the tracks to JS directly.
if (HasAudio()) {
TrackID audioTrackId = mMediaInfo.mAudio.mTrackId;
out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO);
out->mStream->CreateOwnDOMTrack(audioTrackId, MediaSegment::AUDIO, nsString());
}
if (HasVideo()) {
TrackID videoTrackId = mMediaInfo.mVideo.mTrackId;
out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO);
out->mStream->CreateOwnDOMTrack(videoTrackId, MediaSegment::VIDEO, nsString());
}
}
}

Просмотреть файл

@ -14,8 +14,8 @@ namespace dom {
class AudioStreamTrack : public MediaStreamTrack {
public:
AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID)
: MediaStreamTrack(aStream, aTrackID) {}
AudioStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
: MediaStreamTrack(aStream, aTrackID, aLabel) {}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

Просмотреть файл

@ -128,7 +128,7 @@ public:
NS_WARN_IF_FALSE(!mStream->mTracks.IsEmpty(),
"A new track was detected on the input stream; creating a corresponding MediaStreamTrack. "
"Initial tracks should be added manually to immediately and synchronously be available to JS.");
mStream->CreateOwnDOMTrack(aTrackId, aType);
mStream->CreateOwnDOMTrack(aTrackId, aType, nsString());
}
void DoNotifyTrackEnded(TrackID aTrackId)
@ -619,7 +619,7 @@ DOMMediaStream::InitAudioCaptureStream(nsIDOMWindow* aWindow,
InitInputStreamCommon(aGraph->CreateAudioCaptureStream(this, AUDIO_TRACK), aGraph);
InitOwnedStreamCommon(aGraph);
InitPlaybackStreamCommon(aGraph);
CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO);
CreateOwnDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, nsString());
}
void
@ -780,7 +780,7 @@ DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver
}
MediaStreamTrack*
DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel)
{
MOZ_RELEASE_ASSERT(mInputStream);
MOZ_RELEASE_ASSERT(mOwnedStream);
@ -790,10 +790,10 @@ DOMMediaStream::CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
MediaStreamTrack* track;
switch (aType) {
case MediaSegment::AUDIO:
track = new AudioStreamTrack(this, aTrackID);
track = new AudioStreamTrack(this, aTrackID, aLabel);
break;
case MediaSegment::VIDEO:
track = new VideoStreamTrack(this, aTrackID);
track = new VideoStreamTrack(this, aTrackID, aLabel);
break;
default:
MOZ_CRASH("Unhandled track type");

Просмотреть файл

@ -476,7 +476,7 @@ public:
*
* Creates a MediaStreamTrack, adds it to mTracks and returns it.
*/
MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType);
MediaStreamTrack* CreateOwnDOMTrack(TrackID aTrackID, MediaSegment::Type aType, const nsString& aLabel);
class OnTracksAvailableCallback {
public:

Просмотреть файл

@ -1025,10 +1025,14 @@ public:
msg);
if (mAudioDevice) {
domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO);
nsString audioDeviceName;
mAudioDevice->GetName(audioDeviceName);
domStream->CreateOwnDOMTrack(kAudioTrack, MediaSegment::AUDIO, audioDeviceName);
}
if (mVideoDevice) {
domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO);
nsString videoDeviceName;
mVideoDevice->GetName(videoDeviceName);
domStream->CreateOwnDOMTrack(kVideoTrack, MediaSegment::VIDEO, videoDeviceName);
}
nsCOMPtr<nsIPrincipal> principal;

Просмотреть файл

@ -12,8 +12,8 @@
namespace mozilla {
namespace dom {
MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID)
: mOwningStream(aStream), mTrackID(aTrackID), mEnded(false), mEnabled(true)
MediaStreamTrack::MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
: mOwningStream(aStream), mTrackID(aTrackID), mLabel(aLabel), mEnded(false), mEnabled(true)
{
nsresult rv;

Просмотреть файл

@ -29,7 +29,7 @@ public:
* aTrackID is the MediaStreamGraph track ID for the track in the
* MediaStream owned by aStream.
*/
MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID);
MediaStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrack,
@ -54,7 +54,7 @@ public:
// WebIDL
virtual void GetKind(nsAString& aKind) = 0;
void GetId(nsAString& aID) const;
void GetLabel(nsAString& aLabel) { aLabel.Truncate(); }
void GetLabel(nsAString& aLabel) { aLabel.Assign(mLabel); }
bool Enabled() { return mEnabled; }
void SetEnabled(bool aEnabled);
void Stop();
@ -75,6 +75,7 @@ protected:
RefPtr<DOMMediaStream> mOwningStream;
TrackID mTrackID;
nsString mID;
nsString mLabel;
bool mEnded;
bool mEnabled;
};

Просмотреть файл

@ -14,8 +14,8 @@ namespace dom {
class VideoStreamTrack : public MediaStreamTrack {
public:
VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID)
: MediaStreamTrack(aStream, aTrackID) {}
VideoStreamTrack(DOMMediaStream* aStream, TrackID aTrackID, const nsString& aLabel)
: MediaStreamTrack(aStream, aTrackID, aLabel) {}
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;

Просмотреть файл

@ -29,74 +29,16 @@ mozilla::LazyLogModule gCamerasChildLog("CamerasChild");
namespace mozilla {
namespace camera {
// We emulate the sync webrtc.org API with the help of singleton
// CamerasSingleton, which manages a pointer to an IPC object, a thread
// where IPC operations should run on, and a mutex.
// The static function Cameras() will use that Singleton to set up,
// if needed, both the thread and the associated IPC objects and return
// a pointer to the IPC object. Users can then do IPC calls on that object
// after dispatching them to aforementioned thread.
CamerasSingleton::CamerasSingleton()
: mCamerasMutex("CamerasSingleton::mCamerasMutex"),
mCameras(nullptr),
mCamerasChildThread(nullptr) {
LOG(("CamerasSingleton: %p", this));
}
// 2 Threads are involved in this code:
// - the MediaManager thread, which will call the (static, sync API) functions
// through MediaEngineRemoteVideoSource
// - the Cameras IPC thread, which will be doing our IPC to the parent process
// via PBackground
// Our main complication is that we emulate a sync API while (having to do)
// async messaging. We dispatch the messages to another thread to send them
// async and hold a Monitor to wait for the result to be asynchronously received
// again. The requirement for async messaging originates on the parent side:
// it's not reasonable to block all PBackground IPC there while waiting for
// something like device enumeration to complete.
class CamerasSingleton {
public:
CamerasSingleton()
: mCamerasMutex("CamerasSingleton::mCamerasMutex"),
mCameras(nullptr),
mCamerasChildThread(nullptr) {
LOG(("CamerasSingleton: %p", this));
}
~CamerasSingleton() {
LOG(("~CamerasSingleton: %p", this));
}
static CamerasSingleton& GetInstance() {
static CamerasSingleton instance;
return instance;
}
static OffTheBooksMutex& Mutex() {
return GetInstance().mCamerasMutex;
}
static CamerasChild*& Child() {
GetInstance().Mutex().AssertCurrentThreadOwns();
return GetInstance().mCameras;
}
static nsCOMPtr<nsIThread>& Thread() {
GetInstance().Mutex().AssertCurrentThreadOwns();
return GetInstance().mCamerasChildThread;
}
private:
// Reinitializing CamerasChild will change the pointers below.
// We don't want this to happen in the middle of preparing IPC.
// We will be alive on destruction, so this needs to be off the books.
mozilla::OffTheBooksMutex mCamerasMutex;
// This is owned by the IPC code, and the same code controls the lifetime.
// It will set and clear this pointer as appropriate in setup/teardown.
// We'd normally make this a WeakPtr but unfortunately the IPC code already
// uses the WeakPtr mixin in a protected base class of CamerasChild, and in
// any case the object becomes unusable as soon as IPC is tearing down, which
// will be before actual destruction.
CamerasChild* mCameras;
nsCOMPtr<nsIThread> mCamerasChildThread;
};
CamerasSingleton::~CamerasSingleton() {
LOG(("~CamerasSingleton: %p", this));
}
class InitializeIPCThread : public nsRunnable
{
@ -135,7 +77,7 @@ private:
CamerasChild* mCamerasChild;
};
static CamerasChild*
CamerasChild*
GetCamerasChild() {
CamerasSingleton::Mutex().AssertCurrentThreadOwns();
if (!CamerasSingleton::Child()) {
@ -189,17 +131,6 @@ CamerasChild::RecvReplySuccess(void)
return true;
}
int NumberOfCapabilities(CaptureEngine aCapEngine, const char* deviceUniqueIdUTF8)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->NumberOfCapabilities(aCapEngine, deviceUniqueIdUTF8);
} else {
return 0;
}
}
bool
CamerasChild::RecvReplyNumberOfCapabilities(const int& numdev)
{
@ -263,17 +194,6 @@ CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine,
return mReplyInteger;
}
int NumberOfCaptureDevices(CaptureEngine aCapEngine)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->NumberOfCaptureDevices(aCapEngine);
} else {
return 0;
}
}
int
CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine)
{
@ -307,22 +227,6 @@ CamerasChild::RecvReplyNumberOfCaptureDevices(const int& numdev)
return true;
}
int GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8,
const unsigned int capability_number,
webrtc::CaptureCapability& capability)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->GetCaptureCapability(aCapEngine,
unique_idUTF8,
capability_number,
capability);
} else {
return -1;
}
}
int
CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
const char* unique_idUTF8,
@ -365,27 +269,6 @@ CamerasChild::RecvReplyGetCaptureCapability(const CaptureCapability& ipcCapabili
return true;
}
int GetCaptureDevice(CaptureEngine aCapEngine,
unsigned int list_number, char* device_nameUTF8,
const unsigned int device_nameUTF8Length,
char* unique_idUTF8,
const unsigned int unique_idUTF8Length)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->GetCaptureDevice(aCapEngine,
list_number,
device_nameUTF8,
device_nameUTF8Length,
unique_idUTF8,
unique_idUTF8Length);
} else {
return -1;
}
}
int
CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
unsigned int list_number, char* device_nameUTF8,
@ -427,23 +310,6 @@ CamerasChild::RecvReplyGetCaptureDevice(const nsCString& device_name,
return true;
}
int AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->AllocateCaptureDevice(aCapEngine,
unique_idUTF8,
unique_idUTF8Length,
capture_id);
} else {
return -1;
}
}
int
CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
@ -483,17 +349,6 @@ CamerasChild::RecvReplyAllocateCaptureDevice(const int& numdev)
return true;
}
int ReleaseCaptureDevice(CaptureEngine aCapEngine, const int capture_id)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->ReleaseCaptureDevice(aCapEngine, capture_id);
} else {
return -1;
}
}
int
CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
const int capture_id)
@ -539,23 +394,6 @@ CamerasChild::RemoveCallback(const CaptureEngine aCapEngine, const int capture_i
}
}
int StartCapture(CaptureEngine aCapEngine,
const int capture_id,
webrtc::CaptureCapability& webrtcCaps,
webrtc::ExternalRenderer* cb)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->StartCapture(aCapEngine,
capture_id,
webrtcCaps,
cb);
} else {
return -1;
}
}
int
CamerasChild::StartCapture(CaptureEngine aCapEngine,
const int capture_id,
@ -586,17 +424,6 @@ CamerasChild::StartCapture(CaptureEngine aCapEngine,
return 0;
}
int StopCapture(CaptureEngine aCapEngine, const int capture_id)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return child->StopCapture(aCapEngine, capture_id);
} else {
return -1;
}
}
int
CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
{

Просмотреть файл

@ -7,6 +7,7 @@
#ifndef mozilla_CamerasChild_h
#define mozilla_CamerasChild_h
#include "mozilla/Move.h"
#include "mozilla/Pair.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/camera/PCamerasChild.h"
@ -46,33 +47,96 @@ struct CapturerElement {
webrtc::ExternalRenderer* callback;
};
// statically mirror webrtc.org ViECapture API
// these are called via MediaManager->MediaEngineRemoteVideoSource
// on the MediaManager thread
int NumberOfCapabilities(CaptureEngine aCapEngine,
const char* deviceUniqueIdUTF8);
int GetCaptureCapability(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int capability_number,
webrtc::CaptureCapability& capability);
int NumberOfCaptureDevices(CaptureEngine aCapEngine);
int GetCaptureDevice(CaptureEngine aCapEngine,
unsigned int list_number, char* device_nameUTF8,
const unsigned int device_nameUTF8Length,
char* unique_idUTF8,
const unsigned int unique_idUTF8Length);
int AllocateCaptureDevice(CaptureEngine aCapEngine,
const char* unique_idUTF8,
const unsigned int unique_idUTF8Length,
int& capture_id);
int ReleaseCaptureDevice(CaptureEngine aCapEngine,
const int capture_id);
int StartCapture(CaptureEngine aCapEngine,
const int capture_id, webrtc::CaptureCapability& capability,
webrtc::ExternalRenderer* func);
int StopCapture(CaptureEngine aCapEngine, const int capture_id);
// Forward declaration so we can work with pointers to it.
class CamerasChild;
// We emulate the sync webrtc.org API with the help of singleton
// CamerasSingleton, which manages a pointer to an IPC object, a thread
// where IPC operations should run on, and a mutex.
// The static function Cameras() will use that Singleton to set up,
// if needed, both the thread and the associated IPC objects and return
// a pointer to the IPC object. Users can then do IPC calls on that object
// after dispatching them to aforementioned thread.
// 2 Threads are involved in this code:
// - the MediaManager thread, which will call the (static, sync API) functions
// through MediaEngineRemoteVideoSource
// - the Cameras IPC thread, which will be doing our IPC to the parent process
// via PBackground
// Our main complication is that we emulate a sync API while (having to do)
// async messaging. We dispatch the messages to another thread to send them
// async and hold a Monitor to wait for the result to be asynchronously received
// again. The requirement for async messaging originates on the parent side:
// it's not reasonable to block all PBackground IPC there while waiting for
// something like device enumeration to complete.
class CamerasSingleton {
public:
CamerasSingleton();
~CamerasSingleton();
static OffTheBooksMutex& Mutex() {
return GetInstance().mCamerasMutex;
}
static CamerasChild*& Child() {
Mutex().AssertCurrentThreadOwns();
return GetInstance().mCameras;
}
static nsCOMPtr<nsIThread>& Thread() {
Mutex().AssertCurrentThreadOwns();
return GetInstance().mCamerasChildThread;
}
private:
static CamerasSingleton& GetInstance() {
static CamerasSingleton instance;
return instance;
}
// Reinitializing CamerasChild will change the pointers below.
// We don't want this to happen in the middle of preparing IPC.
// We will be alive on destruction, so this needs to be off the books.
mozilla::OffTheBooksMutex mCamerasMutex;
// This is owned by the IPC code, and the same code controls the lifetime.
// It will set and clear this pointer as appropriate in setup/teardown.
// We'd normally make this a WeakPtr but unfortunately the IPC code already
// uses the WeakPtr mixin in a protected base class of CamerasChild, and in
// any case the object becomes unusable as soon as IPC is tearing down, which
// will be before actual destruction.
CamerasChild* mCameras;
nsCOMPtr<nsIThread> mCamerasChildThread;
};
// Get a pointer to a CamerasChild object we can use to do IPC with.
// This does everything needed to set up, including starting the IPC
// channel with PBackground, blocking until thats done, and starting the
// thread to do IPC on. This will fail if we're in shutdown. On success
// it will set up the CamerasSingleton.
CamerasChild* GetCamerasChild();
// Shut down the IPC channel and everything associated, like WebRTC.
// This is a static call because the CamerasChild object may not even
// be alive when we're called.
void Shutdown(void);
// Obtain the CamerasChild object (if possible, i.e. not shutting down),
// and maintain a grip on the object for the duration of the call.
template <class MEM_FUN, class... ARGS>
int GetChildAndCall(MEM_FUN&& f, ARGS&&... args)
{
OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
CamerasChild* child = GetCamerasChild();
if (child) {
return (child->*f)(mozilla::Forward<ARGS>(args)...);
} else {
return -1;
}
}
class CamerasChild final : public PCamerasChild
{
friend class mozilla::ipc::BackgroundChildImpl;

Просмотреть файл

@ -32,15 +32,25 @@ runTest(() =>
.then(() => navigator.mediaDevices.enumerateDevices())
.then(devices => {
ok(devices.length > 0, "At least one device found");
devices.forEach(d => {
var jsoned = JSON.parse(JSON.stringify(devices));
is(jsoned[0].kind, devices[0].kind, "kind survived serializer");
is(jsoned[0].deviceId, devices[0].deviceId, "deviceId survived serializer");
return devices.reduce((p, d) => {
ok(d.kind == "videoinput" || d.kind == "audioinput", "Known device kind");
is(d.deviceId.length, 44, "Correct device id length");
ok(d.label.length !== undefined, "Device label: " + d.label);
is(d.groupId, "", "Don't support groupId yet");
});
var jsoned = JSON.parse(JSON.stringify(devices));
is(jsoned[0].kind, devices[0].kind, "kind survived serializer");
is(jsoned[0].deviceId, devices[0].deviceId, "deviceId survived serializer");
var c = {};
c[d.kind == "videoinput" ? "video" : "audio"] = { deviceId: d.deviceId };
return p
.then(() => navigator.mediaDevices.getUserMedia(c))
.then(stream => {
stream.getTracks().forEach(track => {
is(typeof(track.label), "string", "Label is a string")
is(track.label, d.label, "Label is the device label")
})
});
}, Promise.resolve());
})
// Check deviceId failure paths for video.
.then(() => mustSucceed("unknown plain deviceId on video",

Просмотреть файл

@ -34,7 +34,7 @@ MediaStreamAudioDestinationNode::MediaStreamAudioDestinationNode(AudioContext* a
aContext->Graph()))
{
// Ensure an audio track with the correct ID is exposed to JS
mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK, MediaSegment::AUDIO);
mDOMStream->CreateOwnDOMTrack(AudioNodeStream::AUDIO_TRACK, MediaSegment::AUDIO, nsString());
ProcessedMediaStream* outputStream = mDOMStream->GetInputStream()->AsProcessedStream();
MOZ_ASSERT(!!outputStream);

Просмотреть файл

@ -17,6 +17,12 @@ extern mozilla::LogModule* GetMediaManagerLog();
namespace mozilla {
// These need a definition somewhere because template
// code is allowed to take their address, and they aren't
// guaranteed to have one without this.
const unsigned int MediaEngineSource::kMaxDeviceNameLength;
const unsigned int MediaEngineSource::kMaxUniqueIdLength;;
using dom::ConstrainLongRange;
NS_IMPL_ISUPPORTS0(MediaEngineRemoteVideoSource)
@ -38,10 +44,11 @@ MediaEngineRemoteVideoSource::Init()
LOG((__PRETTY_FUNCTION__));
char deviceName[kMaxDeviceNameLength];
char uniqueId[kMaxUniqueIdLength];
if (mozilla::camera::GetCaptureDevice(mCapEngine,
mCaptureIndex,
deviceName, kMaxDeviceNameLength,
uniqueId, kMaxUniqueIdLength)) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureDevice,
mCapEngine, mCaptureIndex,
deviceName, kMaxDeviceNameLength,
uniqueId, kMaxUniqueIdLength)) {
LOG(("Error initializing RemoteVideoSource (GetCaptureDevice)"));
return;
}
@ -111,9 +118,9 @@ MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstr
return NS_ERROR_UNEXPECTED;
}
if (mozilla::camera::AllocateCaptureDevice(mCapEngine,
GetUUID().get(),
kMaxUniqueIdLength, mCaptureIndex)) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::AllocateCaptureDevice,
mCapEngine, GetUUID().get(), kMaxUniqueIdLength, mCaptureIndex)) {
return NS_ERROR_FAILURE;
}
mState = kAllocated;
@ -145,7 +152,9 @@ MediaEngineRemoteVideoSource::Deallocate()
if (mState != kStopped && mState != kAllocated) {
return NS_ERROR_FAILURE;
}
mozilla::camera::ReleaseCaptureDevice(mCapEngine, mCaptureIndex);
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::ReleaseCaptureDevice,
mCapEngine, mCaptureIndex);
mState = kReleased;
LOG(("Video device %d deallocated", mCaptureIndex));
} else {
@ -179,8 +188,9 @@ MediaEngineRemoteVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
mState = kStarted;
mTrackID = aID;
if (mozilla::camera::StartCapture(mCapEngine,
mCaptureIndex, mCapability, this)) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StartCapture,
mCapEngine, mCaptureIndex, mCapability, this)) {
LOG(("StartCapture failed"));
return NS_ERROR_FAILURE;
}
@ -217,7 +227,9 @@ MediaEngineRemoteVideoSource::Stop(mozilla::SourceMediaStream* aSource,
mImage = nullptr;
}
mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StopCapture,
mCapEngine, mCaptureIndex);
return NS_OK;
}
@ -239,9 +251,12 @@ MediaEngineRemoteVideoSource::Restart(const dom::MediaTrackConstraints& aConstra
return NS_OK;
}
mozilla::camera::StopCapture(mCapEngine, mCaptureIndex);
if (mozilla::camera::StartCapture(mCapEngine,
mCaptureIndex, mCapability, this)) {
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StopCapture,
mCapEngine, mCaptureIndex);
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::StartCapture,
mCapEngine, mCaptureIndex, mCapability, this)) {
LOG(("StartCapture failed"));
return NS_ERROR_FAILURE;
}
@ -354,7 +369,10 @@ MediaEngineRemoteVideoSource::DeliverFrame(unsigned char* buffer,
size_t
MediaEngineRemoteVideoSource::NumCapabilities()
{
int num = mozilla::camera::NumberOfCapabilities(mCapEngine, GetUUID().get());
int num = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCapabilities,
mCapEngine,
GetUUID().get());
if (num > 0) {
return num;
}
@ -433,10 +451,12 @@ MediaEngineRemoteVideoSource::GetCapability(size_t aIndex,
if (!mHardcodedCapabilities.IsEmpty()) {
MediaEngineCameraVideoSource::GetCapability(aIndex, aOut);
}
mozilla::camera::GetCaptureCapability(mCapEngine,
GetUUID().get(),
aIndex,
aOut);
mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureCapability,
mCapEngine,
GetUUID().get(),
aIndex,
aOut);
}
void MediaEngineRemoteVideoSource::Refresh(int aIndex) {
@ -446,10 +466,11 @@ void MediaEngineRemoteVideoSource::Refresh(int aIndex) {
char deviceName[kMaxDeviceNameLength];
char uniqueId[kMaxUniqueIdLength];
if (mozilla::camera::GetCaptureDevice(mCapEngine,
aIndex,
deviceName, sizeof(deviceName),
uniqueId, sizeof(uniqueId))) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureDevice,
mCapEngine, aIndex,
deviceName, sizeof(deviceName),
uniqueId, sizeof(uniqueId))) {
return;
}

Просмотреть файл

@ -161,7 +161,9 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
* mVideoSources must be updated.
*/
int num;
num = mozilla::camera::NumberOfCaptureDevices(capEngine);
num = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCaptureDevices,
capEngine);
if (num <= 0) {
return;
}
@ -175,11 +177,12 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
uniqueId[0] = '\0';
int error;
error = mozilla::camera::GetCaptureDevice(capEngine,
i, deviceName,
sizeof(deviceName), uniqueId,
sizeof(uniqueId));
error = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureDevice,
capEngine,
i, deviceName,
sizeof(deviceName), uniqueId,
sizeof(uniqueId));
if (error) {
LOG(("camera:GetCaptureDevice: Failed %d", error ));
continue;
@ -188,13 +191,17 @@ MediaEngineWebRTC::EnumerateVideoDevices(dom::MediaSourceEnum aMediaSource,
LOG((" Capture Device Index %d, Name %s", i, deviceName));
webrtc::CaptureCapability cap;
int numCaps = mozilla::camera::NumberOfCapabilities(capEngine,
uniqueId);
int numCaps = mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::NumberOfCapabilities,
capEngine,
uniqueId);
LOG(("Number of Capabilities %d", numCaps));
for (int j = 0; j < numCaps; j++) {
if (mozilla::camera::GetCaptureCapability(capEngine,
uniqueId,
j, cap ) != 0 ) {
if (mozilla::camera::GetChildAndCall(
&mozilla::camera::CamerasChild::GetCaptureCapability,
capEngine,
uniqueId,
j, cap) != 0) {
break;
}
LOG(("type=%d width=%d height=%d maxFPS=%d",

Просмотреть файл

@ -40,7 +40,7 @@ const TYPED_ARRAY_THINGS = new Set([
]);
this.SETTINGSDB_NAME = "settings";
this.SETTINGSDB_VERSION = 7;
this.SETTINGSDB_VERSION = 8;
this.SETTINGSSTORE_NAME = "settings";
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");

Просмотреть файл

@ -0,0 +1,8 @@
<!doctype html>
<html>
<body>
<script>
parent.postMessage(SpecialPowers.wrap(localStorage).isSessionOnly, "*");
</script>
</body>
</html>

Просмотреть файл

@ -16,6 +16,7 @@ support-files =
interOriginTest.js
interOriginTest2.js
localStorageCommon.js
frameLocalStorageSessionOnly.html
[test_appIsolation.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 793211 # b2g(needs https to work) b2g-debug(needs https to work) b2g-desktop(needs https to work)
@ -56,3 +57,4 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(needs https
skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || toolkit == 'android'
[test_lowDeviceStorage.html]
[test_storageConstructor.html]
[test_localStorageSessionPrefOverride.html]

Просмотреть файл

@ -0,0 +1,54 @@
<html>
<head>
<title>Local Storage Session Pref Override</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
const ACCEPT_SESSION = 2;
add_task(function*() {
yield new Promise((resolve, reject) => {
SpecialPowers.pushPrefEnv({"set": [["network.cookie.lifetimePolicy",
ACCEPT_SESSION]]},
resolve);
});
// Before setting permission
yield new Promise((resolve) => {
var frame = document.createElement('iframe');
frame.src = "frameLocalStorageSessionOnly.html";
var listener = (e) => {
is(e.data, true, "Before adding permission should be session only");
window.removeEventListener('message', listener);
resolve();
};
window.addEventListener('message', listener);
document.body.appendChild(frame);
});
// After setting permission
yield new Promise((resolve) => {
SpecialPowers.pushPermissions([{"type": "cookie", "allow": 1, "context": document}],
resolve);
});
yield new Promise((resolve) => {
var frame = document.createElement('iframe');
frame.src = "frameLocalStorageSessionOnly.html";
var listener = (e) => {
is(e.data, false, "After adding permission should not be session only");
window.removeEventListener('message', listener);
resolve();
};
window.addEventListener('message', listener);
document.body.appendChild(frame);
});
});
</script>
</head>
<body>
</body>
</html>

Просмотреть файл

@ -49,6 +49,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
}
var firstMoveListener = function (e) {
info("Got first mousemove");
div.removeEventListener("mousemove", firstMoveListener, false);
div.addEventListener("mousemove", secondMoveListener, false);
@ -59,6 +60,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
}
var secondMoveListener = function (e) {
info("Got second mousemove");
totalMovementX = divCenterWidth + ((divCenterWidth / 2) * 3);
totalMovementY = divCenterHeight + ((divCenterHeight / 2) * 3);
@ -68,6 +70,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
document.addEventListener("mozpointerlockchange", function (e) {
if (document.mozPointerLockElement === div) {
info("Got mozpointerlockchange for entering");
div.addEventListener("mousemove", firstMoveListener, false);
divCenterWidth = Math.round(div.getBoundingClientRect().width / 2);
@ -76,20 +79,25 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
synthesizeMouse(div, divCenterWidth, divCenterHeight, {
type: "mousemove"
}, window);
} else {
info("Got mozpointerlockchange for exiting");
}
}, false);
document.addEventListener("mozfullscreenchange", function() {
if (document.mozFullScreenElement === div) {
info("Got mozfullscreenchange for entering");
div.mozRequestPointerLock();
}
else {
info("Got mozfullscreenchange for exiting");
runTests();
SimpleTest.finish();
}
}, false);
function start() {
info("Requesting fullscreen on parent");
div.mozRequestFullScreen();
}
</script>

Просмотреть файл

@ -57,6 +57,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
}
var moveMouse = function(e) {
info("Got mouse move");
mozMovementX = ("mozMovementX" in e);
mozMovementY = ("mozMovementY" in e);
@ -75,6 +76,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var moveMouseAgain = function(e) {
info("Got mouse move again");
secondMove.screenX = e.screenX;
secondMove.screenY = e.screenY;
secondMove.mozMovementX = e.mozMovementX;
@ -86,6 +88,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
function fullscreenchange() {
if (document.mozFullScreenElement === div) {
info("Got mozfullscreenchange for entering");
var screenX = window.screenX;
var screenY = window.screenY;
if (screenX != 0 || screenY != 0) {
@ -94,10 +97,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
setTimeout(fullscreenchange, 250);
return;
}
info("Finish waiting for fullscreenchange");
div.addEventListener("mousemove", moveMouse, false);
synthesizeMouseAtCenter(div, {type: "mousemove"}, window);
}
else {
info("Got mozfullscreenchange for exiting");
runTests();
SimpleTest.finish();
}
@ -106,6 +111,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
document.addEventListener("mozfullscreenchange", fullscreenchange, false);
function start() {
info("Requesting fullscreen on parent");
div.mozRequestFullScreen();
}
</script>

Просмотреть файл

@ -49,6 +49,7 @@
}
function mouseMoveHandler(e) {
info("Got mousemove");
document.removeEventListener("mousemove", mouseMoveHandler, false);
hasMovementX = "mozMovementX" in e;
@ -62,6 +63,7 @@
pointerLockChangeEventFired = true;
if (document.mozPointerLockElement) {
info("Got mozpointerlockchange for entering");
pointerLocked = true;
pointerLockElement = document.mozPointerLockElement === div;
@ -75,6 +77,7 @@
document.addEventListener("mousemove", mouseMoveHandler, false);
synthesizeMouseAtCenter(div, {type: "mousemove"}, window);
} else {
info("Got mozpointerlockchange for exiting");
pointerUnlocked = true;
document.mozCancelFullScreen();
}
@ -82,9 +85,11 @@
document.addEventListener("mozfullscreenchange", function(e) {
if (document.mozFullScreenElement === div) {
info("Got mozfullscreenchange for entering");
hasRequestPointerLock = "mozRequestPointerLock" in div;
div.mozRequestPointerLock();
} else {
info("Got mozfullscreenchange for exiting");
runTests();
SimpleTest.finish();
}
@ -92,6 +97,7 @@
function start() {
div = document.getElementById("div");
info("Requesting fullscreen on parent");
div.mozRequestFullScreen();
}
</script>

Просмотреть файл

@ -93,6 +93,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
// Event listeners for the parent element
var startMouseTests = function() {
info("Got parent mousemove");
parent.removeEventListener("mousemove", startMouseTests);
parent.addEventListener("DOMMouseScroll", parentScrollTest);
child.addEventListener("DOMMouseScroll", childScrollTest);
@ -103,6 +104,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var parentScrollTest = function (e) {
info("Got parent DOMMouseScroll");
parentStats.mouseScroll = true;
parent.removeEventListener("DOMMouseScroll", parentScrollTest);
child.removeEventListener("DOMMouseScroll", childScrollTest);
@ -115,6 +117,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var parentWheelTest = function (e) {
info("Got parent wheel");
parentStats.wheel = true;
parent.removeEventListener("wheel", parentWheelTest);
child.removeEventListener("wheel", childWheelTest);
@ -126,6 +129,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var parentDownTest = function (e) {
info("Got parent mousedown");
parentStats.mouseDown = true;
parent.removeEventListener("mousedown", parentDownTest);
child.removeEventListener("mousedown", childDownTest);
@ -137,6 +141,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var parentUpTest = function (e) {
info("Got parent mouseup");
parentStats.mouseUp = true;
parent.removeEventListener("mouseup", parentUpTest);
child.removeEventListener("mouseup", childUpTest);
@ -148,6 +153,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var parentClickTest = function (e) {
info("Got parent click");
parentStats.mouseClick = true;
parent.removeEventListener("click", parentClickTest);
child.removeEventListener("click", childClickTest);
@ -159,34 +165,42 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
};
var parentMoveTest = function (e) {
info("Got parent mousemove");
parentStats.mouseMove = true;
parent.removeEventListener("mousemove", parentMoveTest);
child.removeEventListener("mousemove", childMoveTest);
SimpleTest.executeSoon(function () {
info("Exit fullscreen");
document.mozCancelFullScreen();
});
}
document.addEventListener("mozpointerlockchange", function (e) {
if (document.mozPointerLockElement === parent) {
info("Got mozpointerlockchange for entering");
parent.addEventListener("mousemove", startMouseTests);
child.addEventListener("mousemove", childMoveTest);
SimpleTest.executeSoon(function () {
synthesizeMouseAtCenter(parent, {type: "mousemove"}, window);
});
} else {
info("Got mozpointerlockchange for exiting");
}
}, false);
document.addEventListener("mozfullscreenchange", function (e) {
if (document.mozFullScreenElement === parent) {
info("Got mozfullscreenchange for entering");
parent.mozRequestPointerLock();
} else {
info("Got mozfullscreenchange for exiting");
runTests();
SimpleTest.finish();
}
}, false);
function start() {
info("Requesting fullscreen on parent");
parent.mozRequestFullScreen();
}
</script>

Просмотреть файл

@ -49,6 +49,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
}
function moveUnlocked(e) {
info("Got mousemove via moveUnlocked");
var firstCall = !unLockedCoords;
if (!firstCall) {
todo(false, "mousemove is fired twice.");
@ -70,6 +71,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
}
function moveLocked(e) {
info("Got mousemove via moveLocked");
div.removeEventListener("mousemove", moveLocked, false);
isLocked = !!document.mozPointerLockElement;
@ -85,11 +87,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
document.addEventListener("mozpointerlockchange", function (e) {
if (document.mozPointerLockElement === div) {
info("Got mozpointerlockchange for entering");
div.removeEventListener("mousemove", moveUnlocked, false);
div.addEventListener("mousemove", moveLocked, false);
divRect = div.getBoundingClientRect();
synthesizeNativeMouseMove(div, (divRect.width / 4) * 3,
(divRect.height / 4) * 3);
} else {
info("Got mozpointerlockchange for exiting");
}
}, false);
@ -97,6 +102,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
var screenX = window.screenX;
var screenY = window.screenY;
if (document.mozFullScreenElement === div) {
info("Got mozfullscreenchange for entering");
if (screenX != 0 || screenY != 0) {
todo(screenX == 0 && screenY == 0,
"We should only receive fullscreenchange once we've finished fullscreen transition " +
@ -104,12 +110,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
setTimeout(fullscreenchange, 250);
return;
}
info("Finish waiting for fullscreenchange");
synthesizeNativeMouseMove(div, 0, 0, () => {
div.addEventListener("mousemove", moveUnlocked, false);
divRect = div.getBoundingClientRect();
synthesizeNativeMouseMove(div, divRect.width / 2, divRect.height / 2);
});
} else {
info("Got mozfullscreenchange for exiting");
runTests();
SimpleTest.finish();
}
@ -118,6 +126,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
function start() {
div = document.getElementById("div");
info("Requesting fullscreen on parent");
div.mozRequestFullScreen();
}
</script>

Просмотреть файл

@ -32,6 +32,10 @@ if (window.opener) {
opener.todo_isnot(a, b, testName + ": " + msg);
};
window.info = function(msg) {
opener.info(testName + ": " + msg);
};
// Override bits of SimpleTest so test files work stand-alone
var SimpleTest = SimpleTest || {};

Просмотреть файл

@ -29,4 +29,7 @@ interface Storage {
[Throws]
void clear();
[ChromeOnly]
readonly attribute boolean isSessionOnly;
};

Просмотреть файл

@ -383,7 +383,9 @@ public:
template<typename T>
Log &operator<<(Hexa<T> aHex) {
if (MOZ_UNLIKELY(LogIt())) {
mMessage << "0x" << std::hex << aHex.mVal << std::dec;
mMessage << std::showbase << std::hex
<< aHex.mVal
<< std::noshowbase << std::dec;
}
return *this;
}

Просмотреть файл

@ -357,6 +357,28 @@ DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
return 4;
}
gfx::IntRect
Compositor::ComputeBackdropCopyRect(const gfx::Rect& aRect,
const gfx::Rect& aClipRect,
const gfx::Matrix4x4& aTransform)
{
gfx::Rect renderBounds = mRenderBounds;
// Compute the clip.
renderBounds.IntersectRect(renderBounds, aClipRect);
// Apply the layer transform.
gfx::Rect dest = aTransform.TransformAndClipBounds(aRect, renderBounds);
dest.RoundOut();
gfx::IntRect result;
dest.ToIntRect(&result);
gfx::IntPoint offset = GetCurrentRenderTarget()->GetOrigin();
result.MoveBy(-offset);
return result;
}
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
void
Compositor::SetDispAcquireFence(Layer* aLayer, nsIWidget* aWidget)

Просмотреть файл

@ -119,6 +119,7 @@ namespace layers {
struct Effect;
struct EffectChain;
class Image;
class ImageHostOverlay;
class Layer;
class TextureSource;
class DataTextureSource;
@ -453,6 +454,12 @@ public:
// these methods properly.
virtual nsIWidget* GetWidget() const { return nullptr; }
virtual bool HasImageHostOverlays() { return false; }
virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) {}
virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) {}
/**
* Debug-build assertion that can be called to ensure code is running on the
* compositor thread.
@ -507,6 +514,16 @@ protected:
bool ShouldDrawDiagnostics(DiagnosticFlags);
/**
* Given a layer rect, clip, and transform, compute the area of the backdrop that
* needs to be copied for mix-blending. The output transform translates from 0..1
* space into the backdrop rect space.
*/
gfx::IntRect ComputeBackdropCopyRect(
const gfx::Rect& aRect,
const gfx::Rect& aClipRect,
const gfx::Matrix4x4& aTransform);
/**
* Render time for the current composition.
*/
@ -535,6 +552,8 @@ protected:
RefPtr<gfx::DrawTarget> mTarget;
gfx::IntRect mTargetBounds;
gfx::Rect mRenderBounds;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
FenceHandle mReleaseFenceHandle;
#endif
@ -551,6 +570,31 @@ size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
decomposedRectArrayT* aLayerRects,
decomposedRectArrayT* aTextureRects);
static inline bool
BlendOpIsMixBlendMode(gfx::CompositionOp aOp)
{
switch (aOp) {
case gfx::CompositionOp::OP_MULTIPLY:
case gfx::CompositionOp::OP_SCREEN:
case gfx::CompositionOp::OP_OVERLAY:
case gfx::CompositionOp::OP_DARKEN:
case gfx::CompositionOp::OP_LIGHTEN:
case gfx::CompositionOp::OP_COLOR_DODGE:
case gfx::CompositionOp::OP_COLOR_BURN:
case gfx::CompositionOp::OP_HARD_LIGHT:
case gfx::CompositionOp::OP_SOFT_LIGHT:
case gfx::CompositionOp::OP_DIFFERENCE:
case gfx::CompositionOp::OP_EXCLUSION:
case gfx::CompositionOp::OP_HUE:
case gfx::CompositionOp::OP_SATURATION:
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
return true;
default:
return false;
}
}
} // namespace layers
} // namespace mozilla

Просмотреть файл

@ -2762,7 +2762,7 @@ void AsyncPanZoomController::FlushRepaintForNewInputBlock() {
APZC_LOG("%p flushing repaint for new input block\n", this);
ReentrantMonitorAutoEnter lock(mMonitor);
RequestContentRepaint(mFrameMetrics);
RequestContentRepaint();
UpdateSharedCompositorFrameMetrics();
}
@ -2805,14 +2805,44 @@ int32_t AsyncPanZoomController::GetLastTouchIdentifier() const {
}
void AsyncPanZoomController::RequestContentRepaint() {
RequestContentRepaint(mFrameMetrics);
// Reinvoke this method on the main thread if it's not there already. It's
// important to do this before the call to CalculatePendingDisplayPort, so
// that CalculatePendingDisplayPort uses the most recent available version of
// mFrameMetrics, just before the paint request is dispatched to content.
if (!NS_IsMainThread()) {
// use the local variable to resolve the function overload.
auto func = static_cast<void (AsyncPanZoomController::*)()>
(&AsyncPanZoomController::RequestContentRepaint);
NS_DispatchToMainThread(NS_NewRunnableMethod(this, func));
return;
}
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter lock(mMonitor);
ParentLayerPoint velocity = GetVelocityVector();
mFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(mFrameMetrics, velocity));
mFrameMetrics.SetUseDisplayPortMargins(true);
mFrameMetrics.SetPaintRequestTime(TimeStamp::Now());
RequestContentRepaint(mFrameMetrics, velocity);
}
void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics)
/*static*/ CSSRect
GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
{
ParentLayerPoint velocity = GetVelocityVector();
aFrameMetrics.SetDisplayPortMargins(CalculatePendingDisplayPort(aFrameMetrics, velocity));
aFrameMetrics.SetUseDisplayPortMargins(true);
// This computation is based on what happens in CalculatePendingDisplayPort. If that
// changes then this might need to change too
CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.DisplayportPixelsPerCSSPixel());
return baseRect;
}
void
AsyncPanZoomController::RequestContentRepaint(const FrameMetrics& aFrameMetrics,
const ParentLayerPoint& aVelocity)
{
MOZ_ASSERT(NS_IsMainThread());
// If we're trying to paint what we already think is painted, discard this
// request since it's a pointless paint.
@ -2835,26 +2865,6 @@ void AsyncPanZoomController::RequestContentRepaint(FrameMetrics& aFrameMetrics)
return;
}
aFrameMetrics.SetPaintRequestTime(TimeStamp::Now());
DispatchRepaintRequest(aFrameMetrics, velocity);
aFrameMetrics.SetPresShellId(mLastContentPaintMetrics.GetPresShellId());
}
/*static*/ CSSRect
GetDisplayPortRect(const FrameMetrics& aFrameMetrics)
{
// This computation is based on what happens in CalculatePendingDisplayPort. If that
// changes then this might need to change too
CSSRect baseRect(aFrameMetrics.GetScrollOffset(),
aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels());
baseRect.Inflate(aFrameMetrics.GetDisplayPortMargins() / aFrameMetrics.DisplayportPixelsPerCSSPixel());
return baseRect;
}
void
AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics,
const ParentLayerPoint& aVelocity)
{
RefPtr<GeckoContentController> controller = GetGeckoContentController();
if (!controller) {
return;
@ -2870,12 +2880,7 @@ AsyncPanZoomController::DispatchRepaintRequest(const FrameMetrics& aFrameMetrics
str);
}
if (NS_IsMainThread()) {
controller->RequestContentRepaint(aFrameMetrics);
} else {
NS_DispatchToMainThread(NS_NewRunnableMethodWithArg<FrameMetrics>(
controller, &GeckoContentController::RequestContentRepaint, aFrameMetrics));
}
controller->RequestContentRepaint(aFrameMetrics);
mExpectedGeckoMetrics = aFrameMetrics;
mLastPaintRequestMetrics = aFrameMetrics;
}
@ -3460,9 +3465,6 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
}
endZoomToMetrics.SetScrollOffset(aRect.TopLeft());
endZoomToMetrics.SetDisplayPortMargins(
CalculatePendingDisplayPort(endZoomToMetrics, ParentLayerPoint(0,0)));
endZoomToMetrics.SetUseDisplayPortMargins(true);
StartAnimation(new ZoomAnimation(
mFrameMetrics.GetScrollOffset(),
@ -3472,7 +3474,21 @@ void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
// Schedule a repaint now, so the new displayport will be painted before the
// animation finishes.
RequestContentRepaint(endZoomToMetrics);
ParentLayerPoint velocity(0, 0);
endZoomToMetrics.SetDisplayPortMargins(
CalculatePendingDisplayPort(endZoomToMetrics, velocity));
endZoomToMetrics.SetUseDisplayPortMargins(true);
endZoomToMetrics.SetPaintRequestTime(TimeStamp::Now());
if (NS_IsMainThread()) {
RequestContentRepaint(endZoomToMetrics, velocity);
} else {
// use a local var to resolve the function overload
auto func = static_cast<void (AsyncPanZoomController::*)(const FrameMetrics&, const ParentLayerPoint&)>
(&AsyncPanZoomController::RequestContentRepaint);
NS_DispatchToMainThread(
NS_NewRunnableMethodWithArgs<FrameMetrics, ParentLayerPoint>(
this, func, endZoomToMetrics, velocity));
}
}
}

Просмотреть файл

@ -577,27 +577,19 @@ protected:
/**
* Utility function to send updated FrameMetrics to Gecko so that it can paint
* the displayport area. Calls into GeckoContentController to do the actual
* work. Note that only one paint request can be active at a time. If a paint
* request is made while a paint is currently happening, it gets queued up. If
* a new paint request arrives before a paint is completed, the old request
* gets discarded.
* work. This call will use the current metrics. If this function is called
* from a non-main thread, it will redispatch itself to the main thread, and
* use the latest metrics during the redispatch.
*/
void RequestContentRepaint();
/**
* Tell the paint throttler to request a content repaint with the given
* metrics. (Helper function used by RequestContentRepaint.) If aThrottled
* is set to false, the repaint request is sent directly without going through
* the paint throttler. In particular, the GeckoContentController::RequestContentRepaint
* function will be invoked before this function returns.
* Send the provided metrics to Gecko to trigger a repaint. This function
* may filter duplicate calls with the same metrics. This function must be
* called on the main thread.
*/
void RequestContentRepaint(FrameMetrics& aFrameMetrics);
/**
* Actually send the next pending paint request to gecko.
*/
void DispatchRepaintRequest(const FrameMetrics& aFrameMetrics,
const ParentLayerPoint& aVelocity);
void RequestContentRepaint(const FrameMetrics& aFrameMetrics,
const ParentLayerPoint& aVelocity);
/**
* Gets the current frame metrics. This is *not* the Gecko copy stored in the

Просмотреть файл

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_APZCBasicTester_h
#define mozilla_layers_APZCBasicTester_h
/**
* Defines a test fixture used for testing a single APZC.
*/
#include "APZTestCommon.h"
class APZCBasicTester : public ::testing::Test {
public:
explicit APZCBasicTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
: mGestureBehavior(aGestureBehavior)
{
}
protected:
virtual void SetUp()
{
gfxPrefs::GetSingleton();
APZThreadUtils::SetThreadAssertionsEnabled(false);
APZThreadUtils::SetControllerThread(MessageLoop::current());
mcc = new NiceMock<MockContentControllerDelayed>();
tm = new TestAPZCTreeManager(mcc);
apzc = new TestAsyncPanZoomController(0, mcc, tm, mGestureBehavior);
apzc->SetFrameMetrics(TestFrameMetrics());
}
/**
* Get the APZC's scroll range in CSS pixels.
*/
CSSRect GetScrollRange() const
{
const FrameMetrics& metrics = apzc->GetFrameMetrics();
return CSSRect(
metrics.GetScrollableRect().TopLeft(),
metrics.GetScrollableRect().Size() - metrics.CalculateCompositedSizeInCssPixels());
}
virtual void TearDown()
{
while (mcc->RunThroughDelayedTasks());
apzc->Destroy();
}
void MakeApzcWaitForMainThread()
{
apzc->SetWaitForMainThread();
}
void MakeApzcZoomable()
{
apzc->UpdateZoomConstraints(ZoomConstraints(true, true, CSSToParentLayerScale(0.25f), CSSToParentLayerScale(4.0f)));
}
void MakeApzcUnzoomable()
{
apzc->UpdateZoomConstraints(ZoomConstraints(false, false, CSSToParentLayerScale(1.0f), CSSToParentLayerScale(1.0f)));
}
void PanIntoOverscroll();
/**
* Sample animations once, 1 ms later than the last sample.
*/
void SampleAnimationOnce()
{
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
mcc->AdvanceBy(increment);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
}
/**
* Sample animations until we recover from overscroll.
* @param aExpectedScrollOffset the expected reported scroll offset
* throughout the animation
*/
void SampleAnimationUntilRecoveredFromOverscroll(const ParentLayerPoint& aExpectedScrollOffset)
{
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
bool recoveredFromOverscroll = false;
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
while (apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut)) {
// The reported scroll offset should be the same throughout.
EXPECT_EQ(aExpectedScrollOffset, pointOut);
// Trigger computation of the overscroll tranform, to make sure
// no assetions fire during the calculation.
apzc->GetOverscrollTransform();
if (!apzc->IsOverscrolled()) {
recoveredFromOverscroll = true;
}
mcc->AdvanceBy(increment);
}
EXPECT_TRUE(recoveredFromOverscroll);
apzc->AssertStateIsReset();
}
void TestOverscroll();
AsyncPanZoomController::GestureBehavior mGestureBehavior;
RefPtr<MockContentControllerDelayed> mcc;
RefPtr<TestAPZCTreeManager> tm;
RefPtr<TestAsyncPanZoomController> apzc;
};
#endif // mozilla_layers_APZCBasicTester_h

Просмотреть файл

@ -0,0 +1,167 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_APZCTreeManagerTester_h
#define mozilla_layers_APZCTreeManagerTester_h
/**
* Defines a test fixture used for testing multiple APZCs interacting in
* an APZCTreeManager.
*/
#include "APZTestCommon.h"
class APZCTreeManagerTester : public ::testing::Test {
protected:
virtual void SetUp() {
gfxPrefs::GetSingleton();
APZThreadUtils::SetThreadAssertionsEnabled(false);
APZThreadUtils::SetControllerThread(MessageLoop::current());
mcc = new NiceMock<MockContentControllerDelayed>();
manager = new TestAPZCTreeManager(mcc);
}
virtual void TearDown() {
while (mcc->RunThroughDelayedTasks());
manager->ClearTree();
}
/**
* Sample animations once for all APZCs, 1 ms later than the last sample.
*/
void SampleAnimationsOnce() {
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
mcc->AdvanceBy(increment);
for (const RefPtr<Layer>& layer : layers) {
if (TestAsyncPanZoomController* apzc = ApzcOf(layer)) {
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
}
}
}
RefPtr<MockContentControllerDelayed> mcc;
nsTArray<RefPtr<Layer> > layers;
RefPtr<LayerManager> lm;
RefPtr<Layer> root;
RefPtr<TestAPZCTreeManager> manager;
protected:
static void SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId,
CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) {
FrameMetrics metrics;
metrics.SetScrollId(aScrollId);
// By convention in this test file, START_SCROLL_ID is the root, so mark it as such.
if (aScrollId == FrameMetrics::START_SCROLL_ID) {
metrics.SetIsLayersIdRoot(true);
}
IntRect layerBound = aLayer->GetVisibleRegion().ToUnknownRegion().GetBounds();
metrics.SetCompositionBounds(ParentLayerRect(layerBound.x, layerBound.y,
layerBound.width, layerBound.height));
metrics.SetScrollableRect(aScrollableRect);
metrics.SetScrollOffset(CSSPoint(0, 0));
metrics.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
metrics.SetAllowVerticalScrollWithWheel(true);
aLayer->SetFrameMetrics(metrics);
aLayer->SetClipRect(Some(ViewAs<ParentLayerPixel>(layerBound)));
if (!aScrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
// The purpose of this is to roughly mimic what layout would do in the
// case of a scrollable frame with the event regions and clip. This lets
// us exercise the hit-testing code in APZCTreeManager
EventRegions er = aLayer->GetEventRegions();
IntRect scrollRect = RoundedToInt(aScrollableRect * metrics.LayersPixelsPerCSSPixel()).ToUnknownRect();
er.mHitRegion = nsIntRegion(IntRect(layerBound.TopLeft(), scrollRect.Size()));
aLayer->SetEventRegions(er);
}
}
void SetScrollHandoff(Layer* aChild, Layer* aParent) {
FrameMetrics metrics = aChild->GetFrameMetrics(0);
metrics.SetScrollParentId(aParent->GetFrameMetrics(0).GetScrollId());
aChild->SetFrameMetrics(metrics);
}
static TestAsyncPanZoomController* ApzcOf(Layer* aLayer) {
EXPECT_EQ(1u, aLayer->GetFrameMetricsCount());
return (TestAsyncPanZoomController*)aLayer->GetAsyncPanZoomController(0);
}
void CreateSimpleScrollingLayer() {
const char* layerTreeSyntax = "t";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,200,200)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
}
void CreateSimpleDTCScrollingLayer() {
const char* layerTreeSyntax = "t";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,200,200)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 500, 500));
EventRegions regions;
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 200));
regions.mDispatchToContentHitRegion = regions.mHitRegion;
layers[0]->SetEventRegions(regions);
}
void CreateSimpleMultiLayerTree() {
const char* layerTreeSyntax = "c(tt)";
// LayerID 0 12
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,100,100)),
nsIntRegion(IntRect(0,0,100,50)),
nsIntRegion(IntRect(0,50,100,50)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
}
void CreatePotentiallyLeakingTree() {
const char* layerTreeSyntax = "c(c(c(t))c(c(t)))";
// LayerID 0 1 2 3 4 5 6
root = CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
}
void CreateBug1194876Tree() {
const char* layerTreeSyntax = "c(t)";
// LayerID 0 1
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,100,100)),
nsIntRegion(IntRect(0,0,100,100)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
// Make layers[1] the root content
FrameMetrics childMetrics = layers[1]->GetFrameMetrics(0);
childMetrics.SetIsRootContent(true);
layers[1]->SetFrameMetrics(childMetrics);
// Both layers are fully dispatch-to-content
EventRegions regions;
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
regions.mDispatchToContentHitRegion = regions.mHitRegion;
layers[0]->SetEventRegions(regions);
layers[1]->SetEventRegions(regions);
}
};
#endif // mozilla_layers_APZCTreeManagerTester_h

Просмотреть файл

@ -0,0 +1,297 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_APZTestCommon_h
#define mozilla_layers_APZTestCommon_h
/**
* Defines a set of mock classes and utility functions/classes for
* writing APZ gtests.
*/
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "mozilla/Attributes.h"
#include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
#include "mozilla/layers/GeckoContentController.h"
#include "mozilla/layers/CompositorParent.h"
#include "mozilla/layers/APZCTreeManager.h"
#include "mozilla/layers/LayerMetricsWrapper.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/UniquePtr.h"
#include "apz/src/AsyncPanZoomController.h"
#include "apz/src/HitTestingTreeNode.h"
#include "base/task.h"
#include "Layers.h"
#include "TestLayers.h"
#include "UnitTransforms.h"
#include "gfxPrefs.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using ::testing::_;
using ::testing::NiceMock;
using ::testing::AtLeast;
using ::testing::AtMost;
using ::testing::MockFunction;
using ::testing::InSequence;
class Task;
template<class T>
class ScopedGfxPref {
public:
ScopedGfxPref(T (*aGetPrefFunc)(void), void (*aSetPrefFunc)(T), T aVal)
: mSetPrefFunc(aSetPrefFunc)
{
mOldVal = aGetPrefFunc();
aSetPrefFunc(aVal);
}
~ScopedGfxPref() {
mSetPrefFunc(mOldVal);
}
private:
void (*mSetPrefFunc)(T);
T mOldVal;
};
#define SCOPED_GFX_PREF(prefBase, prefType, prefValue) \
ScopedGfxPref<prefType> pref_##prefBase( \
&(gfxPrefs::prefBase), \
&(gfxPrefs::Set##prefBase), \
prefValue)
class MockContentController : public GeckoContentController {
public:
MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
MOCK_METHOD2(RequestFlingSnap, void(const FrameMetrics::ViewID& aScrollId, const mozilla::CSSPoint& aDestination));
MOCK_METHOD2(AcknowledgeScrollUpdate, void(const FrameMetrics::ViewID&, const uint32_t& aScrollGeneration));
MOCK_METHOD3(HandleDoubleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&));
MOCK_METHOD3(HandleSingleTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&));
MOCK_METHOD4(HandleLongTap, void(const CSSPoint&, Modifiers, const ScrollableLayerGuid&, uint64_t));
MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg));
MOCK_METHOD0(NotifyFlushComplete, void());
};
class MockContentControllerDelayed : public MockContentController {
public:
MockContentControllerDelayed()
: mTime(TimeStamp::Now())
{
}
const TimeStamp& Time() {
return mTime;
}
void AdvanceByMillis(int aMillis) {
AdvanceBy(TimeDuration::FromMilliseconds(aMillis));
}
void AdvanceBy(const TimeDuration& aIncrement) {
TimeStamp target = mTime + aIncrement;
while (mTaskQueue.Length() > 0 && mTaskQueue[0].second <= target) {
RunNextDelayedTask();
}
mTime = target;
}
void PostDelayedTask(Task* aTask, int aDelayMs) {
TimeStamp runAtTime = mTime + TimeDuration::FromMilliseconds(aDelayMs);
int insIndex = mTaskQueue.Length();
while (insIndex > 0) {
if (mTaskQueue[insIndex - 1].second <= runAtTime) {
break;
}
insIndex--;
}
mTaskQueue.InsertElementAt(insIndex, std::make_pair(aTask, runAtTime));
}
// Run all the tasks in the queue, returning the number of tasks
// run. Note that if a task queues another task while running, that
// new task will not be run. Therefore, there may be still be tasks
// in the queue after this function is called. Only when the return
// value is 0 is the queue guaranteed to be empty.
int RunThroughDelayedTasks() {
nsTArray<std::pair<Task*, TimeStamp>> runQueue;
runQueue.SwapElements(mTaskQueue);
int numTasks = runQueue.Length();
for (int i = 0; i < numTasks; i++) {
mTime = runQueue[i].second;
runQueue[i].first->Run();
// Deleting the task is important in order to release the reference to
// the callee object.
delete runQueue[i].first;
}
return numTasks;
}
private:
void RunNextDelayedTask() {
std::pair<Task*, TimeStamp> next = mTaskQueue[0];
mTaskQueue.RemoveElementAt(0);
mTime = next.second;
next.first->Run();
// Deleting the task is important in order to release the reference to
// the callee object.
delete next.first;
}
// The following array is sorted by timestamp (tasks are inserted in order by
// timestamp).
nsTArray<std::pair<Task*, TimeStamp>> mTaskQueue;
TimeStamp mTime;
};
class TestAPZCTreeManager : public APZCTreeManager {
public:
explicit TestAPZCTreeManager(MockContentControllerDelayed* aMcc) : mcc(aMcc) {}
RefPtr<InputQueue> GetInputQueue() const {
return mInputQueue;
}
protected:
AsyncPanZoomController* NewAPZCInstance(uint64_t aLayersId,
GeckoContentController* aController) override;
TimeStamp GetFrameTime() override {
return mcc->Time();
}
private:
RefPtr<MockContentControllerDelayed> mcc;
};
class TestAsyncPanZoomController : public AsyncPanZoomController {
public:
TestAsyncPanZoomController(uint64_t aLayersId, MockContentControllerDelayed* aMcc,
TestAPZCTreeManager* aTreeManager,
GestureBehavior aBehavior = DEFAULT_GESTURES)
: AsyncPanZoomController(aLayersId, aTreeManager, aTreeManager->GetInputQueue(),
aMcc, aBehavior)
, mWaitForMainThread(false)
, mcc(aMcc)
{}
nsEventStatus ReceiveInputEvent(const InputData& aEvent, ScrollableLayerGuid* aDummy, uint64_t* aOutInputBlockId) {
// This is a function whose signature matches exactly the ReceiveInputEvent
// on APZCTreeManager. This allows us to templates for functions like
// TouchDown, TouchUp, etc so that we can reuse the code for dispatching
// events into both APZC and APZCTM.
return ReceiveInputEvent(aEvent, aOutInputBlockId);
}
nsEventStatus ReceiveInputEvent(const InputData& aEvent, uint64_t* aOutInputBlockId) {
return GetInputQueue()->ReceiveInputEvent(this, !mWaitForMainThread, aEvent, aOutInputBlockId);
}
void ContentReceivedInputBlock(uint64_t aInputBlockId, bool aPreventDefault) {
GetInputQueue()->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
}
void ConfirmTarget(uint64_t aInputBlockId) {
RefPtr<AsyncPanZoomController> target = this;
GetInputQueue()->SetConfirmedTargetApzc(aInputBlockId, target);
}
void SetAllowedTouchBehavior(uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aBehaviors) {
GetInputQueue()->SetAllowedTouchBehavior(aInputBlockId, aBehaviors);
}
void SetFrameMetrics(const FrameMetrics& metrics) {
ReentrantMonitorAutoEnter lock(mMonitor);
mFrameMetrics = metrics;
}
FrameMetrics& GetFrameMetrics() {
ReentrantMonitorAutoEnter lock(mMonitor);
return mFrameMetrics;
}
const FrameMetrics& GetFrameMetrics() const {
ReentrantMonitorAutoEnter lock(mMonitor);
return mFrameMetrics;
}
using AsyncPanZoomController::GetVelocityVector;
void AssertStateIsReset() const {
ReentrantMonitorAutoEnter lock(mMonitor);
EXPECT_EQ(NOTHING, mState);
}
void AssertStateIsFling() const {
ReentrantMonitorAutoEnter lock(mMonitor);
EXPECT_EQ(FLING, mState);
}
void AdvanceAnimationsUntilEnd(const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(10)) {
while (AdvanceAnimations(mcc->Time())) {
mcc->AdvanceBy(aIncrement);
}
}
bool SampleContentTransformForFrame(AsyncTransform* aOutTransform,
ParentLayerPoint& aScrollOffset,
const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) {
mcc->AdvanceBy(aIncrement);
bool ret = AdvanceAnimations(mcc->Time());
AsyncPanZoomController::SampleContentTransformForFrame(
aOutTransform, aScrollOffset);
return ret;
}
void SetWaitForMainThread() {
mWaitForMainThread = true;
}
static TimeStamp GetStartupTime() {
static TimeStamp sStartupTime = TimeStamp::Now();
return sStartupTime;
}
private:
bool mWaitForMainThread;
MockContentControllerDelayed* mcc;
};
AsyncPanZoomController*
TestAPZCTreeManager::NewAPZCInstance(uint64_t aLayersId,
GeckoContentController* aController)
{
MockContentControllerDelayed* mcc = static_cast<MockContentControllerDelayed*>(aController);
return new TestAsyncPanZoomController(aLayersId, mcc, this,
AsyncPanZoomController::USE_GESTURE_DETECTOR);
}
FrameMetrics
TestFrameMetrics()
{
FrameMetrics fm;
fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10));
fm.SetCriticalDisplayPort(CSSRect(0, 0, 10, 10));
fm.SetScrollableRect(CSSRect(0, 0, 100, 100));
return fm;
}
uint32_t
MillisecondsSinceStartup(TimeStamp aTime)
{
return (aTime - TestAsyncPanZoomController::GetStartupTime()).ToMilliseconds();
}
#endif // mozilla_layers_APZTestCommon_h

Просмотреть файл

@ -0,0 +1,466 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_layers_InputUtils_h
#define mozilla_layers_InputUtils_h
/**
* Defines a set of utility functions for generating input events
* to an APZC/APZCTM during APZ gtests.
*/
#include "APZTestCommon.h"
/* The InputReceiver template parameter used in the helper functions below needs
* to be a class that implements functions with the signatures:
* nsEventStatus ReceiveInputEvent(const InputData& aEvent,
* ScrollableLayerGuid* aGuid,
* uint64_t* aOutInputBlockId);
* void SetAllowedTouchBehavior(uint64_t aInputBlockId,
* const nsTArray<uint32_t>& aBehaviours);
* The classes that currently implement these are APZCTreeManager and
* TestAsyncPanZoomController. Using this template allows us to test individual
* APZC instances in isolation and also an entire APZ tree, while using the same
* code to dispatch input events.
*/
// Some helper functions for constructing input event objects suitable to be
// passed either to an APZC (which expects an transformed point), or to an APZTM
// (which expects an untransformed point). We handle both cases by setting both
// the transformed and untransformed fields to the same value.
SingleTouchData
CreateSingleTouchData(int32_t aIdentifier, int aX, int aY)
{
SingleTouchData touch(aIdentifier, ScreenIntPoint(aX, aY), ScreenSize(0, 0), 0, 0);
touch.mLocalScreenPoint = ParentLayerPoint(aX, aY);
return touch;
}
PinchGestureInput
CreatePinchGestureInput(PinchGestureInput::PinchGestureType aType,
int aFocusX, int aFocusY,
float aCurrentSpan, float aPreviousSpan)
{
PinchGestureInput result(aType, 0, TimeStamp(), ScreenPoint(aFocusX, aFocusY),
aCurrentSpan, aPreviousSpan, 0);
result.mLocalFocusPoint = ParentLayerPoint(aFocusX, aFocusY);
return result;
}
template<class InputReceiver>
void
SetDefaultAllowedTouchBehavior(const RefPtr<InputReceiver>& aTarget,
uint64_t aInputBlockId,
int touchPoints = 1)
{
nsTArray<uint32_t> defaultBehaviors;
// use the default value where everything is allowed
for (int i = 0; i < touchPoints; i++) {
defaultBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM
| mozilla::layers::AllowedTouchBehavior::DOUBLE_TAP_ZOOM);
}
aTarget->SetAllowedTouchBehavior(aInputBlockId, defaultBehaviors);
}
MultiTouchInput
CreateMultiTouchInput(MultiTouchInput::MultiTouchType aType, TimeStamp aTime)
{
return MultiTouchInput(aType, MillisecondsSinceStartup(aTime), aTime, 0);
}
template<class InputReceiver>
nsEventStatus
TouchDown(const RefPtr<InputReceiver>& aTarget, int aX, int aY, TimeStamp aTime, uint64_t* aOutInputBlockId = nullptr)
{
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime);
mti.mTouches.AppendElement(CreateSingleTouchData(0, aX, aY));
return aTarget->ReceiveInputEvent(mti, nullptr, aOutInputBlockId);
}
template<class InputReceiver>
nsEventStatus
TouchMove(const RefPtr<InputReceiver>& aTarget, int aX, int aY, TimeStamp aTime)
{
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime);
mti.mTouches.AppendElement(CreateSingleTouchData(0, aX, aY));
return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
}
template<class InputReceiver>
nsEventStatus
TouchUp(const RefPtr<InputReceiver>& aTarget, int aX, int aY, TimeStamp aTime)
{
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime);
mti.mTouches.AppendElement(CreateSingleTouchData(0, aX, aY));
return aTarget->ReceiveInputEvent(mti, nullptr, nullptr);
}
template<class InputReceiver>
void
Tap(const RefPtr<InputReceiver>& aTarget, int aX, int aY, MockContentControllerDelayed* aMcc,
TimeDuration aTapLength,
nsEventStatus (*aOutEventStatuses)[2] = nullptr,
uint64_t* aOutInputBlockId = nullptr)
{
// Even if the caller doesn't care about the block id, we need it to set the
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
uint64_t blockId;
if (!aOutInputBlockId) {
aOutInputBlockId = &blockId;
}
nsEventStatus status = TouchDown(aTarget, aX, aY, aMcc->Time(), aOutInputBlockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
aMcc->AdvanceBy(aTapLength);
// If touch-action is enabled then simulate the allowed touch behaviour
// notification that the main thread is supposed to deliver.
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
}
status = TouchUp(aTarget, aX, aY, aMcc->Time());
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
}
template<class InputReceiver>
void
TapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aX, int aY,
MockContentControllerDelayed* aMcc, TimeDuration aTapLength)
{
nsEventStatus statuses[2];
Tap(aTarget, aX, aY, aMcc, aTapLength, &statuses);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
}
template<class InputReceiver>
void
Pan(const RefPtr<InputReceiver>& aTarget,
MockContentControllerDelayed* aMcc,
const ScreenPoint& aTouchStart,
const ScreenPoint& aTouchEnd,
bool aKeepFingerDown = false,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t* aOutInputBlockId = nullptr)
{
// Reduce the touch start and move tolerance to a tiny value.
// We can't use a scoped pref because this value might be read at some later
// time when the events are actually processed, rather than when we deliver
// them.
gfxPrefs::SetAPZTouchStartTolerance(1.0f / 1000.0f);
gfxPrefs::SetAPZTouchMoveTolerance(0.0f);
const int OVERCOME_TOUCH_TOLERANCE = 1;
const TimeDuration TIME_BETWEEN_TOUCH_EVENT = TimeDuration::FromMilliseconds(50);
// Even if the caller doesn't care about the block id, we need it to set the
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
uint64_t blockId;
if (!aOutInputBlockId) {
aOutInputBlockId = &blockId;
}
// Make sure the move is large enough to not be handled as a tap
nsEventStatus status = TouchDown(aTarget, aTouchStart.x, aTouchStart.y + OVERCOME_TOUCH_TOLERANCE, aMcc->Time(), aOutInputBlockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
aMcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
// Allowed touch behaviours must be set after sending touch-start.
if (status != nsEventStatus_eConsumeNoDefault) {
if (aAllowedTouchBehaviors) {
EXPECT_EQ(1UL, aAllowedTouchBehaviors->Length());
aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
} else if (gfxPrefs::TouchActionEnabled()) {
SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId);
}
}
status = TouchMove(aTarget, aTouchStart.x, aTouchStart.y, aMcc->Time());
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
aMcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
status = TouchMove(aTarget, aTouchEnd.x, aTouchEnd.y, aMcc->Time());
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = status;
}
aMcc->AdvanceBy(TIME_BETWEEN_TOUCH_EVENT);
if (!aKeepFingerDown) {
status = TouchUp(aTarget, aTouchEnd.x, aTouchEnd.y, aMcc->Time());
} else {
status = nsEventStatus_eIgnore;
}
if (aOutEventStatuses) {
(*aOutEventStatuses)[3] = status;
}
// Don't increment the time here. Animations started on touch-up, such as
// flings, are affected by elapsed time, and we want to be able to sample
// them immediately after they start, without time having elapsed.
}
// A version of Pan() that only takes y coordinates rather than (x, y) points
// for the touch start and end points, and uses 10 for the x coordinates.
// This is for convenience, as most tests only need to pan in one direction.
template<class InputReceiver>
void
Pan(const RefPtr<InputReceiver>& aTarget,
MockContentControllerDelayed* aMcc,
int aTouchStartY,
int aTouchEndY,
bool aKeepFingerDown = false,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t* aOutInputBlockId = nullptr)
{
::Pan(aTarget, aMcc, ScreenPoint(10, aTouchStartY), ScreenPoint(10, aTouchEndY),
aKeepFingerDown, aAllowedTouchBehaviors, aOutEventStatuses, aOutInputBlockId);
}
/*
* Dispatches mock touch events to the apzc and checks whether apzc properly
* consumed them and triggered scrolling behavior.
*/
template<class InputReceiver>
void
PanAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
MockContentControllerDelayed* aMcc,
int aTouchStartY,
int aTouchEndY,
bool aExpectConsumed,
nsTArray<uint32_t>* aAllowedTouchBehaviors,
uint64_t* aOutInputBlockId = nullptr)
{
nsEventStatus statuses[4]; // down, move, move, up
Pan(aTarget, aMcc, aTouchStartY, aTouchEndY, false, aAllowedTouchBehaviors, &statuses, aOutInputBlockId);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
nsEventStatus touchMoveStatus;
if (aExpectConsumed) {
touchMoveStatus = nsEventStatus_eConsumeDoDefault;
} else {
touchMoveStatus = nsEventStatus_eIgnore;
}
EXPECT_EQ(touchMoveStatus, statuses[1]);
EXPECT_EQ(touchMoveStatus, statuses[2]);
}
void
ApzcPanNoFling(const RefPtr<TestAsyncPanZoomController>& aApzc,
MockContentControllerDelayed* aMcc,
int aTouchStartY,
int aTouchEndY,
uint64_t* aOutInputBlockId = nullptr)
{
Pan(aApzc, aMcc, aTouchStartY, aTouchEndY, false, nullptr, nullptr, aOutInputBlockId);
aApzc->CancelAnimation();
}
template<class InputReceiver>
void
PinchWithPinchInput(const RefPtr<InputReceiver>& aTarget,
int aFocusX, int aFocusY, int aSecondFocusX, int aSecondFocusY, float aScale,
nsEventStatus (*aOutEventStatuses)[3] = nullptr)
{
nsEventStatus actualStatus = aTarget->ReceiveInputEvent(
CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_START,
aFocusX, aFocusY, 10.0, 10.0),
nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = actualStatus;
}
actualStatus = aTarget->ReceiveInputEvent(
CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_SCALE,
aSecondFocusX, aSecondFocusY, 10.0 * aScale, 10.0),
nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = actualStatus;
}
actualStatus = aTarget->ReceiveInputEvent(
CreatePinchGestureInput(PinchGestureInput::PINCHGESTURE_END,
// note: negative values here tell APZC
// not to turn the pinch into a pan
aFocusX, aFocusY, -1.0, -1.0),
nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = actualStatus;
}
}
template<class InputReceiver>
void
PinchWithPinchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
int aFocusX, int aFocusY, float aScale,
bool aShouldTriggerPinch)
{
nsEventStatus statuses[3]; // scalebegin, scale, scaleend
PinchWithPinchInput(aTarget, aFocusX, aFocusY, aFocusX, aFocusY, aScale, &statuses);
nsEventStatus expectedStatus = aShouldTriggerPinch
? nsEventStatus_eConsumeNoDefault
: nsEventStatus_eIgnore;
EXPECT_EQ(expectedStatus, statuses[0]);
EXPECT_EQ(expectedStatus, statuses[1]);
}
template<class InputReceiver>
void
PinchWithTouchInput(const RefPtr<InputReceiver>& aTarget,
int aFocusX, int aFocusY, float aScale,
int& inputId,
nsTArray<uint32_t>* aAllowedTouchBehaviors = nullptr,
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t* aOutInputBlockId = nullptr)
{
// Having pinch coordinates in float type may cause problems with high-precision scale values
// since SingleTouchData accepts integer value. But for trivial tests it should be ok.
float pinchLength = 100.0;
float pinchLengthScaled = pinchLength * aScale;
// Even if the caller doesn't care about the block id, we need it to set the
// allowed touch behaviour below, so make sure aOutInputBlockId is non-null.
uint64_t blockId;
if (!aOutInputBlockId) {
aOutInputBlockId = &blockId;
}
MultiTouchInput mtiStart = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX, aFocusY));
mtiStart.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX, aFocusY));
nsEventStatus status = aTarget->ReceiveInputEvent(mtiStart, aOutInputBlockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
if (aAllowedTouchBehaviors) {
EXPECT_EQ(2UL, aAllowedTouchBehaviors->Length());
aTarget->SetAllowedTouchBehavior(*aOutInputBlockId, *aAllowedTouchBehaviors);
} else if (gfxPrefs::TouchActionEnabled()) {
SetDefaultAllowedTouchBehavior(aTarget, *aOutInputBlockId, 2);
}
MultiTouchInput mtiMove1 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX - pinchLength, aFocusY));
mtiMove1.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX + pinchLength, aFocusY));
status = aTarget->ReceiveInputEvent(mtiMove1, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
MultiTouchInput mtiMove2 = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX - pinchLengthScaled, aFocusY));
mtiMove2.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX + pinchLengthScaled, aFocusY));
status = aTarget->ReceiveInputEvent(mtiMove2, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = status;
}
MultiTouchInput mtiEnd = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId, aFocusX - pinchLengthScaled, aFocusY));
mtiEnd.mTouches.AppendElement(CreateSingleTouchData(inputId + 1, aFocusX + pinchLengthScaled, aFocusY));
status = aTarget->ReceiveInputEvent(mtiEnd, nullptr);
if (aOutEventStatuses) {
(*aOutEventStatuses)[3] = status;
}
inputId += 2;
}
template<class InputReceiver>
void
PinchWithTouchInputAndCheckStatus(const RefPtr<InputReceiver>& aTarget,
int aFocusX, int aFocusY, float aScale,
int& inputId, bool aShouldTriggerPinch,
nsTArray<uint32_t>* aAllowedTouchBehaviors)
{
nsEventStatus statuses[4]; // down, move, move, up
PinchWithTouchInput(aTarget, aFocusX, aFocusY, aScale, inputId, aAllowedTouchBehaviors, &statuses);
nsEventStatus expectedMoveStatus = aShouldTriggerPinch
? nsEventStatus_eConsumeDoDefault
: nsEventStatus_eIgnore;
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
EXPECT_EQ(expectedMoveStatus, statuses[1]);
EXPECT_EQ(expectedMoveStatus, statuses[2]);
}
template<class InputReceiver>
void
DoubleTap(const RefPtr<InputReceiver>& aTarget, int aX, int aY, MockContentControllerDelayed* aMcc,
nsEventStatus (*aOutEventStatuses)[4] = nullptr,
uint64_t (*aOutInputBlockIds)[2] = nullptr)
{
uint64_t blockId;
nsEventStatus status = TouchDown(aTarget, aX, aY, aMcc->Time(), &blockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[0] = status;
}
if (aOutInputBlockIds) {
(*aOutInputBlockIds)[0] = blockId;
}
aMcc->AdvanceByMillis(10);
// If touch-action is enabled then simulate the allowed touch behaviour
// notification that the main thread is supposed to deliver.
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
SetDefaultAllowedTouchBehavior(aTarget, blockId);
}
status = TouchUp(aTarget, aX, aY, aMcc->Time());
if (aOutEventStatuses) {
(*aOutEventStatuses)[1] = status;
}
aMcc->AdvanceByMillis(10);
status = TouchDown(aTarget, aX, aY, aMcc->Time(), &blockId);
if (aOutEventStatuses) {
(*aOutEventStatuses)[2] = status;
}
if (aOutInputBlockIds) {
(*aOutInputBlockIds)[1] = blockId;
}
aMcc->AdvanceByMillis(10);
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
SetDefaultAllowedTouchBehavior(aTarget, blockId);
}
status = TouchUp(aTarget, aX, aY, aMcc->Time());
if (aOutEventStatuses) {
(*aOutEventStatuses)[3] = status;
}
}
template<class InputReceiver>
void
DoubleTapAndCheckStatus(const RefPtr<InputReceiver>& aTarget, int aX, int aY,
MockContentControllerDelayed* aMcc, uint64_t (*aOutInputBlockIds)[2] = nullptr)
{
nsEventStatus statuses[4];
DoubleTap(aTarget, aX, aY, aMcc, &statuses, aOutInputBlockIds);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[1]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[2]);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[3]);
}
#endif // mozilla_layers_InputUtils_h

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,348 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCBasicTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
TEST_F(APZCBasicTester, Overzoom) {
// the visible area of the document in CSS pixels is x=10 y=0 w=100 h=100
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(0, 0, 100, 100));
fm.SetScrollableRect(CSSRect(0, 0, 125, 150));
fm.SetScrollOffset(CSSPoint(10, 0));
fm.SetZoom(CSSToParentLayerScale2D(1.0, 1.0));
fm.SetIsRootContent(true);
apzc->SetFrameMetrics(fm);
MakeApzcZoomable();
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
PinchWithPinchInputAndCheckStatus(apzc, 50, 50, 0.5, true);
fm = apzc->GetFrameMetrics();
EXPECT_EQ(0.8f, fm.GetZoom().ToScaleFactor().scale);
// bug 936721 - PGO builds introduce rounding error so
// use a fuzzy match instead
EXPECT_LT(std::abs(fm.GetScrollOffset().x), 1e-5);
EXPECT_LT(std::abs(fm.GetScrollOffset().y), 1e-5);
}
TEST_F(APZCBasicTester, SimpleTransform) {
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(ParentLayerPoint(), pointOut);
EXPECT_EQ(AsyncTransform(), viewTransformOut);
}
TEST_F(APZCBasicTester, ComplexTransform) {
// This test assumes there is a page that gets rendered to
// two layers. In CSS pixels, the first layer is 50x50 and
// the second layer is 25x50. The widget scale factor is 3.0
// and the presShell resolution is 2.0. Therefore, these layers
// end up being 300x300 and 150x300 in layer pixels.
//
// The second (child) layer has an additional CSS transform that
// stretches it by 2.0 on the x-axis. Therefore, after applying
// CSS transforms, the two layers are the same size in screen
// pixels.
//
// The screen itself is 24x24 in screen pixels (therefore 4x4 in
// CSS pixels). The displayport is 1 extra CSS pixel on all
// sides.
RefPtr<TestAsyncPanZoomController> childApzc =
new TestAsyncPanZoomController(0, mcc, tm);
const char* layerTreeSyntax = "c(c)";
// LayerID 0 1
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0, 0, 300, 300)),
nsIntRegion(IntRect(0, 0, 150, 300)),
};
Matrix4x4 transforms[] = {
Matrix4x4(),
Matrix4x4(),
};
transforms[0].PostScale(0.5f, 0.5f, 1.0f); // this results from the 2.0 resolution on the root layer
transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
nsTArray<RefPtr<Layer> > layers;
RefPtr<LayerManager> lm;
RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
FrameMetrics metrics;
metrics.SetCompositionBounds(ParentLayerRect(0, 0, 24, 24));
metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6));
metrics.SetScrollOffset(CSSPoint(10, 10));
metrics.SetScrollableRect(CSSRect(0, 0, 50, 50));
metrics.SetCumulativeResolution(LayoutDeviceToLayerScale2D(2, 2));
metrics.SetPresShellResolution(2.0f);
metrics.SetZoom(CSSToParentLayerScale2D(6, 6));
metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3));
metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
FrameMetrics childMetrics = metrics;
childMetrics.SetScrollId(FrameMetrics::START_SCROLL_ID + 1);
layers[0]->SetFrameMetrics(metrics);
layers[1]->SetFrameMetrics(childMetrics);
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
// Both the parent and child layer should behave exactly the same here, because
// the CSS transform on the child layer does not affect the SampleContentTransformForFrame code
// initial transform
apzc->SetFrameMetrics(metrics);
apzc->NotifyLayersUpdated(metrics, true, true);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
childApzc->SetFrameMetrics(childMetrics);
childApzc->NotifyLayersUpdated(childMetrics, true, true);
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint()), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(60, 60), pointOut);
// do an async scroll by 5 pixels and check the transform
metrics.ScrollBy(CSSPoint(5, 0));
apzc->SetFrameMetrics(metrics);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
childMetrics.ScrollBy(CSSPoint(5, 0));
childApzc->SetFrameMetrics(childMetrics);
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1), ParentLayerPoint(-30, 0)), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(90, 60), pointOut);
// do an async zoom of 1.5x and check the transform
metrics.ZoomBy(1.5f);
apzc->SetFrameMetrics(metrics);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
childMetrics.ZoomBy(1.5f);
childApzc->SetFrameMetrics(childMetrics);
childApzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(AsyncTransform(LayerToParentLayerScale(1.5), ParentLayerPoint(-45, 0)), viewTransformOut);
EXPECT_EQ(ParentLayerPoint(135, 90), pointOut);
childApzc->Destroy();
}
TEST_F(APZCBasicTester, Fling) {
int touchStart = 50;
int touchEnd = 10;
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
// Fling down. Each step scroll further down
Pan(apzc, mcc, touchStart, touchEnd);
ParentLayerPoint lastPoint;
for (int i = 1; i < 50; i+=1) {
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(1));
EXPECT_GT(pointOut.y, lastPoint.y);
lastPoint = pointOut;
}
}
TEST_F(APZCBasicTester, FlingIntoOverscroll) {
// Enable overscrolling.
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Scroll down by 25 px. Don't fling for simplicity.
ApzcPanNoFling(apzc, mcc, 50, 25);
// Now scroll back up by 20px, this time flinging after.
// The fling should cover the remaining 5 px of room to scroll, then
// go into overscroll, and finally snap-back to recover from overscroll.
Pan(apzc, mcc, 25, 45);
const TimeDuration increment = TimeDuration::FromMilliseconds(1);
bool reachedOverscroll = false;
bool recoveredFromOverscroll = false;
while (apzc->AdvanceAnimations(mcc->Time())) {
if (!reachedOverscroll && apzc->IsOverscrolled()) {
reachedOverscroll = true;
}
if (reachedOverscroll && !apzc->IsOverscrolled()) {
recoveredFromOverscroll = true;
}
mcc->AdvanceBy(increment);
}
EXPECT_TRUE(reachedOverscroll);
EXPECT_TRUE(recoveredFromOverscroll);
}
TEST_F(APZCBasicTester, PanningTransformNotifications) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Scroll down by 25 px. Ensure we only get one set of
// state change notifications.
//
// Then, scroll back up by 20px, this time flinging after.
// The fling should cover the remaining 5 px of room to scroll, then
// go into overscroll, and finally snap-back to recover from overscroll.
// Again, ensure we only get one set of state change notifications for
// this entire procedure.
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
EXPECT_CALL(check, Call("Simple pan"));
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::StartTouch,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::TransformBegin,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::StartPanning,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::EndTouch,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::TransformEnd,_)).Times(1);
EXPECT_CALL(check, Call("Complex pan"));
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::StartTouch,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::TransformBegin,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::StartPanning,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::EndTouch,_)).Times(1);
EXPECT_CALL(*mcc, NotifyAPZStateChange(_,GeckoContentController::APZStateChange::TransformEnd,_)).Times(1);
EXPECT_CALL(check, Call("Done"));
}
check.Call("Simple pan");
ApzcPanNoFling(apzc, mcc, 50, 25);
check.Call("Complex pan");
Pan(apzc, mcc, 25, 45);
apzc->AdvanceAnimationsUntilEnd();
check.Call("Done");
}
void APZCBasicTester::PanIntoOverscroll()
{
int touchStart = 500;
int touchEnd = 10;
Pan(apzc, mcc, touchStart, touchEnd);
EXPECT_TRUE(apzc->IsOverscrolled());
}
void APZCBasicTester::TestOverscroll()
{
// Pan sufficiently to hit overscroll behavior
PanIntoOverscroll();
// Check that we recover from overscroll via an animation.
ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
TEST_F(APZCBasicTester, OverScrollPanning) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
TestOverscroll();
}
// Tests that an overscroll animation doesn't trigger an assertion failure
// in the case where a sample has a velocity of zero.
TEST_F(APZCBasicTester, OverScroll_Bug1152051a) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Doctor the prefs to make the velocity zero at the end of the first sample.
// This ensures our incoming velocity to the overscroll animation is
// a round(ish) number, 4.9 (that being the distance of the pan before
// overscroll, which is 500 - 10 = 490 pixels, divided by the duration of
// the pan, which is 100 ms).
SCOPED_GFX_PREF(APZFlingFriction, float, 0);
// To ensure the velocity after the first sample is 0, set the spring
// stiffness to the incoming velocity (4.9) divided by the overscroll
// (400 pixels) times the step duration (1 ms).
SCOPED_GFX_PREF(APZOverscrollSpringStiffness, float, 0.01225f);
TestOverscroll();
}
// Tests that ending an overscroll animation doesn't leave around state that
// confuses the next overscroll animation.
TEST_F(APZCBasicTester, OverScroll_Bug1152051b) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
SCOPED_GFX_PREF(APZOverscrollStopDistanceThreshold, float, 0.1f);
// Pan sufficiently to hit overscroll behavior
PanIntoOverscroll();
// Sample animations once, to give the fling animation started on touch-up
// a chance to realize it's overscrolled, and schedule a call to
// HandleFlingOverscroll().
SampleAnimationOnce();
// This advances the time and runs the HandleFlingOverscroll task scheduled in
// the previous call, which starts an overscroll animation. It then samples
// the overscroll animation once, to get it to initialize the first overscroll
// sample.
SampleAnimationOnce();
// Do a touch-down to cancel the overscroll animation, and then a touch-up
// to schedule a new one since we're still overscrolled. We don't pan because
// panning can trigger functions that clear the overscroll animation state
// in other ways.
TouchDown(apzc, 10, 10, mcc->Time(), nullptr);
TouchUp(apzc, 10, 10, mcc->Time());
// Sample the second overscroll animation to its end.
// If the ending of the first overscroll animation fails to clear state
// properly, this will assert.
ParentLayerPoint expectedScrollOffset(0, GetScrollRange().YMost());
SampleAnimationUntilRecoveredFromOverscroll(expectedScrollOffset);
}
TEST_F(APZCBasicTester, OverScrollAbort) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Pan sufficiently to hit overscroll behavior
int touchStart = 500;
int touchEnd = 10;
Pan(apzc, mcc, touchStart, touchEnd);
EXPECT_TRUE(apzc->IsOverscrolled());
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
// This sample call will run to the end of the fling animation
// and will schedule the overscroll animation.
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(10000));
EXPECT_TRUE(apzc->IsOverscrolled());
// At this point, we have an active overscroll animation.
// Check that cancelling the animation clears the overscroll.
apzc->CancelAnimation();
EXPECT_FALSE(apzc->IsOverscrolled());
apzc->AssertStateIsReset();
}
TEST_F(APZCBasicTester, OverScrollPanningAbort) {
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
// Pan sufficiently to hit overscroll behaviour. Keep the finger down so
// the pan does not end.
int touchStart = 500;
int touchEnd = 10;
Pan(apzc, mcc, touchStart, touchEnd, true); // keep finger down
EXPECT_TRUE(apzc->IsOverscrolled());
// Check that calling CancelAnimation() while the user is still panning
// (and thus no fling or snap-back animation has had a chance to start)
// clears the overscroll.
apzc->CancelAnimation();
EXPECT_FALSE(apzc->IsOverscrolled());
apzc->AssertStateIsReset();
}

Просмотреть файл

@ -0,0 +1,271 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
class APZEventRegionsTester : public APZCTreeManagerTester {
protected:
UniquePtr<ScopedLayerTreeRegistration> registration;
TestAsyncPanZoomController* rootApzc;
void CreateEventRegionsLayerTree1() {
const char* layerTreeSyntax = "c(tt)";
nsIntRegion layerVisibleRegions[] = {
nsIntRegion(IntRect(0, 0, 200, 200)), // root
nsIntRegion(IntRect(0, 0, 100, 200)), // left half
nsIntRegion(IntRect(0, 100, 200, 100)), // bottom half
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2);
SetScrollHandoff(layers[1], root);
SetScrollHandoff(layers[2], root);
// Set up the event regions over a 200x200 area. The root layer has the
// whole 200x200 as the hit region; layers[1] has the left half and
// layers[2] has the bottom half. The bottom-left 100x100 area is also
// in the d-t-c region for both layers[1] and layers[2] (but layers[2] is
// on top so it gets the events by default if the main thread doesn't
// respond).
EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
root->SetEventRegions(regions);
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 100, 100, 100));
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 200));
layers[1]->SetEventRegions(regions);
regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
layers[2]->SetEventRegions(regions);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
}
void CreateEventRegionsLayerTree2() {
const char* layerTreeSyntax = "c(t)";
nsIntRegion layerVisibleRegions[] = {
nsIntRegion(IntRect(0, 0, 100, 500)),
nsIntRegion(IntRect(0, 150, 100, 100)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
// Set up the event regions so that the child thebes layer is positioned far
// away from the scrolling container layer.
EventRegions regions(nsIntRegion(IntRect(0, 0, 100, 100)));
root->SetEventRegions(regions);
regions.mHitRegion = nsIntRegion(IntRect(0, 150, 100, 100));
layers[1]->SetEventRegions(regions);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
}
void CreateObscuringLayerTree() {
const char* layerTreeSyntax = "c(c(t)t)";
// LayerID 0 1 2 3
// 0 is the root.
// 1 is a parent scrollable layer.
// 2 is a child scrollable layer.
// 3 is the Obscurer, who ruins everything.
nsIntRegion layerVisibleRegions[] = {
// x coordinates are uninteresting
nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
nsIntRegion(IntRect(0, 0, 200, 200)), // [0, 200]
nsIntRegion(IntRect(0, 100, 200, 50)), // [100, 150]
nsIntRegion(IntRect(0, 100, 200, 100)) // [100, 200]
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 200, 300));
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 200, 100));
SetScrollHandoff(layers[2], layers[1]);
SetScrollHandoff(layers[1], root);
EventRegions regions(nsIntRegion(IntRect(0, 0, 200, 200)));
root->SetEventRegions(regions);
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 200, 300));
layers[1]->SetEventRegions(regions);
regions.mHitRegion = nsIntRegion(IntRect(0, 100, 200, 100));
layers[2]->SetEventRegions(regions);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
}
void CreateBug1119497LayerTree() {
const char* layerTreeSyntax = "c(tt)";
// LayerID 0 12
// 0 is the root and has an APZC
// 1 is behind 2 and has an APZC
// 2 entirely covers 1 and should take all the input events, but has no APZC
// so hits to 2 should go to to the root APZC
nsIntRegion layerVisibleRegions[] = {
nsIntRegion(IntRect(0, 0, 100, 100)),
nsIntRegion(IntRect(0, 0, 100, 100)),
nsIntRegion(IntRect(0, 0, 100, 100)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
}
void CreateBug1117712LayerTree() {
const char* layerTreeSyntax = "c(c(t)t)";
// LayerID 0 1 2 3
// 0 is the root
// 1 is a container layer whose sole purpose to make a non-empty ancestor
// transform for 2, so that 2's screen-to-apzc and apzc-to-gecko
// transforms are different from 3's.
// 2 is a small layer that is the actual target
// 3 is a big layer obscuring 2 with a dispatch-to-content region
nsIntRegion layerVisibleRegions[] = {
nsIntRegion(IntRect(0, 0, 100, 100)),
nsIntRegion(IntRect(0, 0, 0, 0)),
nsIntRegion(IntRect(0, 0, 10, 10)),
nsIntRegion(IntRect(0, 0, 100, 100)),
};
Matrix4x4 layerTransforms[] = {
Matrix4x4(),
Matrix4x4::Translation(50, 0, 0),
Matrix4x4(),
Matrix4x4(),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegions, layerTransforms, lm, layers);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 10, 10));
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 100, 100));
EventRegions regions(nsIntRegion(IntRect(0, 0, 10, 10)));
layers[2]->SetEventRegions(regions);
regions.mHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
regions.mDispatchToContentHitRegion = nsIntRegion(IntRect(0, 0, 100, 100));
layers[3]->SetEventRegions(regions);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
}
};
TEST_F(APZEventRegionsTester, HitRegionImmediateResponse) {
CreateEventRegionsLayerTree1();
TestAsyncPanZoomController* root = ApzcOf(layers[0]);
TestAsyncPanZoomController* left = ApzcOf(layers[1]);
TestAsyncPanZoomController* bottom = ApzcOf(layers[2]);
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
EXPECT_CALL(*mcc, HandleSingleTap(_, _, left->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped on left"));
EXPECT_CALL(*mcc, HandleSingleTap(_, _, bottom->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped on bottom"));
EXPECT_CALL(*mcc, HandleSingleTap(_, _, root->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped on root"));
EXPECT_CALL(check, Call("Tap pending on d-t-c region"));
EXPECT_CALL(*mcc, HandleSingleTap(_, _, bottom->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped on bottom again"));
EXPECT_CALL(*mcc, HandleSingleTap(_, _, left->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped on left this time"));
}
TimeDuration tapDuration = TimeDuration::FromMilliseconds(100);
// Tap in the exposed hit regions of each of the layers once and ensure
// the clicks are dispatched right away
Tap(manager, 10, 10, mcc, tapDuration);
mcc->RunThroughDelayedTasks(); // this runs the tap event
check.Call("Tapped on left");
Tap(manager, 110, 110, mcc, tapDuration);
mcc->RunThroughDelayedTasks(); // this runs the tap event
check.Call("Tapped on bottom");
Tap(manager, 110, 10, mcc, tapDuration);
mcc->RunThroughDelayedTasks(); // this runs the tap event
check.Call("Tapped on root");
// Now tap on the dispatch-to-content region where the layers overlap
Tap(manager, 10, 110, mcc, tapDuration);
mcc->RunThroughDelayedTasks(); // this runs the main-thread timeout
check.Call("Tap pending on d-t-c region");
mcc->RunThroughDelayedTasks(); // this runs the tap event
check.Call("Tapped on bottom again");
// Now let's do that again, but simulate a main-thread response
uint64_t inputBlockId = 0;
Tap(manager, 10, 110, mcc, tapDuration, nullptr, &inputBlockId);
nsTArray<ScrollableLayerGuid> targets;
targets.AppendElement(left->GetGuid());
manager->SetTargetAPZC(inputBlockId, targets);
while (mcc->RunThroughDelayedTasks()); // this runs the tap event
check.Call("Tapped on left this time");
}
TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
CreateEventRegionsLayerTree2();
// Tap in the area of the child layer that's not directly included in the
// parent layer's hit region. Verify that it comes out of the APZC's
// content controller, which indicates the input events got routed correctly
// to the APZC.
EXPECT_CALL(*mcc, HandleSingleTap(_, _, rootApzc->GetGuid())).Times(1);
Tap(manager, 10, 160, mcc, TimeDuration::FromMilliseconds(100));
}
TEST_F(APZEventRegionsTester, Obscuration) {
CreateObscuringLayerTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
TestAsyncPanZoomController* parent = ApzcOf(layers[1]);
TestAsyncPanZoomController* child = ApzcOf(layers[2]);
ApzcPanNoFling(parent, mcc, 75, 25);
HitTestResult result;
RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 75), &result);
EXPECT_EQ(child, hit.get());
EXPECT_EQ(HitTestResult::HitLayer, result);
}
TEST_F(APZEventRegionsTester, Bug1119497) {
CreateBug1119497LayerTree();
HitTestResult result;
RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(50, 50), &result);
// We should hit layers[2], so |result| will be HitLayer but there's no
// actual APZC on layers[2], so it will be the APZC of the root layer.
EXPECT_EQ(ApzcOf(layers[0]), hit.get());
EXPECT_EQ(HitTestResult::HitLayer, result);
}
TEST_F(APZEventRegionsTester, Bug1117712) {
CreateBug1117712LayerTree();
TestAsyncPanZoomController* apzc2 = ApzcOf(layers[2]);
// These touch events should hit the dispatch-to-content region of layers[3]
// and so get queued with that APZC as the tentative target.
uint64_t inputBlockId = 0;
Tap(manager, 55, 5, mcc, TimeDuration::FromMilliseconds(100), nullptr, &inputBlockId);
// But now we tell the APZ that really it hit layers[2], and expect the tap
// to be delivered at the correct coordinates.
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(55, 5), 0, apzc2->GetGuid())).Times(1);
nsTArray<ScrollableLayerGuid> targets;
targets.AppendElement(apzc2->GetGuid());
manager->SetTargetAPZC(inputBlockId, targets);
}

Просмотреть файл

@ -0,0 +1,594 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCBasicTester.h"
#include "APZTestCommon.h"
class APZCGestureDetectorTester : public APZCBasicTester {
public:
APZCGestureDetectorTester()
: APZCBasicTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
{
}
protected:
FrameMetrics GetPinchableFrameMetrics()
{
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
fm.SetScrollOffset(CSSPoint(300, 300));
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
// APZC only allows zooming on the root scrollable frame.
fm.SetIsRootContent(true);
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
return fm;
}
};
TEST_F(APZCGestureDetectorTester, Pan_After_Pinch) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
MakeApzcZoomable();
// Test parameters
float zoomAmount = 1.25;
float pinchLength = 100.0;
float pinchLengthScaled = pinchLength * zoomAmount;
int focusX = 250;
int focusY = 300;
int panDistance = 20;
int firstFingerId = 0;
int secondFingerId = firstFingerId + 1;
// Put fingers down
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX, focusY));
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// Spread fingers out to enter the pinch state
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLength, focusY));
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLength, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// Do the actual pinch of 1.25x
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// Verify that the zoom changed, just to make sure our code above did what it
// was supposed to.
FrameMetrics zoomedMetrics = apzc->GetFrameMetrics();
float newZoom = zoomedMetrics.GetZoom().ToScaleFactor().scale;
EXPECT_EQ(originalMetrics.GetZoom().ToScaleFactor().scale * zoomAmount, newZoom);
// Now we lift one finger...
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, focusX + pinchLengthScaled, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// ... and pan with the remaining finger. This pan just breaks through the
// distance threshold.
focusY += 40;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// This one does an actual pan of 20 pixels
focusY += panDistance;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// Lift the remaining finger
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, focusX - pinchLengthScaled, focusY));
apzc->ReceiveInputEvent(mti, nullptr);
// Verify that we scrolled
FrameMetrics finalMetrics = apzc->GetFrameMetrics();
EXPECT_EQ(zoomedMetrics.GetScrollOffset().y - (panDistance / newZoom), finalMetrics.GetScrollOffset().y);
// Clear out any remaining fling animation and pending tasks
apzc->AdvanceAnimationsUntilEnd();
while (mcc->RunThroughDelayedTasks());
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, Pan_With_Tap) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
// Making the APZC zoomable isn't really needed for the correct operation of
// this test, but it could help catch regressions where we accidentally enter
// a pinch state.
MakeApzcZoomable();
// Test parameters
int touchX = 250;
int touchY = 300;
int panDistance = 20;
int firstFingerId = 0;
int secondFingerId = firstFingerId + 1;
// Put finger down
MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Start a pan, break through the threshold
touchY += 40;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Do an actual pan for a bit
touchY += panDistance;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Put a second finger down
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Lift the second finger
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(secondFingerId, touchX + 10, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Bust through the threshold again
touchY += 40;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Do some more actual panning
touchY += panDistance;
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Lift the first finger
mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, 0, TimeStamp(), 0);
mti.mTouches.AppendElement(CreateSingleTouchData(firstFingerId, touchX, touchY));
apzc->ReceiveInputEvent(mti, nullptr);
// Verify that we scrolled
FrameMetrics finalMetrics = apzc->GetFrameMetrics();
float zoom = finalMetrics.GetZoom().ToScaleFactor().scale;
EXPECT_EQ(originalMetrics.GetScrollOffset().y - (panDistance * 2 / zoom), finalMetrics.GetScrollOffset().y);
// Clear out any remaining fling animation and pending tasks
apzc->AdvanceAnimationsUntilEnd();
while (mcc->RunThroughDelayedTasks());
apzc->AssertStateIsReset();
}
class APZCFlingStopTester : public APZCGestureDetectorTester {
protected:
// Start a fling, and then tap while the fling is ongoing. When
// aSlow is false, the tap will happen while the fling is at a
// high velocity, and we check that the tap doesn't trigger sending a tap
// to content. If aSlow is true, the tap will happen while the fling
// is at a slow velocity, and we check that the tap does trigger sending
// a tap to content. See bug 1022956.
void DoFlingStopTest(bool aSlow) {
int touchStart = 50;
int touchEnd = 10;
// Start the fling down.
Pan(apzc, mcc, touchStart, touchEnd);
// The touchstart from the pan will leave some cancelled tasks in the queue, clear them out
// If we want to tap while the fling is fast, let the fling advance for 10ms only. If we want
// the fling to slow down more, advance to 2000ms. These numbers may need adjusting if our
// friction and threshold values change, but they should be deterministic at least.
int timeDelta = aSlow ? 2000 : 10;
int tapCallsExpected = aSlow ? 2 : 1;
// Advance the fling animation by timeDelta milliseconds.
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut, TimeDuration::FromMilliseconds(timeDelta));
// Deliver a tap to abort the fling. Ensure that we get a HandleSingleTap
// call out of it if and only if the fling is slow.
EXPECT_CALL(*mcc, HandleSingleTap(_, 0, apzc->GetGuid())).Times(tapCallsExpected);
Tap(apzc, 10, 10, mcc, 0);
while (mcc->RunThroughDelayedTasks());
// Deliver another tap, to make sure that taps are flowing properly once
// the fling is aborted.
Tap(apzc, 100, 100, mcc, 0);
while (mcc->RunThroughDelayedTasks());
// Verify that we didn't advance any further after the fling was aborted, in either case.
ParentLayerPoint finalPointOut;
apzc->SampleContentTransformForFrame(&viewTransformOut, finalPointOut);
EXPECT_EQ(pointOut.x, finalPointOut.x);
EXPECT_EQ(pointOut.y, finalPointOut.y);
apzc->AssertStateIsReset();
}
void DoFlingStopWithSlowListener(bool aPreventDefault) {
MakeApzcWaitForMainThread();
int touchStart = 50;
int touchEnd = 10;
uint64_t blockId = 0;
// Start the fling down.
Pan(apzc, mcc, touchStart, touchEnd, false, nullptr, nullptr, &blockId);
apzc->ConfirmTarget(blockId);
apzc->ContentReceivedInputBlock(blockId, false);
// Sample the fling a couple of times to ensure it's going.
ParentLayerPoint point, finalPoint;
AsyncTransform viewTransform;
apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
apzc->SampleContentTransformForFrame(&viewTransform, finalPoint, TimeDuration::FromMilliseconds(10));
EXPECT_GT(finalPoint.y, point.y);
// Now we put our finger down to stop the fling
TouchDown(apzc, 10, 10, mcc->Time(), &blockId);
// Re-sample to make sure it hasn't moved
apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(10));
EXPECT_EQ(finalPoint.x, point.x);
EXPECT_EQ(finalPoint.y, point.y);
// respond to the touchdown that stopped the fling.
// even if we do a prevent-default on it, the animation should remain stopped.
apzc->ContentReceivedInputBlock(blockId, aPreventDefault);
// Verify the page hasn't moved
apzc->SampleContentTransformForFrame(&viewTransform, point, TimeDuration::FromMilliseconds(70));
EXPECT_EQ(finalPoint.x, point.x);
EXPECT_EQ(finalPoint.y, point.y);
// clean up
TouchUp(apzc, 10, 10, mcc->Time());
apzc->AssertStateIsReset();
}
};
TEST_F(APZCFlingStopTester, FlingStop) {
DoFlingStopTest(false);
}
TEST_F(APZCFlingStopTester, FlingStopTap) {
DoFlingStopTest(true);
}
TEST_F(APZCFlingStopTester, FlingStopSlowListener) {
DoFlingStopWithSlowListener(false);
}
TEST_F(APZCFlingStopTester, FlingStopPreventDefault) {
DoFlingStopWithSlowListener(true);
}
TEST_F(APZCGestureDetectorTester, ShortPress) {
MakeApzcUnzoomable();
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
// This verifies that the single tap notification is sent after the
// touchup is fully processed. The ordering here is important.
EXPECT_CALL(check, Call("pre-tap"));
EXPECT_CALL(check, Call("post-tap"));
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
}
check.Call("pre-tap");
TapAndCheckStatus(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(100));
check.Call("post-tap");
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, MediumPress) {
MakeApzcUnzoomable();
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
// This verifies that the single tap notification is sent after the
// touchup is fully processed. The ordering here is important.
EXPECT_CALL(check, Call("pre-tap"));
EXPECT_CALL(check, Call("post-tap"));
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
}
check.Call("pre-tap");
TapAndCheckStatus(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(400));
check.Call("post-tap");
apzc->AssertStateIsReset();
}
class APZCLongPressTester : public APZCGestureDetectorTester {
protected:
void DoLongPressTest(uint32_t aBehavior) {
MakeApzcUnzoomable();
uint64_t blockId = 0;
nsEventStatus status = TouchDown(apzc, 10, 10, mcc->Time(), &blockId);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
// SetAllowedTouchBehavior() must be called after sending touch-start.
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(aBehavior);
apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
}
// Have content "respond" to the touchstart
apzc->ContentReceivedInputBlock(blockId, false);
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
EXPECT_CALL(check, Call("preHandleLongTap"));
blockId++;
EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(10, 10), 0, apzc->GetGuid(), blockId)).Times(1);
EXPECT_CALL(check, Call("postHandleLongTap"));
EXPECT_CALL(check, Call("preHandleSingleTap"));
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
EXPECT_CALL(check, Call("postHandleSingleTap"));
}
// Manually invoke the longpress while the touch is currently down.
check.Call("preHandleLongTap");
mcc->RunThroughDelayedTasks();
check.Call("postHandleLongTap");
// Dispatching the longpress event starts a new touch block, which
// needs a new content response and also has a pending timeout task
// in the queue. Deal with those here. We do the content response first
// with preventDefault=false, and then we run the timeout task which
// "loses the race" and does nothing.
apzc->ContentReceivedInputBlock(blockId, false);
mcc->AdvanceByMillis(1000);
// Finally, simulate lifting the finger. Since the long-press wasn't
// prevent-defaulted, we should get a long-tap-up event.
check.Call("preHandleSingleTap");
status = TouchUp(apzc, 10, 10, mcc->Time());
mcc->RunThroughDelayedTasks();
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
check.Call("postHandleSingleTap");
apzc->AssertStateIsReset();
}
void DoLongPressPreventDefaultTest(uint32_t aBehavior) {
MakeApzcUnzoomable();
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
int touchX = 10,
touchStartY = 10,
touchEndY = 50;
uint64_t blockId = 0;
nsEventStatus status = TouchDown(apzc, touchX, touchStartY, mcc->Time(), &blockId);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
if (gfxPrefs::TouchActionEnabled() && status != nsEventStatus_eConsumeNoDefault) {
// SetAllowedTouchBehavior() must be called after sending touch-start.
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(aBehavior);
apzc->SetAllowedTouchBehavior(blockId, allowedTouchBehaviors);
}
// Have content "respond" to the touchstart
apzc->ContentReceivedInputBlock(blockId, false);
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
EXPECT_CALL(check, Call("preHandleLongTap"));
blockId++;
EXPECT_CALL(*mcc, HandleLongTap(CSSPoint(touchX, touchStartY), 0, apzc->GetGuid(), blockId)).Times(1);
EXPECT_CALL(check, Call("postHandleLongTap"));
}
// Manually invoke the longpress while the touch is currently down.
check.Call("preHandleLongTap");
mcc->RunThroughDelayedTasks();
check.Call("postHandleLongTap");
// There should be a TimeoutContentResponse task in the queue still,
// waiting for the response from the longtap event dispatched above.
// Send the signal that content has handled the long-tap, and then run
// the timeout task (it will be a no-op because the content "wins" the
// race. This takes the place of the "contextmenu" event.
apzc->ContentReceivedInputBlock(blockId, true);
mcc->AdvanceByMillis(1000);
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(touchX, touchEndY), ScreenSize(0, 0), 0, 0));
status = apzc->ReceiveInputEvent(mti, nullptr);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(touchX, touchEndY), 0, apzc->GetGuid())).Times(0);
status = TouchUp(apzc, touchX, touchEndY, mcc->Time());
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, status);
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(ParentLayerPoint(), pointOut);
EXPECT_EQ(AsyncTransform(), viewTransformOut);
apzc->AssertStateIsReset();
}
};
TEST_F(APZCLongPressTester, LongPress) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
DoLongPressTest(mozilla::layers::AllowedTouchBehavior::NONE);
}
TEST_F(APZCLongPressTester, LongPressWithTouchAction) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoLongPressTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
}
TEST_F(APZCLongPressTester, LongPressPreventDefault) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::NONE);
}
TEST_F(APZCLongPressTester, LongPressPreventDefaultWithTouchAction) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoLongPressPreventDefaultTest(mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN
| mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
}
TEST_F(APZCGestureDetectorTester, DoubleTap) {
MakeApzcWaitForMainThread();
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
uint64_t blockIds[2];
DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedInputBlock(blockIds[0], false);
apzc->ContentReceivedInputBlock(blockIds[1], false);
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, DoubleTapNotZoomable) {
MakeApzcWaitForMainThread();
MakeApzcUnzoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(2);
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
uint64_t blockIds[2];
DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedInputBlock(blockIds[0], false);
apzc->ContentReceivedInputBlock(blockIds[1], false);
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultFirstOnly) {
MakeApzcWaitForMainThread();
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
uint64_t blockIds[2];
DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedInputBlock(blockIds[0], true);
apzc->ContentReceivedInputBlock(blockIds[1], false);
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, DoubleTapPreventDefaultBoth) {
MakeApzcWaitForMainThread();
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
EXPECT_CALL(*mcc, HandleDoubleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(0);
uint64_t blockIds[2];
DoubleTapAndCheckStatus(apzc, 10, 10, mcc, &blockIds);
// responses to the two touchstarts
apzc->ContentReceivedInputBlock(blockIds[0], true);
apzc->ContentReceivedInputBlock(blockIds[1], true);
apzc->AssertStateIsReset();
}
// Test for bug 947892
// We test whether we dispatch tap event when the tap is followed by pinch.
TEST_F(APZCGestureDetectorTester, TapFollowedByPinch) {
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
Tap(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(100));
int inputId = 0;
MultiTouchInput mti;
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti, nullptr);
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti, nullptr);
apzc->AssertStateIsReset();
}
TEST_F(APZCGestureDetectorTester, TapFollowedByMultipleTouches) {
MakeApzcZoomable();
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(10, 10), 0, apzc->GetGuid())).Times(1);
Tap(apzc, 10, 10, mcc, TimeDuration::FromMilliseconds(100));
int inputId = 0;
MultiTouchInput mti;
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti, nullptr);
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti, nullptr);
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_END, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(inputId, ParentLayerPoint(20, 20), ScreenSize(0, 0), 0, 0));
mti.mTouches.AppendElement(SingleTouchData(inputId + 1, ParentLayerPoint(10, 10), ScreenSize(0, 0), 0, 0));
apzc->ReceiveInputEvent(mti, nullptr);
apzc->AssertStateIsReset();
}

Просмотреть файл

@ -0,0 +1,487 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
class APZHitTestingTester : public APZCTreeManagerTester {
protected:
ScreenToParentLayerMatrix4x4 transformToApzc;
ParentLayerToScreenMatrix4x4 transformToGecko;
already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
RefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
if (hit) {
transformToApzc = manager->GetScreenToApzcTransform(hit.get());
transformToGecko = manager->GetApzcToGeckoTransform(hit.get());
}
return hit.forget();
}
protected:
void CreateHitTesting1LayerTree() {
const char* layerTreeSyntax = "c(tttt)";
// LayerID 0 1234
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,100,100)),
nsIntRegion(IntRect(0,0,100,100)),
nsIntRegion(IntRect(10,10,20,20)),
nsIntRegion(IntRect(10,10,20,20)),
nsIntRegion(IntRect(5,5,20,20)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
}
void CreateHitTesting2LayerTree() {
const char* layerTreeSyntax = "c(tc(t))";
// LayerID 0 12 3
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,100,100)),
nsIntRegion(IntRect(10,10,40,40)),
nsIntRegion(IntRect(10,60,40,40)),
nsIntRegion(IntRect(10,60,40,40)),
};
Matrix4x4 transforms[] = {
Matrix4x4(),
Matrix4x4(),
Matrix4x4::Scaling(2, 1, 1),
Matrix4x4(),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 80, 80));
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80));
}
void CreateComplexMultiLayerTree() {
const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))";
// LayerID 0 12 3 45 6 7 89
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,300,400)), // root(0)
nsIntRegion(IntRect(0,0,100,100)), // thebes(1) in top-left
nsIntRegion(IntRect(50,50,200,300)), // container(2) centered in root(0)
nsIntRegion(IntRect(50,50,200,300)), // thebes(3) fully occupying parent container(2)
nsIntRegion(IntRect(0,200,100,100)), // thebes(4) in bottom-left
nsIntRegion(IntRect(200,0,100,400)), // container(5) along the right 100px of root(0)
nsIntRegion(IntRect(200,0,100,200)), // container(6) taking up the top half of parent container(5)
nsIntRegion(IntRect(200,0,100,200)), // thebes(7) fully occupying parent container(6)
nsIntRegion(IntRect(200,200,100,100)), // thebes(8) in bottom-right (below (6))
nsIntRegion(IntRect(200,300,100,100)), // thebes(9) in bottom-right (below (8))
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2);
SetScrollableFrameMetrics(layers[8], FrameMetrics::START_SCROLL_ID + 1);
SetScrollableFrameMetrics(layers[9], FrameMetrics::START_SCROLL_ID + 3);
}
void CreateBug1148350LayerTree() {
const char* layerTreeSyntax = "c(t)";
// LayerID 0 1
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0,0,200,200)),
nsIntRegion(IntRect(0,0,200,200)),
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
}
};
// A simple hit testing test that doesn't involve any transforms on layers.
TEST_F(APZHitTestingTester, HitTesting1) {
CreateHitTesting1LayerTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
// No APZC attached so hit testing will return no APZC at (20,20)
RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(20, 20));
TestAsyncPanZoomController* nullAPZC = nullptr;
EXPECT_EQ(nullAPZC, hit.get());
EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
uint32_t paintSequenceNumber = 0;
// Now we have a root APZC that will match the page
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID);
manager->UpdateHitTestingTree(nullptr, root, false, 0, paintSequenceNumber++);
hit = GetTargetAPZC(ScreenPoint(15, 15));
EXPECT_EQ(ApzcOf(root), hit.get());
// expect hit point at LayerIntPoint(15, 15)
EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc * ScreenPoint(15, 15));
EXPECT_EQ(ScreenPoint(15, 15), transformToGecko * ParentLayerPoint(15, 15));
// Now we have a sub APZC with a better fit
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 1);
manager->UpdateHitTestingTree(nullptr, root, false, 0, paintSequenceNumber++);
EXPECT_NE(ApzcOf(root), ApzcOf(layers[3]));
hit = GetTargetAPZC(ScreenPoint(25, 25));
EXPECT_EQ(ApzcOf(layers[3]), hit.get());
// expect hit point at LayerIntPoint(25, 25)
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25));
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25));
// At this point, layers[4] obscures layers[3] at the point (15, 15) so
// hitting there should hit the root APZC
hit = GetTargetAPZC(ScreenPoint(15, 15));
EXPECT_EQ(ApzcOf(root), hit.get());
// Now test hit testing when we have two scrollable layers
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 2);
manager->UpdateHitTestingTree(nullptr, root, false, 0, paintSequenceNumber++);
hit = GetTargetAPZC(ScreenPoint(15, 15));
EXPECT_EQ(ApzcOf(layers[4]), hit.get());
// expect hit point at LayerIntPoint(15, 15)
EXPECT_EQ(ParentLayerPoint(15, 15), transformToApzc * ScreenPoint(15, 15));
EXPECT_EQ(ScreenPoint(15, 15), transformToGecko * ParentLayerPoint(15, 15));
// Hit test ouside the reach of layer[3,4] but inside root
hit = GetTargetAPZC(ScreenPoint(90, 90));
EXPECT_EQ(ApzcOf(root), hit.get());
// expect hit point at LayerIntPoint(90, 90)
EXPECT_EQ(ParentLayerPoint(90, 90), transformToApzc * ScreenPoint(90, 90));
EXPECT_EQ(ScreenPoint(90, 90), transformToGecko * ParentLayerPoint(90, 90));
// Hit test ouside the reach of any layer
hit = GetTargetAPZC(ScreenPoint(1000, 10));
EXPECT_EQ(nullAPZC, hit.get());
EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
hit = GetTargetAPZC(ScreenPoint(-1000, 10));
EXPECT_EQ(nullAPZC, hit.get());
EXPECT_EQ(ScreenToParentLayerMatrix4x4(), transformToApzc);
EXPECT_EQ(ParentLayerToScreenMatrix4x4(), transformToGecko);
}
// A more involved hit testing test that involves css and async transforms.
TEST_F(APZHitTestingTester, HitTesting2) {
CreateHitTesting2LayerTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
// At this point, the following holds (all coordinates in screen pixels):
// layers[0] has content from (0,0)-(200,200), clipped by composition bounds (0,0)-(100,100)
// layers[1] has content from (10,10)-(90,90), clipped by composition bounds (10,10)-(50,50)
// layers[2] has content from (20,60)-(100,100). no clipping as it's not a scrollable layer
// layers[3] has content from (20,60)-(180,140), clipped by composition bounds (20,60)-(100,100)
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
TestAsyncPanZoomController* apzc1 = ApzcOf(layers[1]);
TestAsyncPanZoomController* apzc3 = ApzcOf(layers[3]);
// Hit an area that's clearly on the root layer but not any of the child layers.
RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(75, 25));
EXPECT_EQ(apzcroot, hit.get());
EXPECT_EQ(ParentLayerPoint(75, 25), transformToApzc * ScreenPoint(75, 25));
EXPECT_EQ(ScreenPoint(75, 25), transformToGecko * ParentLayerPoint(75, 25));
// Hit an area on the root that would be on layers[3] if layers[2]
// weren't transformed.
// Note that if layers[2] were scrollable, then this would hit layers[2]
// because its composition bounds would be at (10,60)-(50,100) (and the
// scale-only transform that we set on layers[2] would be invalid because
// it would place the layer into overscroll, as its composition bounds
// start at x=10 but its content at x=20).
hit = GetTargetAPZC(ScreenPoint(15, 75));
EXPECT_EQ(apzcroot, hit.get());
EXPECT_EQ(ParentLayerPoint(15, 75), transformToApzc * ScreenPoint(15, 75));
EXPECT_EQ(ScreenPoint(15, 75), transformToGecko * ParentLayerPoint(15, 75));
// Hit an area on layers[1].
hit = GetTargetAPZC(ScreenPoint(25, 25));
EXPECT_EQ(apzc1, hit.get());
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25));
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25));
// Hit an area on layers[3].
hit = GetTargetAPZC(ScreenPoint(25, 75));
EXPECT_EQ(apzc3, hit.get());
// transformToApzc should unapply layers[2]'s transform
EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc * ScreenPoint(25, 75));
// and transformToGecko should reapply it
EXPECT_EQ(ScreenPoint(25, 75), transformToGecko * ParentLayerPoint(12.5, 75));
// Hit an area on layers[3] that would be on the root if layers[2]
// weren't transformed.
hit = GetTargetAPZC(ScreenPoint(75, 75));
EXPECT_EQ(apzc3, hit.get());
// transformToApzc should unapply layers[2]'s transform
EXPECT_EQ(ParentLayerPoint(37.5, 75), transformToApzc * ScreenPoint(75, 75));
// and transformToGecko should reapply it
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(37.5, 75));
// Pan the root layer upward by 50 pixels.
// This causes layers[1] to scroll out of view, and an async transform
// of -50 to be set on the root layer.
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
// This first pan will move the APZC by 50 pixels, and dispatch a paint request.
// Since this paint request is in the queue to Gecko, transformToGecko will
// take it into account.
ApzcPanNoFling(apzcroot, mcc, 100, 50);
// Hit where layers[3] used to be. It should now hit the root.
hit = GetTargetAPZC(ScreenPoint(75, 75));
EXPECT_EQ(apzcroot, hit.get());
// transformToApzc doesn't unapply the root's own async transform
EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc * ScreenPoint(75, 75));
// and transformToGecko unapplies it and then reapplies it, because by the
// time the event being transformed reaches Gecko the new paint request will
// have been handled.
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(75, 75));
// Hit where layers[1] used to be and where layers[3] should now be.
hit = GetTargetAPZC(ScreenPoint(25, 25));
EXPECT_EQ(apzc3, hit.get());
// transformToApzc unapplies both layers[2]'s css transform and the root's
// async transform
EXPECT_EQ(ParentLayerPoint(12.5, 75), transformToApzc * ScreenPoint(25, 25));
// transformToGecko reapplies both the css transform and the async transform
// because we have already issued a paint request with it.
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(12.5, 75));
// This second pan will move the APZC by another 50 pixels.
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
ApzcPanNoFling(apzcroot, mcc, 100, 50);
// Hit where layers[3] used to be. It should now hit the root.
hit = GetTargetAPZC(ScreenPoint(75, 75));
EXPECT_EQ(apzcroot, hit.get());
// transformToApzc doesn't unapply the root's own async transform
EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc * ScreenPoint(75, 75));
// transformToGecko unapplies the full async transform of -100 pixels
EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(75, 75));
// Hit where layers[1] used to be. It should now hit the root.
hit = GetTargetAPZC(ScreenPoint(25, 25));
EXPECT_EQ(apzcroot, hit.get());
// transformToApzc doesn't unapply the root's own async transform
EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25));
// transformToGecko unapplies the full async transform of -100 pixels
EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25));
}
TEST_F(APZHitTestingTester, ComplexMultiLayerTree) {
CreateComplexMultiLayerTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
/* The layer tree looks like this:
0
|----|--+--|----|
1 2 4 5
| /|\
3 6 8 9
|
7
Layers 1,2 have the same APZC
Layers 4,6,8 have the same APZC
Layer 7 has an APZC
Layer 9 has an APZC
*/
TestAsyncPanZoomController* nullAPZC = nullptr;
// Ensure all the scrollable layers have an APZC
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
EXPECT_FALSE(layers[3]->HasScrollableFrameMetrics());
EXPECT_NE(nullAPZC, ApzcOf(layers[4]));
EXPECT_FALSE(layers[5]->HasScrollableFrameMetrics());
EXPECT_NE(nullAPZC, ApzcOf(layers[6]));
EXPECT_NE(nullAPZC, ApzcOf(layers[7]));
EXPECT_NE(nullAPZC, ApzcOf(layers[8]));
EXPECT_NE(nullAPZC, ApzcOf(layers[9]));
// Ensure those that scroll together have the same APZCs
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
EXPECT_EQ(ApzcOf(layers[4]), ApzcOf(layers[6]));
EXPECT_EQ(ApzcOf(layers[8]), ApzcOf(layers[6]));
// Ensure those that don't scroll together have different APZCs
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[4]));
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[7]));
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[9]));
EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[7]));
EXPECT_NE(ApzcOf(layers[4]), ApzcOf(layers[9]));
EXPECT_NE(ApzcOf(layers[7]), ApzcOf(layers[9]));
// Ensure the APZC parent chains are set up correctly
TestAsyncPanZoomController* layers1_2 = ApzcOf(layers[1]);
TestAsyncPanZoomController* layers4_6_8 = ApzcOf(layers[4]);
TestAsyncPanZoomController* layer7 = ApzcOf(layers[7]);
TestAsyncPanZoomController* layer9 = ApzcOf(layers[9]);
EXPECT_EQ(nullptr, layers1_2->GetParent());
EXPECT_EQ(nullptr, layers4_6_8->GetParent());
EXPECT_EQ(layers4_6_8, layer7->GetParent());
EXPECT_EQ(nullptr, layer9->GetParent());
// Ensure the hit-testing tree looks like the layer tree
RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
RefPtr<HitTestingTreeNode> node5 = root->GetLastChild();
RefPtr<HitTestingTreeNode> node4 = node5->GetPrevSibling();
RefPtr<HitTestingTreeNode> node2 = node4->GetPrevSibling();
RefPtr<HitTestingTreeNode> node1 = node2->GetPrevSibling();
RefPtr<HitTestingTreeNode> node3 = node2->GetLastChild();
RefPtr<HitTestingTreeNode> node9 = node5->GetLastChild();
RefPtr<HitTestingTreeNode> node8 = node9->GetPrevSibling();
RefPtr<HitTestingTreeNode> node6 = node8->GetPrevSibling();
RefPtr<HitTestingTreeNode> node7 = node6->GetLastChild();
EXPECT_EQ(nullptr, node1->GetPrevSibling());
EXPECT_EQ(nullptr, node3->GetPrevSibling());
EXPECT_EQ(nullptr, node6->GetPrevSibling());
EXPECT_EQ(nullptr, node7->GetPrevSibling());
EXPECT_EQ(nullptr, node1->GetLastChild());
EXPECT_EQ(nullptr, node3->GetLastChild());
EXPECT_EQ(nullptr, node4->GetLastChild());
EXPECT_EQ(nullptr, node7->GetLastChild());
EXPECT_EQ(nullptr, node8->GetLastChild());
EXPECT_EQ(nullptr, node9->GetLastChild());
RefPtr<AsyncPanZoomController> hit = GetTargetAPZC(ScreenPoint(25, 25));
EXPECT_EQ(ApzcOf(layers[1]), hit.get());
hit = GetTargetAPZC(ScreenPoint(275, 375));
EXPECT_EQ(ApzcOf(layers[9]), hit.get());
hit = GetTargetAPZC(ScreenPoint(250, 100));
EXPECT_EQ(ApzcOf(layers[7]), hit.get());
}
TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
// The main purpose of this test is to verify that touch-start events (or anything
// that starts a new input block) don't ever get untransformed. This should always
// hold because the APZ code should flush repaints when we start a new input block
// and the transform to gecko space should be empty.
CreateSimpleScrollingLayer();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
// At this point, the following holds (all coordinates in screen pixels):
// layers[0] has content from (0,0)-(500,500), clipped by composition bounds (0,0)-(200,200)
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
EXPECT_CALL(check, Call("post-first-touch-start"));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
EXPECT_CALL(check, Call("post-second-fling"));
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(1));
EXPECT_CALL(check, Call("post-second-touch-start"));
}
// This first pan will move the APZC by 50 pixels, and dispatch a paint request.
ApzcPanNoFling(apzcroot, mcc, 100, 50);
// Verify that a touch start doesn't get untransformed
ScreenIntPoint touchPoint(50, 50);
MultiTouchInput mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(0, touchPoint, ScreenSize(0, 0), 0, 0));
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
check.Call("post-first-touch-start");
// Send a touchend to clear state
mti.mType = MultiTouchInput::MULTITOUCH_END;
manager->ReceiveInputEvent(mti, nullptr, nullptr);
mcc->AdvanceByMillis(1000);
// Now do two pans. The first of these will dispatch a repaint request, as above.
// The second will get stuck in the paint throttler because the first one doesn't
// get marked as "completed", so this will result in a non-empty LD transform.
// (Note that any outstanding repaint requests from the first half of this test
// don't impact this half because we advance the time by 1 second, which will trigger
// the max-wait-exceeded codepath in the paint throttler).
ApzcPanNoFling(apzcroot, mcc, 100, 50);
check.Call("post-second-fling");
ApzcPanNoFling(apzcroot, mcc, 100, 50);
// Ensure that a touch start again doesn't get untransformed by flushing
// a repaint
mti.mType = MultiTouchInput::MULTITOUCH_START;
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
check.Call("post-second-touch-start");
mti.mType = MultiTouchInput::MULTITOUCH_END;
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
}
TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
// The purpose of this test is to ensure that wheel events trigger a repaint
// flush as per bug 1166871, and that the wheel event untransform is a no-op.
CreateSimpleScrollingLayer();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
TestAsyncPanZoomController* apzcroot = ApzcOf(root);
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
ScreenPoint origin(100, 50);
for (int i = 0; i < 3; i++) {
ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
origin, 0, 10);
EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
EXPECT_EQ(origin, swi.mOrigin);
AsyncTransform viewTransform;
ParentLayerPoint point;
apzcroot->SampleContentTransformForFrame(&viewTransform, point);
EXPECT_EQ(0, point.x);
EXPECT_EQ((i + 1) * 10, point.y);
EXPECT_EQ(0, viewTransform.mTranslation.x);
EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
mcc->AdvanceByMillis(5);
}
}
TEST_F(APZHitTestingTester, Bug1148350) {
CreateBug1148350LayerTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
MockFunction<void(std::string checkPointName)> check;
{
InSequence s;
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped without transform"));
EXPECT_CALL(*mcc, HandleSingleTap(CSSPoint(100, 100), 0, ApzcOf(layers[1])->GetGuid())).Times(1);
EXPECT_CALL(check, Call("Tapped with interleaved transform"));
}
Tap(manager, 100, 100, mcc, TimeDuration::FromMilliseconds(100));
mcc->RunThroughDelayedTasks();
check.Call("Tapped without transform");
uint64_t blockId;
TouchDown(manager, 100, 100, mcc->Time(), &blockId);
if (gfxPrefs::TouchActionEnabled()) {
SetDefaultAllowedTouchBehavior(manager, blockId);
}
mcc->AdvanceByMillis(100);
layers[0]->SetVisibleRegion(LayerIntRegion(LayerIntRect(0,50,200,150)));
layers[0]->SetBaseTransform(Matrix4x4::Translation(0, 50, 0));
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
TouchUp(manager, 100, 100, mcc->Time());
mcc->RunThroughDelayedTasks();
check.Call("Tapped with interleaved transform");
}

Просмотреть файл

@ -0,0 +1,430 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
class APZOverscrollHandoffTester : public APZCTreeManagerTester {
protected:
UniquePtr<ScopedLayerTreeRegistration> registration;
TestAsyncPanZoomController* rootApzc;
void CreateOverscrollHandoffLayerTree1() {
const char* layerTreeSyntax = "c(t)";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0, 0, 100, 100)),
nsIntRegion(IntRect(0, 50, 100, 50))
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
SetScrollHandoff(layers[1], root);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
}
void CreateOverscrollHandoffLayerTree2() {
const char* layerTreeSyntax = "c(c(t))";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0, 0, 100, 100)),
nsIntRegion(IntRect(0, 0, 100, 100)),
nsIntRegion(IntRect(0, 50, 100, 50))
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 200, 200));
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 2, CSSRect(-100, -100, 200, 200));
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
SetScrollHandoff(layers[1], root);
SetScrollHandoff(layers[2], layers[1]);
// No ScopedLayerTreeRegistration as that just needs to be done once per test
// and this is the second layer tree for a particular test.
MOZ_ASSERT(registration);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
}
void CreateOverscrollHandoffLayerTree3() {
const char* layerTreeSyntax = "c(c(t)c(t))";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0, 0, 100, 100)), // root
nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling parent 1
nsIntRegion(IntRect(0, 0, 100, 50)), // scrolling child 1
nsIntRegion(IntRect(0, 50, 100, 50)), // scrolling parent 2
nsIntRegion(IntRect(0, 50, 100, 50)) // scrolling child 2
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, 100));
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 100));
SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 50, 100, 100));
SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 3, CSSRect(0, 50, 100, 100));
SetScrollHandoff(layers[2], layers[1]);
SetScrollHandoff(layers[4], layers[3]);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
}
void CreateScrollgrabLayerTree(bool makeParentScrollable = true) {
const char* layerTreeSyntax = "c(t)";
nsIntRegion layerVisibleRegion[] = {
nsIntRegion(IntRect(0, 0, 100, 100)), // scroll-grabbing parent
nsIntRegion(IntRect(0, 20, 100, 80)) // child
};
root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, nullptr, lm, layers);
float parentHeight = makeParentScrollable ? 120 : 100;
SetScrollableFrameMetrics(root, FrameMetrics::START_SCROLL_ID, CSSRect(0, 0, 100, parentHeight));
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1, CSSRect(0, 0, 100, 200));
SetScrollHandoff(layers[1], root);
registration = MakeUnique<ScopedLayerTreeRegistration>(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
rootApzc = ApzcOf(root);
rootApzc->GetFrameMetrics().SetHasScrollgrab(true);
}
void TestFlingAcceleration() {
// Jack up the fling acceleration multiplier so we can easily determine
// whether acceleration occured.
const float kAcceleration = 100.0f;
SCOPED_GFX_PREF(APZFlingAccelBaseMultiplier, float, kAcceleration);
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
// Pan once, enough to fully scroll the scrollgrab parent and then scroll
// and fling the child.
Pan(manager, mcc, 70, 40);
// Give the fling animation a chance to start.
SampleAnimationsOnce();
float childVelocityAfterFling1 = childApzc->GetVelocityVector().y;
// Pan again.
Pan(manager, mcc, 70, 40);
// Give the fling animation a chance to start.
// This time it should be accelerated.
SampleAnimationsOnce();
float childVelocityAfterFling2 = childApzc->GetVelocityVector().y;
// We should have accelerated once.
// The division by 2 is to account for friction.
EXPECT_GT(childVelocityAfterFling2,
childVelocityAfterFling1 * kAcceleration / 2);
// We should not have accelerated twice.
// The division by 4 is to account for friction.
EXPECT_LE(childVelocityAfterFling2,
childVelocityAfterFling1 * kAcceleration * kAcceleration / 4);
}
};
// Here we test that if the processing of a touch block is deferred while we
// wait for content to send a prevent-default message, overscroll is still
// handed off correctly when the block is processed.
TEST_F(APZOverscrollHandoffTester, DeferredInputEventProcessing) {
// Set up the APZC tree.
CreateOverscrollHandoffLayerTree1();
TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
// Enable touch-listeners so that we can separate the queueing of input
// events from them being processed.
childApzc->SetWaitForMainThread();
// Queue input events for a pan.
uint64_t blockId = 0;
ApzcPanNoFling(childApzc, mcc, 90, 30, &blockId);
// Allow the pan to be processed.
childApzc->ContentReceivedInputBlock(blockId, false);
childApzc->ConfirmTarget(blockId);
// Make sure overscroll was handed off correctly.
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
}
// Here we test that if the layer structure changes in between two input
// blocks being queued, and the first block is only processed after the second
// one has been queued, overscroll handoff for the first block follows
// the original layer structure while overscroll handoff for the second block
// follows the new layer structure.
TEST_F(APZOverscrollHandoffTester, LayerStructureChangesWhileEventsArePending) {
// Set up an initial APZC tree.
CreateOverscrollHandoffLayerTree1();
TestAsyncPanZoomController* childApzc = ApzcOf(layers[1]);
// Enable touch-listeners so that we can separate the queueing of input
// events from them being processed.
childApzc->SetWaitForMainThread();
// Queue input events for a pan.
uint64_t blockId = 0;
ApzcPanNoFling(childApzc, mcc, 90, 30, &blockId);
// Modify the APZC tree to insert a new APZC 'middle' into the handoff chain
// between the child and the root.
CreateOverscrollHandoffLayerTree2();
RefPtr<Layer> middle = layers[1];
childApzc->SetWaitForMainThread();
TestAsyncPanZoomController* middleApzc = ApzcOf(middle);
// Queue input events for another pan.
uint64_t secondBlockId = 0;
ApzcPanNoFling(childApzc, mcc, 30, 90, &secondBlockId);
// Allow the first pan to be processed.
childApzc->ContentReceivedInputBlock(blockId, false);
childApzc->ConfirmTarget(blockId);
// Make sure things have scrolled according to the handoff chain in
// place at the time the touch-start of the first pan was queued.
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(0, middleApzc->GetFrameMetrics().GetScrollOffset().y);
// Allow the second pan to be processed.
childApzc->ContentReceivedInputBlock(secondBlockId, false);
childApzc->ConfirmTarget(secondBlockId);
// Make sure things have scrolled according to the handoff chain in
// place at the time the touch-start of the second pan was queued.
EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
}
// Test that putting a second finger down on an APZC while a down-chain APZC
// is overscrolled doesn't result in being stuck in overscroll.
TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1073250) {
// Enable overscrolling.
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
CreateOverscrollHandoffLayerTree1();
TestAsyncPanZoomController* child = ApzcOf(layers[1]);
// Pan, causing the parent APZC to overscroll.
Pan(manager, mcc, 10, 40, true /* keep finger down */);
EXPECT_FALSE(child->IsOverscrolled());
EXPECT_TRUE(rootApzc->IsOverscrolled());
// Put a second finger down.
MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
// Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
// Release the fingers.
MultiTouchInput fingersUp = secondFingerDown;
fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
// Allow any animations to run their course.
child->AdvanceAnimationsUntilEnd();
rootApzc->AdvanceAnimationsUntilEnd();
// Make sure nothing is overscrolled.
EXPECT_FALSE(child->IsOverscrolled());
EXPECT_FALSE(rootApzc->IsOverscrolled());
}
// This is almost exactly like StuckInOverscroll_Bug1073250, except the
// APZC receiving the input events for the first touch block is the child
// (and thus not the same APZC that overscrolls, which is the parent).
TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1231228) {
// Enable overscrolling.
SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
CreateOverscrollHandoffLayerTree1();
TestAsyncPanZoomController* child = ApzcOf(layers[1]);
// Pan, causing the parent APZC to overscroll.
Pan(manager, mcc, 60, 90, true /* keep finger down */);
EXPECT_FALSE(child->IsOverscrolled());
EXPECT_TRUE(rootApzc->IsOverscrolled());
// Put a second finger down.
MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
// Use the same touch identifier for the first touch (0) as Pan(). (A bit hacky.)
secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
manager->ReceiveInputEvent(secondFingerDown, nullptr, nullptr);
// Release the fingers.
MultiTouchInput fingersUp = secondFingerDown;
fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
manager->ReceiveInputEvent(fingersUp, nullptr, nullptr);
// Allow any animations to run their course.
child->AdvanceAnimationsUntilEnd();
rootApzc->AdvanceAnimationsUntilEnd();
// Make sure nothing is overscrolled.
EXPECT_FALSE(child->IsOverscrolled());
EXPECT_FALSE(rootApzc->IsOverscrolled());
}
// Test that flinging in a direction where one component of the fling goes into
// overscroll but the other doesn't, results in just the one component being
// handed off to the parent, while the original APZC continues flinging in the
// other direction.
TEST_F(APZOverscrollHandoffTester, PartialFlingHandoff) {
CreateOverscrollHandoffLayerTree1();
// Fling up and to the left. The child APZC has room to scroll up, but not
// to the left, so the horizontal component of the fling should be handed
// off to the parent APZC.
Pan(manager, mcc, ScreenPoint(90, 90), ScreenPoint(55, 55));
RefPtr<TestAsyncPanZoomController> parent = ApzcOf(root);
RefPtr<TestAsyncPanZoomController> child = ApzcOf(layers[1]);
// Advance the child's fling animation once to give the partial handoff
// a chance to occur.
mcc->AdvanceByMillis(10);
child->AdvanceAnimations(mcc->Time());
// Assert that partial handoff has occurred.
child->AssertStateIsFling();
parent->AssertStateIsFling();
}
// Here we test that if two flings are happening simultaneously, overscroll
// is handed off correctly for each.
TEST_F(APZOverscrollHandoffTester, SimultaneousFlings) {
// Set up an initial APZC tree.
CreateOverscrollHandoffLayerTree3();
RefPtr<TestAsyncPanZoomController> parent1 = ApzcOf(layers[1]);
RefPtr<TestAsyncPanZoomController> child1 = ApzcOf(layers[2]);
RefPtr<TestAsyncPanZoomController> parent2 = ApzcOf(layers[3]);
RefPtr<TestAsyncPanZoomController> child2 = ApzcOf(layers[4]);
// Pan on the lower child.
Pan(child2, mcc, 45, 5);
// Pan on the upper child.
Pan(child1, mcc, 95, 55);
// Check that child1 and child2 are in a FLING state.
child1->AssertStateIsFling();
child2->AssertStateIsFling();
// Advance the animations on child1 and child2 until their end.
child1->AdvanceAnimationsUntilEnd();
child2->AdvanceAnimationsUntilEnd();
// Check that the flings have been handed off to the parents.
child1->AssertStateIsReset();
parent1->AssertStateIsFling();
child2->AssertStateIsReset();
parent2->AssertStateIsFling();
}
TEST_F(APZOverscrollHandoffTester, Scrollgrab) {
// Set up the layer tree
CreateScrollgrabLayerTree();
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
// Pan on the child, enough to fully scroll the scrollgrab parent (20 px)
// and leave some more (another 15 px) for the child.
Pan(childApzc, mcc, 80, 45);
// Check that the parent and child have scrolled as much as we expect.
EXPECT_EQ(20, rootApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(15, childApzc->GetFrameMetrics().GetScrollOffset().y);
}
TEST_F(APZOverscrollHandoffTester, ScrollgrabFling) {
// Set up the layer tree
CreateScrollgrabLayerTree();
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
// Pan on the child, not enough to fully scroll the scrollgrab parent.
Pan(childApzc, mcc, 80, 70);
// Check that it is the scrollgrab parent that's in a fling, not the child.
rootApzc->AssertStateIsFling();
childApzc->AssertStateIsReset();
}
TEST_F(APZOverscrollHandoffTester, ScrollgrabFlingAcceleration1) {
CreateScrollgrabLayerTree(true /* make parent scrollable */);
TestFlingAcceleration();
}
TEST_F(APZOverscrollHandoffTester, ScrollgrabFlingAcceleration2) {
CreateScrollgrabLayerTree(false /* do not make parent scrollable */);
TestFlingAcceleration();
}
TEST_F(APZOverscrollHandoffTester, ImmediateHandoffDisallowed_Pan) {
SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
CreateOverscrollHandoffLayerTree1();
RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
// Pan on the child, enough to scroll it to its end and have scroll
// left to hand off. Since immediate handoff is disallowed, we expect
// the leftover scroll not to be handed off.
Pan(childApzc, mcc, 60, 5);
// Verify that the parent has not scrolled.
EXPECT_EQ(50, childApzc->GetFrameMetrics().GetScrollOffset().y);
EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
// Pan again on the child. This time, since the child was scrolled to
// its end when the gesture began, we expect the scroll to be handed off.
Pan(childApzc, mcc, 60, 50);
// Verify that the parent scrolled.
EXPECT_EQ(10, parentApzc->GetFrameMetrics().GetScrollOffset().y);
}
TEST_F(APZOverscrollHandoffTester, ImmediateHandoffDisallowed_Fling) {
SCOPED_GFX_PREF(APZAllowImmediateHandoff, bool, false);
CreateOverscrollHandoffLayerTree1();
RefPtr<TestAsyncPanZoomController> parentApzc = ApzcOf(root);
RefPtr<TestAsyncPanZoomController> childApzc = ApzcOf(layers[1]);
// Pan on the child, enough to get very close to the end, so that the
// subsequent fling reaches the end and has leftover velocity to hand off.
Pan(childApzc, mcc, 60, 12);
// Allow the fling to run its course.
childApzc->AdvanceAnimationsUntilEnd();
parentApzc->AdvanceAnimationsUntilEnd();
// Verify that the parent has not scrolled.
// The first comparison needs to be an ASSERT_NEAR because the fling
// computations are such that the final scroll position can be within
// COORDINATE_EPSILON of the end rather than right at the end.
ASSERT_NEAR(50, childApzc->GetFrameMetrics().GetScrollOffset().y, COORDINATE_EPSILON);
EXPECT_EQ(0, parentApzc->GetFrameMetrics().GetScrollOffset().y);
// Pan again on the child. This time, since the child was scrolled to
// its end when the gesture began, we expect the scroll to be handed off.
Pan(childApzc, mcc, 60, 50);
// Allow the fling to run its course. The fling should also be handed off.
childApzc->AdvanceAnimationsUntilEnd();
parentApzc->AdvanceAnimationsUntilEnd();
// Verify that the parent scrolled from the fling.
EXPECT_GT(parentApzc->GetFrameMetrics().GetScrollOffset().y, 10);
}

Просмотреть файл

@ -0,0 +1,124 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCBasicTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
class APZCPanningTester : public APZCBasicTester {
protected:
void DoPanTest(bool aShouldTriggerScroll, bool aShouldBeConsumed, uint32_t aBehavior)
{
if (aShouldTriggerScroll) {
// One repaint request for each pan.
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
} else {
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
}
int touchStart = 50;
int touchEnd = 10;
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(aBehavior);
// Pan down
PanAndCheckStatus(apzc, mcc, touchStart, touchEnd, aShouldBeConsumed, &allowedTouchBehaviors);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
if (aShouldTriggerScroll) {
EXPECT_EQ(ParentLayerPoint(0, -(touchEnd-touchStart)), pointOut);
EXPECT_NE(AsyncTransform(), viewTransformOut);
} else {
EXPECT_EQ(ParentLayerPoint(), pointOut);
EXPECT_EQ(AsyncTransform(), viewTransformOut);
}
// Clear the fling from the previous pan, or stopping it will
// consume the next touchstart
apzc->CancelAnimation();
// Pan back
PanAndCheckStatus(apzc, mcc, touchEnd, touchStart, aShouldBeConsumed, &allowedTouchBehaviors);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(ParentLayerPoint(), pointOut);
EXPECT_EQ(AsyncTransform(), viewTransformOut);
}
void DoPanWithPreventDefaultTest()
{
MakeApzcWaitForMainThread();
int touchStart = 50;
int touchEnd = 10;
ParentLayerPoint pointOut;
AsyncTransform viewTransformOut;
uint64_t blockId = 0;
// Pan down
nsTArray<uint32_t> allowedTouchBehaviors;
allowedTouchBehaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
PanAndCheckStatus(apzc, mcc, touchStart, touchEnd, true, &allowedTouchBehaviors, &blockId);
// Send the signal that content has handled and preventDefaulted the touch
// events. This flushes the event queue.
apzc->ContentReceivedInputBlock(blockId, true);
apzc->SampleContentTransformForFrame(&viewTransformOut, pointOut);
EXPECT_EQ(ParentLayerPoint(), pointOut);
EXPECT_EQ(AsyncTransform(), viewTransformOut);
apzc->AssertStateIsReset();
}
};
TEST_F(APZCPanningTester, Pan) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::NONE);
}
// In the each of the following 4 pan tests we are performing two pan gestures: vertical pan from top
// to bottom and back - from bottom to top.
// According to the pointer-events/touch-action spec AUTO and PAN_Y touch-action values allow vertical
// scrolling while NONE and PAN_X forbid it. The first parameter of DoPanTest method specifies this
// behavior.
// However, the events will be marked as consumed even if the behavior in PAN_X, because the user could
// move their finger horizontally too - APZ has no way of knowing beforehand and so must consume the
// events.
TEST_F(APZCPanningTester, PanWithTouchActionAuto) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN
| mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
}
TEST_F(APZCPanningTester, PanWithTouchActionNone) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(false, false, 0);
}
TEST_F(APZCPanningTester, PanWithTouchActionPanX) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(false, true, mozilla::layers::AllowedTouchBehavior::HORIZONTAL_PAN);
}
TEST_F(APZCPanningTester, PanWithTouchActionPanY) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanTest(true, true, mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
}
TEST_F(APZCPanningTester, PanWithPreventDefaultAndTouchAction) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
DoPanWithPreventDefaultTest();
}
TEST_F(APZCPanningTester, PanWithPreventDefault) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
DoPanWithPreventDefaultTest();
}

Просмотреть файл

@ -0,0 +1,175 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCBasicTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
class APZCPinchTester : public APZCBasicTester {
public:
explicit APZCPinchTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
: APZCBasicTester(aGestureBehavior)
{
}
protected:
FrameMetrics GetPinchableFrameMetrics()
{
FrameMetrics fm;
fm.SetCompositionBounds(ParentLayerRect(200, 200, 100, 200));
fm.SetScrollableRect(CSSRect(0, 0, 980, 1000));
fm.SetScrollOffset(CSSPoint(300, 300));
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
// APZC only allows zooming on the root scrollable frame.
fm.SetIsRootContent(true);
// the visible area of the document in CSS pixels is x=300 y=300 w=50 h=100
return fm;
}
void DoPinchTest(bool aShouldTriggerPinch,
nsTArray<uint32_t> *aAllowedTouchBehaviors = nullptr)
{
apzc->SetFrameMetrics(GetPinchableFrameMetrics());
MakeApzcZoomable();
if (aShouldTriggerPinch) {
// One repaint request for each gesture.
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(2);
} else {
EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(0);
}
int touchInputId = 0;
if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
PinchWithTouchInputAndCheckStatus(apzc, 250, 300, 1.25, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
} else {
PinchWithPinchInputAndCheckStatus(apzc, 250, 300, 1.25, aShouldTriggerPinch);
}
FrameMetrics fm = apzc->GetFrameMetrics();
if (aShouldTriggerPinch) {
// the visible area of the document in CSS pixels is now x=305 y=310 w=40 h=80
EXPECT_EQ(2.5f, fm.GetZoom().ToScaleFactor().scale);
EXPECT_EQ(305, fm.GetScrollOffset().x);
EXPECT_EQ(310, fm.GetScrollOffset().y);
} else {
// The frame metrics should stay the same since touch-action:none makes
// apzc ignore pinch gestures.
EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
EXPECT_EQ(300, fm.GetScrollOffset().x);
EXPECT_EQ(300, fm.GetScrollOffset().y);
}
// part 2 of the test, move to the top-right corner of the page and pinch and
// make sure we stay in the correct spot
fm.SetZoom(CSSToParentLayerScale2D(2.0, 2.0));
fm.SetScrollOffset(CSSPoint(930, 5));
apzc->SetFrameMetrics(fm);
// the visible area of the document in CSS pixels is x=930 y=5 w=50 h=100
if (mGestureBehavior == AsyncPanZoomController::USE_GESTURE_DETECTOR) {
PinchWithTouchInputAndCheckStatus(apzc, 250, 300, 0.5, touchInputId, aShouldTriggerPinch, aAllowedTouchBehaviors);
} else {
PinchWithPinchInputAndCheckStatus(apzc, 250, 300, 0.5, aShouldTriggerPinch);
}
fm = apzc->GetFrameMetrics();
if (aShouldTriggerPinch) {
// the visible area of the document in CSS pixels is now x=880 y=0 w=100 h=200
EXPECT_EQ(1.0f, fm.GetZoom().ToScaleFactor().scale);
EXPECT_EQ(880, fm.GetScrollOffset().x);
EXPECT_EQ(0, fm.GetScrollOffset().y);
} else {
EXPECT_EQ(2.0f, fm.GetZoom().ToScaleFactor().scale);
EXPECT_EQ(930, fm.GetScrollOffset().x);
EXPECT_EQ(5, fm.GetScrollOffset().y);
}
}
};
class APZCPinchGestureDetectorTester : public APZCPinchTester {
public:
APZCPinchGestureDetectorTester()
: APZCPinchTester(AsyncPanZoomController::USE_GESTURE_DETECTOR)
{
}
};
TEST_F(APZCPinchTester, Pinch_DefaultGestures_NoTouchAction) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
DoPinchTest(true);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_NoTouchAction) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, false);
DoPinchTest(true);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNone) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
nsTArray<uint32_t> behaviors = { mozilla::layers::AllowedTouchBehavior::NONE,
mozilla::layers::AllowedTouchBehavior::NONE };
DoPinchTest(false, &behaviors);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionZoom) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
DoPinchTest(true, &behaviors);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_UseGestureDetector_TouchActionNotAllowZoom) {
SCOPED_GFX_PREF(TouchActionEnabled, bool, true);
nsTArray<uint32_t> behaviors;
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::VERTICAL_PAN);
behaviors.AppendElement(mozilla::layers::AllowedTouchBehavior::PINCH_ZOOM);
DoPinchTest(false, &behaviors);
}
TEST_F(APZCPinchGestureDetectorTester, Pinch_PreventDefault) {
FrameMetrics originalMetrics = GetPinchableFrameMetrics();
apzc->SetFrameMetrics(originalMetrics);
MakeApzcWaitForMainThread();
MakeApzcZoomable();
int touchInputId = 0;
uint64_t blockId = 0;
PinchWithTouchInput(apzc, 250, 300, 1.25, touchInputId, nullptr, nullptr, &blockId);
// Send the prevent-default notification for the touch block
apzc->ContentReceivedInputBlock(blockId, true);
// verify the metrics didn't change (i.e. the pinch was ignored)
FrameMetrics fm = apzc->GetFrameMetrics();
EXPECT_EQ(originalMetrics.GetZoom(), fm.GetZoom());
EXPECT_EQ(originalMetrics.GetScrollOffset().x, fm.GetScrollOffset().x);
EXPECT_EQ(originalMetrics.GetScrollOffset().y, fm.GetScrollOffset().y);
apzc->AssertStateIsReset();
}
TEST_F(APZCPinchTester, Panning_TwoFinger_ZoomDisabled) {
// set up APZ
apzc->SetFrameMetrics(GetPinchableFrameMetrics());
MakeApzcUnzoomable();
nsEventStatus statuses[3]; // scalebegin, scale, scaleend
PinchWithPinchInput(apzc, 250, 350, 200, 300, 10, &statuses);
FrameMetrics fm = apzc->GetFrameMetrics();
// It starts from (300, 300), then moves the focus point from (250, 350) to
// (200, 300) pans by (50, 50) screen pixels, but there is a 2x zoom, which
// causes the scroll offset to change by half of that (25, 25) pixels.
EXPECT_EQ(325, fm.GetScrollOffset().x);
EXPECT_EQ(325, fm.GetScrollOffset().y);
EXPECT_EQ(2.0, fm.GetZoom().ToScaleFactor().scale);
}

Просмотреть файл

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "APZCTreeManagerTester.h"
#include "APZTestCommon.h"
#include "InputUtils.h"
TEST_F(APZCTreeManagerTester, ScrollablePaintedLayers) {
CreateSimpleMultiLayerTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
// both layers have the same scrollId
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID);
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
TestAsyncPanZoomController* nullAPZC = nullptr;
// so they should have the same APZC
EXPECT_FALSE(layers[0]->HasScrollableFrameMetrics());
EXPECT_NE(nullAPZC, ApzcOf(layers[1]));
EXPECT_NE(nullAPZC, ApzcOf(layers[2]));
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
// Change the scrollId of layers[1], and verify the APZC changes
SetScrollableFrameMetrics(layers[1], FrameMetrics::START_SCROLL_ID + 1);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
EXPECT_NE(ApzcOf(layers[1]), ApzcOf(layers[2]));
// Change the scrollId of layers[2] to match that of layers[1], ensure we get the same
// APZC for both again
SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
EXPECT_EQ(ApzcOf(layers[1]), ApzcOf(layers[2]));
}
TEST_F(APZCTreeManagerTester, Bug1068268) {
CreatePotentiallyLeakingTree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
RefPtr<HitTestingTreeNode> root = manager->GetRootNode();
RefPtr<HitTestingTreeNode> node2 = root->GetFirstChild()->GetFirstChild();
RefPtr<HitTestingTreeNode> node5 = root->GetLastChild()->GetLastChild();
EXPECT_EQ(ApzcOf(layers[2]), node5->GetApzc());
EXPECT_EQ(ApzcOf(layers[2]), node2->GetApzc());
EXPECT_EQ(ApzcOf(layers[0]), ApzcOf(layers[2])->GetParent());
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[5]));
EXPECT_EQ(node2->GetFirstChild(), node2->GetLastChild());
EXPECT_EQ(ApzcOf(layers[3]), node2->GetLastChild()->GetApzc());
EXPECT_EQ(node5->GetFirstChild(), node5->GetLastChild());
EXPECT_EQ(ApzcOf(layers[6]), node5->GetLastChild()->GetApzc());
EXPECT_EQ(ApzcOf(layers[2]), ApzcOf(layers[3])->GetParent());
EXPECT_EQ(ApzcOf(layers[5]), ApzcOf(layers[6])->GetParent());
}
TEST_F(APZCTreeManagerTester, Bug1194876) {
CreateBug1194876Tree();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
uint64_t blockId;
nsTArray<ScrollableLayerGuid> targets;
// First touch goes down, APZCTM will hit layers[1] because it is on top of
// layers[0], but we tell it the real target APZC is layers[0].
MultiTouchInput mti;
mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
mti.mTouches.AppendElement(SingleTouchData(0, ParentLayerPoint(25, 50), ScreenSize(0, 0), 0, 0));
manager->ReceiveInputEvent(mti, nullptr, &blockId);
manager->ContentReceivedInputBlock(blockId, false);
targets.AppendElement(ApzcOf(layers[0])->GetGuid());
manager->SetTargetAPZC(blockId, targets);
// Around here, the above touch will get processed by ApzcOf(layers[0])
// Second touch goes down (first touch remains down), APZCTM will again hit
// layers[1]. Again we tell it both touches landed on layers[0], but because
// layers[1] is the RCD layer, it will end up being the multitouch target.
mti.mTouches.AppendElement(SingleTouchData(1, ParentLayerPoint(75, 50), ScreenSize(0, 0), 0, 0));
manager->ReceiveInputEvent(mti, nullptr, &blockId);
manager->ContentReceivedInputBlock(blockId, false);
targets.AppendElement(ApzcOf(layers[0])->GetGuid());
manager->SetTargetAPZC(blockId, targets);
// Around here, the above multi-touch will get processed by ApzcOf(layers[1]).
// We want to ensure that ApzcOf(layers[0]) has had its state cleared, because
// otherwise it will do things like dispatch spurious long-tap events.
EXPECT_CALL(*mcc, HandleLongTap(_, _, _, _)).Times(0);
}
TEST_F(APZCTreeManagerTester, Bug1198900) {
// This is just a test that cancels a wheel event to make sure it doesn't
// crash.
CreateSimpleDTCScrollingLayer();
ScopedLayerTreeRegistration registration(manager, 0, root, mcc);
manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
ScreenPoint origin(100, 50);
ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0,
ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
origin, 0, 10);
uint64_t blockId;
manager->ReceiveInputEvent(swi, nullptr, &blockId);
manager->ContentReceivedInputBlock(blockId, /* preventDefault= */ true);
}

Просмотреть файл

@ -5,7 +5,14 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
'TestAsyncPanZoomController.cpp',
'TestBasic.cpp',
'TestEventRegions.cpp',
'TestGestureDetector.cpp',
'TestHitTesting.cpp',
'TestOverscrollHandoff.cpp',
'TestPanning.cpp',
'TestPinching.cpp',
'TestTreeManager.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')

Просмотреть файл

@ -536,6 +536,7 @@ window.onload = function() {
});
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
</script>
</body>

Просмотреть файл

@ -44,6 +44,7 @@ window.onload = function() {
});
}
SimpleTest.testInChaosMode();
SimpleTest.waitForExplicitFinish();
</script>
</body>

Просмотреть файл

@ -10,6 +10,7 @@
#include "gfxPrefs.h"
#include "LayersLogging.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Move.h"
#include "mozilla/Preferences.h"
#include "mozilla/TouchEvents.h"

Просмотреть файл

@ -441,6 +441,9 @@ ImageHost::SetCompositor(Compositor* aCompositor)
img.mFrontBuffer->SetCompositor(aCompositor);
}
}
if (mImageHostOverlay) {
mImageHostOverlay->SetCompositor(aCompositor);
}
CompositableHost::SetCompositor(aCompositor);
}
@ -586,6 +589,9 @@ ImageHostOverlay::ImageHostOverlay()
ImageHostOverlay::~ImageHostOverlay()
{
if (mCompositor) {
mCompositor->RemoveImageHostOverlay(this);
}
MOZ_COUNT_DTOR(ImageHostOverlay);
}
@ -601,6 +607,18 @@ ImageHostOverlay::IsValid(OverlaySource aOverlay)
return false;
}
void
ImageHostOverlay::SetCompositor(Compositor* aCompositor)
{
if (mCompositor && (mCompositor != aCompositor)) {
mCompositor->RemoveImageHostOverlay(this);
}
if (aCompositor) {
aCompositor->AddImageHostOverlay(this);
}
mCompositor = aCompositor;
}
void
ImageHostOverlay::Composite(Compositor* aCompositor,
uint32_t aFlashCounter,
@ -612,6 +630,8 @@ ImageHostOverlay::Composite(Compositor* aCompositor,
const gfx::Rect& aClipRect,
const nsIntRegion* aVisibleRegion)
{
MOZ_ASSERT(mCompositor == aCompositor);
if (mOverlay.handle().type() == OverlayHandle::Tnull_t) {
return;
}

Просмотреть файл

@ -158,6 +158,8 @@ public:
static bool IsValid(OverlaySource aOverlay);
void SetCompositor(Compositor* aCompositor);
virtual void Composite(Compositor* aCompositor,
uint32_t aFlashCounter,
LayerComposite* aLayer,
@ -173,6 +175,7 @@ public:
virtual gfx::IntSize GetImageSize() const;
virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
protected:
RefPtr<Compositor> mCompositor;
gfx::IntRect mPictureRect;
OverlaySource mOverlay;
};

Просмотреть файл

@ -809,7 +809,9 @@ LayerManagerComposite::Render(const nsIntRegion& aInvalidRegion)
if (!mTarget && !haveLayerEffects &&
gfxPrefs::Composer2DCompositionEnabled() &&
composer2D && composer2D->HasHwc() && composer2D->TryRenderWithHwc(mRoot,
mCompositor->GetWidget(), mGeometryChanged))
mCompositor->GetWidget(),
mGeometryChanged,
mCompositor->HasImageHostOverlays()))
{
LayerScope::SetHWComposed();
if (mFPS) {

Просмотреть файл

@ -49,8 +49,10 @@ public:
* Currently, when TryRender() returns true, the entire framebuffer
* must have been rendered.
*/
virtual bool TryRenderWithHwc(Layer* aRoot, nsIWidget* aWidget,
bool aGeometryChanged) = 0;
virtual bool TryRenderWithHwc(Layer* aRoot,
nsIWidget* aWidget,
bool aGeometryChanged,
bool aHasImageHostOverlays) = 0;
/**
* Return true if Composer2D does composition. Return false if Composer2D

Просмотреть файл

@ -71,6 +71,18 @@ BindMaskForProgram(ShaderProgramOGL* aProgram, TextureSourceOGL* aSourceMask,
aProgram->SetMaskLayerTransform(aTransform);
}
void
CompositorOGL::BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit)
{
MOZ_ASSERT(aBackdrop);
mGLContext->fActiveTexture(aTexUnit);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, aBackdrop);
mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
mGLContext->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
aProgram->SetBackdropTextureUnit(aTexUnit - LOCAL_GL_TEXTURE0);
}
CompositorOGL::CompositorOGL(nsIWidget *aWidget, int aSurfaceWidth,
int aSurfaceHeight, bool aUseExternalSurfaceSize)
: mWidget(aWidget)
@ -474,7 +486,7 @@ CompositorOGL::PrepareViewport(CompositingRenderTargetOGL* aRenderTarget)
// Set the viewport correctly.
mGLContext->fViewport(0, 0, size.width, size.height);
mRenderBound = Rect(0, 0, size.width, size.height);
mRenderBounds = Rect(0, 0, size.width, size.height);
mViewportSize = size;
@ -707,9 +719,16 @@ CompositorOGL::BeginFrame(const nsIntRegion& aInvalidRegion,
}
void
CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
CompositorOGL::CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
GLuint aSourceFrameBuffer,
GLuint *aFBO, GLuint *aTexture)
{
*aTexture = CreateTexture(aRect, aCopyFromSource, aSourceFrameBuffer);
mGLContext->fGenFramebuffers(1, aFBO);
}
GLuint
CompositorOGL::CreateTexture(const IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer)
{
// we're about to create a framebuffer backed by textures to use as an intermediate
// surface. What to do if its size (as given by aRect) would exceed the
@ -722,7 +741,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
clampedRect.width = std::min(clampedRect.width, maxTexSize);
clampedRect.height = std::min(clampedRect.height, maxTexSize);
GLuint tex, fbo;
GLuint tex;
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
mGLContext->fGenTextures(1, &tex);
@ -774,6 +793,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
LOCAL_GL_UNSIGNED_BYTE,
buf.get());
}
GLenum error = mGLContext->fGetError();
if (error != LOCAL_GL_NO_ERROR) {
nsAutoCString msg;
@ -801,10 +821,7 @@ CompositorOGL::CreateFBOWithTexture(const IntRect& aRect, bool aCopyFromSource,
LOCAL_GL_CLAMP_TO_EDGE);
mGLContext->fBindTexture(mFBOTextureTarget, 0);
mGLContext->fGenFramebuffers(1, &fbo);
*aFBO = fbo;
*aTexture = tex;
return tex;
}
ShaderConfigOGL
@ -855,6 +872,9 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect,
source->GetFormat() == gfx::SurfaceFormat::R5G6B5_UINT16);
config = ShaderConfigFromTargetAndFormat(source->GetTextureTarget(),
source->GetFormat());
if (!texturedEffect->mPremultiplied) {
config.SetNoPremultipliedAlpha();
}
break;
}
}
@ -862,6 +882,7 @@ CompositorOGL::GetShaderConfigFor(Effect *aEffect,
config.SetMask2D(aMask == MaskType::Mask2d);
config.SetMask3D(aMask == MaskType::Mask3d);
config.SetDEAA(aDEAAEnabled);
config.SetCompositionOp(aOp);
return config;
}
@ -898,10 +919,15 @@ CompositorOGL::ResetProgram()
mCurrentProgram = nullptr;
}
static bool SetBlendMode(GLContext* aGL, gfx::CompositionOp aBlendMode, bool aIsPremultiplied = true)
{
if (BlendOpIsMixBlendMode(aBlendMode)) {
// Mix-blend modes require an extra step (or more) that cannot be expressed
// in the fixed-function blending capabilities of opengl. We handle them
// separately in shaders, and the shaders assume we will use our default
// blend function for compositing (premultiplied OP_OVER).
return false;
}
if (aBlendMode == gfx::CompositionOp::OP_OVER && aIsPremultiplied) {
return false;
}
@ -981,7 +1007,7 @@ CompositorOGL::DrawQuad(const Rect& aRect,
}
IntPoint offset = mCurrentRenderTarget->GetOrigin();
Rect renderBound = mRenderBound;
Rect renderBound = mRenderBounds;
renderBound.IntersectRect(renderBound, aClipRect);
renderBound.MoveBy(offset);
@ -995,7 +1021,7 @@ CompositorOGL::DrawQuad(const Rect& aRect,
// Inflate a small size to avoid some numerical imprecision issue.
destRect.Inflate(1, 1);
destRect.MoveBy(-offset);
if (!mRenderBound.Intersects(destRect)) {
if (!mRenderBounds.Intersects(destRect)) {
return;
}
@ -1066,7 +1092,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
aOpacity = 1.f;
}
GLuint mixBlendBackdrop = 0;
gfx::CompositionOp blendMode = gfx::CompositionOp::OP_OVER;
if (aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE]) {
EffectBlendMode *blendEffect =
static_cast<EffectBlendMode*>(aEffectChain.mSecondaryEffects[EffectTypes::BLEND_MODE].get());
@ -1094,6 +1122,20 @@ CompositorOGL::DrawQuad(const Rect& aRect,
program->SetColorMatrix(effectColorMatrix->mColorMatrix);
}
if (BlendOpIsMixBlendMode(blendMode)) {
gfx::IntRect rect = ComputeBackdropCopyRect(aRect, aClipRect, aTransform);
mixBlendBackdrop = CreateTexture(rect, true, mCurrentRenderTarget->GetFBO());
// Create a transform from adjusted clip space to render target space,
// translate it for the backdrop rect, then transform it into the backdrop's
// uv-space.
gfx::Matrix4x4 transform;
transform.PostScale(mRenderBounds.width, mRenderBounds.height, 1.0);
transform.PostTranslate(-rect.x, -rect.y, 0.0);
transform.PostScale(1 / float(rect.width), 1 / float(rect.height), 1.0);
program->SetBackdropTransform(transform);
}
program->SetRenderOffset(offset.x, offset.y);
LayerScope::SetRenderOffset(offset.x, offset.y);
@ -1184,6 +1226,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE0, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE1);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
@ -1221,6 +1266,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
}
BindAndDrawQuadWithTextureRect(program, aRect, texturedEffect->mTextureCoords, source);
}
@ -1249,6 +1297,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE3, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE4);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
BindAndDrawQuadWithTextureRect(program,
aRect,
@ -1283,6 +1334,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE2, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE3);
}
didSetBlendMode = SetBlendMode(gl(), blendMode);
BindAndDrawQuadWithTextureRect(program,
aRect,
@ -1309,6 +1363,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
if (maskType != MaskType::MaskNone) {
BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
}
if (mixBlendBackdrop) {
BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
}
if (config.mFeatures & ENABLE_TEXTURE_RECT) {
// 2DRect case, get the multiplier right for a sampler2DRect
@ -1392,6 +1449,9 @@ CompositorOGL::DrawQuad(const Rect& aRect,
gl()->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
}
if (mixBlendBackdrop) {
gl()->fDeleteTextures(1, &mixBlendBackdrop);
}
// in case rendering has used some other GL context
MakeCurrent();
@ -1451,22 +1511,12 @@ CompositorOGL::EndFrame()
mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
// Unbind all textures
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE1);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE2);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
for (GLuint i = 0; i <= 4; i++) {
mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
if (!mGLContext->IsGLES()) {
mGLContext->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0);
}
}
}

Просмотреть файл

@ -34,6 +34,10 @@
#include "nscore.h" // for NS_IMETHOD
#include "gfxVR.h"
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
#include "nsTHashtable.h" // for nsTHashtable
#endif
class nsIWidget;
namespace mozilla {
@ -48,6 +52,11 @@ class TextureSource;
struct Effect;
struct EffectChain;
class GLBlitTextureImageHelper;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
class ImageHostOverlay;
#endif
/**
* Interface for pools of temporary gl textures for the compositor.
* The textures are fully owned by the pool, so the latter is responsible
@ -207,6 +216,11 @@ public:
mFBOTextureTarget == LOCAL_GL_TEXTURE_2D,
SupportsPartialTextureUpdate());
result.mSupportedBlendModes += gfx::CompositionOp::OP_SOURCE;
for (uint8_t op = 0; op < uint8_t(gfx::CompositionOp::OP_COUNT); op++) {
if (BlendOpIsMixBlendMode(gfx::CompositionOp(op))) {
result.mSupportedBlendModes += gfx::CompositionOp(op);
}
}
return result;
}
@ -268,6 +282,29 @@ public:
virtual nsIWidget* GetWidget() const override { return mWidget; }
virtual bool HasImageHostOverlays() override
{
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
return mImageHostOverlays.Count() > 0;
#else
return false;
#endif
}
virtual void AddImageHostOverlay(ImageHostOverlay* aOverlay) override
{
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
mImageHostOverlays.PutEntry(aOverlay);
#endif
}
virtual void RemoveImageHostOverlay(ImageHostOverlay* aOverlay) override
{
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
mImageHostOverlays.RemoveEntry(aOverlay);
#endif
}
GLContext* gl() const { return mGLContext; }
/**
* Clear the program state. This must be called
@ -390,6 +427,7 @@ private:
void CreateFBOWithTexture(const gfx::IntRect& aRect, bool aCopyFromSource,
GLuint aSourceFrameBuffer,
GLuint *aFBO, GLuint *aTexture);
GLuint CreateTexture(const gfx::IntRect& aRect, bool aCopyFromSource, GLuint aSourceFrameBuffer);
void BindAndDrawQuads(ShaderProgramOGL *aProg,
int aQuads,
@ -413,6 +451,12 @@ private:
void ActivateProgram(ShaderProgramOGL *aProg);
void CleanupResources();
/**
* Bind the texture behind the current render target as the backdrop for a
* mix-blend shader.
*/
void BindBackdrop(ShaderProgramOGL* aProgram, GLuint aBackdrop, GLenum aTexUnit);
/**
* Copies the content of our backbuffer to the set transaction target.
* Does not restore the target FBO, so only call from EndFrame.
@ -444,9 +488,12 @@ private:
ShaderProgramOGL *mCurrentProgram;
gfx::Rect mRenderBound;
CompositorOGLVRObjects mVR;
#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
nsTHashtable<nsPtrHashKey<ImageHostOverlay> > mImageHostOverlays;
#endif
};
} // namespace layers

Просмотреть файл

@ -30,6 +30,7 @@ AddUniforms(ProgramProfileOGL& aProfile)
"uLayerTransform",
"uLayerTransformInverse",
"uMaskTransform",
"uBackdropTransform",
"uLayerRects",
"uMatrixProj",
"uTextureTransform",
@ -43,6 +44,7 @@ AddUniforms(ProgramProfileOGL& aProfile)
"uBlackTexture",
"uWhiteTexture",
"uMaskTexture",
"uBackdropTexture",
"uRenderColor",
"uTexCoordMultiplier",
"uCbCrTexCoordMultiplier",
@ -148,9 +150,9 @@ ShaderConfigOGL::SetMask3D(bool aEnabled)
}
void
ShaderConfigOGL::SetPremultiply(bool aEnabled)
ShaderConfigOGL::SetNoPremultipliedAlpha()
{
SetFeature(ENABLE_PREMULTIPLY, aEnabled);
SetFeature(ENABLE_NO_PREMUL_ALPHA, true);
}
void
@ -159,6 +161,12 @@ ShaderConfigOGL::SetDEAA(bool aEnabled)
SetFeature(ENABLE_DEAA, aEnabled);
}
void
ShaderConfigOGL::SetCompositionOp(CompositionOp aOp)
{
mCompositionOp = aOp;
}
/* static */ ProgramProfileOGL
ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
{
@ -167,6 +175,8 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
AddUniforms(result);
CompositionOp blendOp = aConfig.mCompositionOp;
vs << "#ifdef GL_ES" << endl;
vs << "#define EDGE_PRECISION mediump" << endl;
vs << "#else" << endl;
@ -190,6 +200,11 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
vs << "varying vec2 vTexCoord;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
vs << "uniform mat4 uBackdropTransform;" << endl;
vs << "varying vec2 vBackdropCoord;" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_2D ||
aConfig.mFeatures & ENABLE_MASK_3D) {
vs << "uniform mat4 uMaskTransform;" << endl;
@ -273,6 +288,14 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
}
vs << " finalPosition.xy -= uRenderTargetOffset * finalPosition.w;" << endl;
vs << " finalPosition = uMatrixProj * finalPosition;" << endl;
if (BlendOpIsMixBlendMode(blendOp)) {
// Translate from clip space (-1, 1) to (0..1), apply the backdrop
// transform, then invert the y-axis.
vs << " vBackdropCoord.x = (finalPosition.x + 1.0) / 2.0;" << endl;
vs << " vBackdropCoord.y = 1.0 - (finalPosition.y + 1.0) / 2.0;" << endl;
vs << " vBackdropCoord = (uBackdropTransform * vec4(vBackdropCoord.xy, 0.0, 1.0)).xy;" << endl;
vs << " vBackdropCoord.y = 1.0 - vBackdropCoord.y;" << endl;
}
vs << " gl_Position = finalPosition;" << endl;
vs << "}" << endl;
@ -309,6 +332,9 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform COLOR_PRECISION float uLayerOpacity;" << endl;
}
}
if (BlendOpIsMixBlendMode(blendOp)) {
fs << "varying vec2 vBackdropCoord;" << endl;
}
const char *sampler2D = "sampler2D";
const char *texture2D = "texture2D";
@ -342,6 +368,13 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform " << sampler2D << " uTexture;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
// Component alpha should be flattened away inside blend containers.
MOZ_ASSERT(!(aConfig.mFeatures & ENABLE_TEXTURE_COMPONENT_ALPHA));
fs << "uniform sampler2D uBackdropTexture;" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_2D ||
aConfig.mFeatures & ENABLE_MASK_3D) {
fs << "varying vec3 vMaskCoord;" << endl;
@ -352,6 +385,10 @@ ProgramProfileOGL::GetProfileFor(ShaderConfigOGL aConfig)
fs << "uniform EDGE_PRECISION vec3 uSSEdges[4];" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
BuildMixBlender(aConfig, fs);
}
if (!(aConfig.mFeatures & ENABLE_RENDER_COLOR)) {
fs << "vec4 sample(vec2 coord) {" << endl;
fs << " vec4 color;" << endl;
@ -459,9 +496,6 @@ For [0,1] instead of [0,255], and to 5 places:
if (aConfig.mFeatures & ENABLE_OPACITY) {
fs << " color *= uLayerOpacity;" << endl;
}
if (aConfig.mFeatures & ENABLE_PREMULTIPLY) {
fs << " color.rgb *= color.a;" << endl;
}
}
if (aConfig.mFeatures & ENABLE_DEAA) {
// Calculate the sub-pixel coverage of the pixel and modulate its opacity
@ -473,6 +507,10 @@ For [0,1] instead of [0,255], and to 5 places:
fs << " deaaCoverage *= clamp(dot(uSSEdges[3], ssPos), 0.0, 1.0);" << endl;
fs << " color *= deaaCoverage;" << endl;
}
if (BlendOpIsMixBlendMode(blendOp)) {
fs << " vec4 backdrop = texture2D(uBackdropTexture, vBackdropCoord);" << endl;
fs << " color = mixAndBlend(backdrop, color);" << endl;
}
if (aConfig.mFeatures & ENABLE_MASK_3D) {
fs << " vec2 maskCoords = vMaskCoord.xy / vMaskCoord.z;" << endl;
fs << " COLOR_PRECISION float mask = texture2D(uMaskTexture, maskCoords).r;" << endl;
@ -507,10 +545,226 @@ For [0,1] instead of [0,255], and to 5 places:
aConfig.mFeatures & ENABLE_MASK_3D) {
result.mTextureCount = 1;
}
if (BlendOpIsMixBlendMode(blendOp)) {
result.mTextureCount += 1;
}
return result;
}
void
ProgramProfileOGL::BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs)
{
// From the "Compositing and Blending Level 1" spec.
// Generate helper functions first.
switch (aConfig.mCompositionOp) {
case gfx::CompositionOp::OP_OVERLAY:
case gfx::CompositionOp::OP_HARD_LIGHT:
// Note: we substitute (2*src-1) into the screen formula below.
fs << "float hardlight(float dest, float src) {" << endl;
fs << " if (src <= 0.5) {" << endl;
fs << " return dest * (2.0 * src);" << endl;
fs << " } else {" << endl;
fs << " return 2.0*dest + 2.0*src - 1.0 - 2.0*dest*src;" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_COLOR_DODGE:
fs << "float dodge(float dest, float src) {" << endl;
fs << " if (dest == 0.0) {" << endl;
fs << " return 0.0;" << endl;
fs << " } else if (src == 1.0) {" << endl;
fs << " return 1.0;" << endl;
fs << " } else {" << endl;
fs << " return min(1.0, dest / (1.0 - src));" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_COLOR_BURN:
fs << "float burn(float dest, float src) {" << endl;
fs << " if (dest == 1.0) {" << endl;
fs << " return 1.0;" << endl;
fs << " } else if (src == 0.0) {" << endl;
fs << " return 0.0;" << endl;
fs << " } else {" << endl;
fs << " return 1.0 - min(1.0, (1.0 - dest) / src);" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_SOFT_LIGHT:
fs << "float darken(float dest) {" << endl;
fs << " if (dest <= 0.25) {" << endl;
fs << " return ((16.0 * dest - 12.0) * dest + 4.0) * dest;" << endl;
fs << " } else {" << endl;
fs << " return sqrt(dest);" << endl;
fs << " }" << endl;
fs << "}" << endl;
fs << "float softlight(float dest, float src) {" << endl;
fs << " if (src <= 0.5) {" << endl;
fs << " return dest - (1.0 - 2.0 * src) * dest * (1.0 - dest);" << endl;
fs << " } else {" << endl;
fs << " return dest + (2.0 * src - 1.0) * (darken(dest) - dest);" << endl;
fs << " }" << endl;
fs << "}" << endl;
break;
case gfx::CompositionOp::OP_HUE:
case gfx::CompositionOp::OP_SATURATION:
case gfx::CompositionOp::OP_COLOR:
case gfx::CompositionOp::OP_LUMINOSITY:
fs << "float Lum(vec3 c) {" << endl;
fs << " return dot(vec3(0.3, 0.59, 0.11), c);" << endl;
fs << "}" << endl;
fs << "vec3 ClipColor(vec3 c) {" << endl;
fs << " float L = Lum(c);" << endl;
fs << " float n = min(min(c.r, c.g), c.b);" << endl;
fs << " float x = max(max(c.r, c.g), c.b);" << endl;
fs << " if (n < 0.0) {" << endl;
fs << " c = L + (((c - L) * L) / (L - n));" << endl;
fs << " }" << endl;
fs << " if (x > 1.0) {" << endl;
fs << " c = L + (((c - L) * (1.0 - L)) / (x - L));" << endl;
fs << " }" << endl;
fs << " return c;" << endl;
fs << "}" << endl;
fs << "vec3 SetLum(vec3 c, float L) {" << endl;
fs << " float d = L - Lum(c);" << endl;
fs << " return ClipColor(vec3(" << endl;
fs << " c.r + d," << endl;
fs << " c.g + d," << endl;
fs << " c.b + d));" << endl;
fs << "}" << endl;
fs << "float Sat(vec3 c) {" << endl;
fs << " return max(max(c.r, c.g), c.b) - min(min(c.r, c.g), c.b);" << endl;
fs << "}" << endl;
// To use this helper, re-arrange rgb such that r=min, g=mid, and b=max.
fs << "vec3 SetSatInner(vec3 c, float s) {" << endl;
fs << " if (c.b > c.r) {" << endl;
fs << " c.g = (((c.g - c.r) * s) / (c.b - c.r));" << endl;
fs << " c.b = s;" << endl;
fs << " } else {" << endl;
fs << " c.gb = vec2(0.0, 0.0);" << endl;
fs << " }" << endl;
fs << " return vec3(0.0, c.gb);" << endl;
fs << "}" << endl;
fs << "vec3 SetSat(vec3 c, float s) {" << endl;
fs << " if (c.r <= c.g) {" << endl;
fs << " if (c.g <= c.b) {" << endl;
fs << " c.rgb = SetSatInner(c.rgb, s);" << endl;
fs << " } else if (c.r <= c.b) {" << endl;
fs << " c.rbg = SetSatInner(c.rbg, s);" << endl;
fs << " } else {" << endl;
fs << " c.brg = SetSatInner(c.brg, s);" << endl;
fs << " }" << endl;
fs << " } else if (c.r <= c.b) {" << endl;
fs << " c.grb = SetSatInner(c.grb, s);" << endl;
fs << " } else if (c.g <= c.b) {" << endl;
fs << " c.gbr = SetSatInner(c.gbr, s);" << endl;
fs << " } else {" << endl;
fs << " c.bgr = SetSatInner(c.bgr, s);" << endl;
fs << " }" << endl;
fs << " return c;" << endl;
fs << "}" << endl;
break;
default:
break;
}
// Generate the main blending helper.
fs << "vec3 blend(vec3 dest, vec3 src) {" << endl;
switch (aConfig.mCompositionOp) {
case gfx::CompositionOp::OP_MULTIPLY:
fs << " return dest * src;" << endl;
break;
case gfx::CompositionOp::OP_SCREEN:
fs << " return dest + src - (dest * src);" << endl;
break;
case gfx::CompositionOp::OP_OVERLAY:
fs << " return vec3(" << endl;
fs << " hardlight(src.r, dest.r)," << endl;
fs << " hardlight(src.g, dest.g)," << endl;
fs << " hardlight(src.b, dest.b));" << endl;
break;
case gfx::CompositionOp::OP_DARKEN:
fs << " return min(dest, src);" << endl;
break;
case gfx::CompositionOp::OP_LIGHTEN:
fs << " return max(dest, src);" << endl;
break;
case gfx::CompositionOp::OP_COLOR_DODGE:
fs << " return vec3(" << endl;
fs << " dodge(dest.r, src.r)," << endl;
fs << " dodge(dest.g, src.g)," << endl;
fs << " dodge(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_COLOR_BURN:
fs << " return vec3(" << endl;
fs << " burn(dest.r, src.r)," << endl;
fs << " burn(dest.g, src.g)," << endl;
fs << " burn(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_HARD_LIGHT:
fs << " return vec3(" << endl;
fs << " hardlight(dest.r, src.r)," << endl;
fs << " hardlight(dest.g, src.g)," << endl;
fs << " hardlight(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_SOFT_LIGHT:
fs << " return vec3(" << endl;
fs << " softlight(dest.r, src.r)," << endl;
fs << " softlight(dest.g, src.g)," << endl;
fs << " softlight(dest.b, src.b));" << endl;
break;
case gfx::CompositionOp::OP_DIFFERENCE:
fs << " return abs(dest - src);" << endl;
break;
case gfx::CompositionOp::OP_EXCLUSION:
fs << " return dest + src - 2.0*dest*src;" << endl;
break;
case gfx::CompositionOp::OP_HUE:
fs << " return SetLum(SetSat(src, Sat(dest)), Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_SATURATION:
fs << " return SetLum(SetSat(dest, Sat(src)), Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_COLOR:
fs << " return SetLum(src, Lum(dest));" << endl;
break;
case gfx::CompositionOp::OP_LUMINOSITY:
fs << " return SetLum(dest, Lum(src));" << endl;
break;
default:
MOZ_ASSERT_UNREACHABLE("unknown blend mode");
}
fs << "}" << endl;
// Generate the mix-blend function the fragment shader will call.
fs << "vec4 mixAndBlend(vec4 backdrop, vec4 color) {" << endl;
// Shortcut when the backdrop or source alpha is 0, otherwise we may leak
// Infinity into the blend function and return incorrect results.
fs << " if (backdrop.a == 0.0) {" << endl;
fs << " return color;" << endl;
fs << " }" << endl;
fs << " if (color.a == 0.0) {" << endl;
fs << " return backdrop;" << endl;
fs << " }" << endl;
// The spec assumes there is no premultiplied alpha. The backdrop is always
// premultiplied, so undo the premultiply. If the source is premultiplied we
// must fix that as well.
fs << " backdrop.rgb /= backdrop.a;" << endl;
if (!(aConfig.mFeatures & ENABLE_NO_PREMUL_ALPHA)) {
fs << " color.rgb /= color.a;" << endl;
}
fs << " vec3 blended = blend(backdrop.rgb, color.rgb);" << endl;
fs << " color.rgb = (1.0 - backdrop.a) * color.rgb + backdrop.a * blended.rgb;" << endl;
fs << " color.rgb *= color.a;" << endl;
fs << " return color;" << endl;
fs << "}" << endl;
}
ShaderProgramOGL::ShaderProgramOGL(GLContext* aGL, const ProgramProfileOGL& aProfile)
: mGL(aGL)
, mProgram(0)

Просмотреть файл

@ -39,7 +39,7 @@ enum ShaderFeatures {
ENABLE_COLOR_MATRIX=0x400,
ENABLE_MASK_2D=0x800,
ENABLE_MASK_3D=0x1000,
ENABLE_PREMULTIPLY=0x2000,
ENABLE_NO_PREMUL_ALPHA=0x2000,
ENABLE_DEAA=0x4000
};
@ -52,6 +52,7 @@ public:
LayerTransform = 0,
LayerTransformInverse,
MaskTransform,
BackdropTransform,
LayerRects,
MatrixProj,
TextureTransform,
@ -65,6 +66,7 @@ public:
BlackTexture,
WhiteTexture,
MaskTexture,
BackdropTexture,
RenderColor,
TexCoordMultiplier,
CbCrTexCoordMultiplier,
@ -207,7 +209,9 @@ class ShaderConfigOGL
{
public:
ShaderConfigOGL() :
mFeatures(0) {}
mFeatures(0),
mCompositionOp(gfx::CompositionOp::OP_OVER)
{}
void SetRenderColor(bool aEnabled);
void SetTextureTarget(GLenum aTarget);
@ -221,11 +225,14 @@ public:
void SetBlur(bool aEnabled);
void SetMask2D(bool aEnabled);
void SetMask3D(bool aEnabled);
void SetPremultiply(bool aEnabled);
void SetDEAA(bool aEnabled);
void SetCompositionOp(gfx::CompositionOp aOp);
void SetNoPremultipliedAlpha();
bool operator< (const ShaderConfigOGL& other) const {
return mFeatures < other.mFeatures;
return mFeatures < other.mFeatures ||
(mFeatures == other.mFeatures &&
(int)mCompositionOp < (int)other.mCompositionOp);
}
public:
@ -237,6 +244,7 @@ public:
}
int mFeatures;
gfx::CompositionOp mCompositionOp;
};
static inline ShaderConfigOGL
@ -278,6 +286,9 @@ struct ProgramProfileOGL
ProgramProfileOGL() :
mTextureCount(0)
{}
private:
static void BuildMixBlender(const ShaderConfigOGL& aConfig, std::ostringstream& fs);
};
@ -342,6 +353,10 @@ public:
SetMatrixUniform(KnownUniform::MaskTransform, aMatrix);
}
void SetBackdropTransform(const gfx::Matrix4x4& aMatrix) {
SetMatrixUniform(KnownUniform::BackdropTransform, aMatrix);
}
void SetDEAAEdges(const gfx::Point3D* aEdges) {
SetArrayUniform(KnownUniform::SSEdges, 4, aEdges);
}
@ -433,6 +448,10 @@ public:
SetUniform(KnownUniform::MaskTexture, aUnit);
}
void SetBackdropTextureUnit(GLint aUnit) {
SetUniform(KnownUniform::BackdropTexture, aUnit);
}
void SetRenderColor(const gfx::Color& aColor) {
SetUniform(KnownUniform::RenderColor, aColor);
}

Просмотреть файл

@ -233,14 +233,16 @@ nsBMPDecoder::FinishInternal()
if (!IsMetadataDecode() && HasSize()) {
// If it was truncated, fill in the missing pixels as black.
while (mCurrentRow > 0) {
uint32_t* dst = RowBuffer();
while (mCurrentPos < mH.mWidth) {
SetPixel(dst, 0, 0, 0);
mCurrentPos++;
if (mImageData) {
while (mCurrentRow > 0) {
uint32_t* dst = RowBuffer();
while (mCurrentPos < mH.mWidth) {
SetPixel(dst, 0, 0, 0);
mCurrentPos++;
}
mCurrentPos = 0;
FinishRow();
}
mCurrentPos = 0;
FinishRow();
}
// Invalidate.
@ -554,6 +556,11 @@ nsBMPDecoder::ReadInfoHeaderRest(const char* aData, size_t aLength)
(mH.mCompression == Compression::RLE8 && mH.mBpp == 8) ||
(mH.mCompression == Compression::RLE4 && mH.mBpp == 4) ||
(mH.mCompression == Compression::BITFIELDS &&
// For BITFIELDS compression we require an exact match for one of the
// WinBMP BIH sizes; this clearly isn't an OS2 BMP.
(mH.mBIHSize == InfoHeaderLength::WIN_V3 ||
mH.mBIHSize == InfoHeaderLength::WIN_V4 ||
mH.mBIHSize == InfoHeaderLength::WIN_V5) &&
(mH.mBpp == 16 || mH.mBpp == 32));
if (!bppCompressionOk) {
PostDataError();

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 78 B

Просмотреть файл

@ -10,3 +10,8 @@
# Tests for RLE8 with an invalid BPP
== wrapper.html?invalid-compression-RLE8.bmp about:blank
# Test for BITFIELDS with an invalid BIH size. (This is the obscure
# BITMAPV3INFOHEADER variant mentioned in
# https://en.wikipedia.org/wiki/BMP_file_format which we don't accept.)
== wrapper.html?invalid-compression-BITFIELDS.bmp about:blank

Просмотреть файл

@ -27,6 +27,8 @@ namespace base {
typedef ::Lock Lock;
typedef ::AutoLock AutoLock;
using mozilla::OffTheBooksMutexAutoLock;
// Static table of checksums for all possible 8 bit bytes.
const uint32_t Histogram::kCrcTable[256] = {0x0, 0x77073096L, 0xee0e612cL,
0x990951baL, 0x76dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0xedb8832L,
@ -172,20 +174,25 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
// are consistent across our output activities.
SampleSet snapshot;
SnapshotSample(&snapshot);
Count sample_count = snapshot.TotalCount();
WriteAsciiHeader(snapshot, sample_count, output);
// For the rest of the routine, we hold |snapshot|'s lock so as to
// be able to examine it atomically.
OffTheBooksMutexAutoLock locker(snapshot.mutex());
Count sample_count = snapshot.TotalCount(locker);
WriteAsciiHeader(snapshot, locker, sample_count, output);
output->append(newline);
// Prepare to normalize graphical rendering of bucket contents.
double max_size = 0;
if (graph_it)
max_size = GetPeakBucketSize(snapshot);
max_size = GetPeakBucketSize(snapshot, locker);
// Calculate space needed to print bucket range numbers. Leave room to print
// nearly the largest bucket range without sliding over the histogram.
size_t largest_non_empty_bucket = bucket_count() - 1;
while (0 == snapshot.counts(largest_non_empty_bucket)) {
while (0 == snapshot.counts(locker, largest_non_empty_bucket)) {
if (0 == largest_non_empty_bucket)
break; // All buckets are empty.
--largest_non_empty_bucket;
@ -194,7 +201,7 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
// Calculate largest print width needed for any of our bucket range displays.
size_t print_width = 1;
for (size_t i = 0; i < bucket_count(); ++i) {
if (snapshot.counts(i)) {
if (snapshot.counts(locker, i)) {
size_t width = GetAsciiBucketRange(i).size() + 1;
if (width > print_width)
print_width = width;
@ -205,7 +212,7 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
int64_t past = 0;
// Output the actual histogram graph.
for (size_t i = 0; i < bucket_count(); ++i) {
Count current = snapshot.counts(i);
Count current = snapshot.counts(locker, i);
if (!current && !PrintEmptyBucket(i))
continue;
remaining -= current;
@ -213,8 +220,9 @@ void Histogram::WriteAscii(bool graph_it, const std::string& newline,
output->append(range);
for (size_t j = 0; range.size() + j < print_width + 1; ++j)
output->push_back(' ');
if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) {
while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1))
if (0 == current &&
i < bucket_count() - 1 && 0 == snapshot.counts(locker, i + 1)) {
while (i < bucket_count() - 1 && 0 == snapshot.counts(locker, i + 1))
++i;
output->append("... ");
output->append(newline);
@ -328,12 +336,13 @@ bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) {
//------------------------------------------------------------------------------
Histogram::Inconsistencies Histogram::FindCorruption(
const SampleSet& snapshot) const {
const SampleSet& snapshot,
const OffTheBooksMutexAutoLock& snapshotLockEvidence) const {
int inconsistencies = NO_INCONSISTENCIES;
Sample previous_range = -1; // Bottom range is always 0.
int64_t count = 0;
for (size_t index = 0; index < bucket_count(); ++index) {
count += snapshot.counts(index);
count += snapshot.counts(snapshotLockEvidence, index);
int new_range = ranges(index);
if (previous_range >= new_range)
inconsistencies |= BUCKET_ORDER_ERROR;
@ -343,7 +352,7 @@ Histogram::Inconsistencies Histogram::FindCorruption(
if (!HasValidRangeChecksum())
inconsistencies |= RANGE_CHECKSUM_ERROR;
int64_t delta64 = snapshot.redundant_count() - count;
int64_t delta64 = snapshot.redundant_count(snapshotLockEvidence) - count;
if (delta64 != 0) {
int delta = static_cast<int>(delta64);
if (delta != delta64)
@ -383,10 +392,8 @@ size_t Histogram::bucket_count() const {
return bucket_count_;
}
// Do a safe atomic snapshot of sample data.
// This implementation assumes we are on a safe single thread.
void Histogram::SnapshotSample(SampleSet* sample) const {
// Note locking not done in this version!!!
OffTheBooksMutexAutoLock locker(sample_.mutex());
*sample = sample_;
}
@ -422,6 +429,7 @@ size_t Histogram::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
size_t Histogram::SampleSet::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
{
OffTheBooksMutexAutoLock locker(mutex_);
// We're not allowed to do deep dives into STL data structures. This
// is as close as we can get to measuring this array.
return aMallocSizeOf(&counts_[0]);
@ -559,7 +567,6 @@ const std::string Histogram::GetAsciiBucketRange(size_t i) const {
// Update histogram data with new sample.
void Histogram::Accumulate(Sample value, Count count, size_t index) {
// Note locking not done in this version!!!
sample_.AccumulateWithLinearStats(value, count, index);
}
@ -640,10 +647,13 @@ uint32_t Histogram::Crc32(uint32_t sum, Histogram::Sample range) {
//------------------------------------------------------------------------------
// Private methods
double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
double Histogram::GetPeakBucketSize(const SampleSet& snapshot,
const OffTheBooksMutexAutoLock&
snapshotLockEvidence) const {
double max = 0;
for (size_t i = 0; i < bucket_count() ; ++i) {
double current_size = GetBucketSize(snapshot.counts(i), i);
double current_size
= GetBucketSize(snapshot.counts(snapshotLockEvidence, i), i);
if (current_size > max)
max = current_size;
}
@ -651,16 +661,19 @@ double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
}
void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
const OffTheBooksMutexAutoLock&
snapshotLockEvidence,
Count sample_count,
std::string* output) const {
StringAppendF(output,
"Histogram: %s recorded %d samples",
histogram_name().c_str(),
sample_count);
int64_t snapshot_sum = snapshot.sum(snapshotLockEvidence);
if (0 == sample_count) {
DCHECK_EQ(snapshot.sum(), 0);
DCHECK_EQ(snapshot_sum, 0);
} else {
double average = static_cast<float>(snapshot.sum()) / sample_count;
double average = static_cast<float>(snapshot_sum) / sample_count;
StringAppendF(output, ", average = %.1f", average);
}
@ -710,22 +723,21 @@ Histogram::SampleSet::SampleSet()
sum_squares_(0),
log_sum_(0),
log_sum_squares_(0),
redundant_count_(0) {
redundant_count_(0),
mutex_("Histogram::SampleSet::SampleSet") {
}
Histogram::SampleSet::~SampleSet() {
}
void Histogram::SampleSet::Resize(const Histogram& histogram) {
OffTheBooksMutexAutoLock locker(mutex_);
counts_.resize(histogram.bucket_count(), 0);
}
void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
DCHECK_EQ(histogram.bucket_count(), counts_.size());
}
void Histogram::SampleSet::Accumulate(Sample value, Count count,
size_t index) {
void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
Sample value, Count count,
size_t index) {
DCHECK(count == 1 || count == -1);
counts_[index] += count;
redundant_count_ += count;
@ -738,11 +750,13 @@ void Histogram::SampleSet::Accumulate(Sample value, Count count,
void Histogram::SampleSet::AccumulateWithLinearStats(Sample value,
Count count,
size_t index) {
Accumulate(value, count, index);
OffTheBooksMutexAutoLock locker(mutex_);
Accumulate(locker, value, count, index);
sum_squares_ += static_cast<int64_t>(count) * value * value;
}
Count Histogram::SampleSet::TotalCount() const {
Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
const {
Count total = 0;
for (Counts::const_iterator it = counts_.begin();
it != counts_.end();
@ -753,6 +767,7 @@ Count Histogram::SampleSet::TotalCount() const {
}
void Histogram::SampleSet::Add(const SampleSet& other) {
OffTheBooksMutexAutoLock locker(mutex_);
DCHECK_EQ(counts_.size(), other.counts_.size());
sum_ += other.sum_;
sum_squares_ += other.sum_squares_;
@ -763,23 +778,8 @@ void Histogram::SampleSet::Add(const SampleSet& other) {
counts_[index] += other.counts_[index];
}
void Histogram::SampleSet::Subtract(const SampleSet& other) {
DCHECK_EQ(counts_.size(), other.counts_.size());
// Note: Race conditions in snapshotting a sum may lead to (temporary)
// negative values when snapshots are later combined (and deltas calculated).
// As a result, we don't currently CHCEK() for positive values.
sum_ -= other.sum_;
sum_squares_ -= other.sum_squares_;
log_sum_ -= other.log_sum_;
log_sum_squares_ -= other.log_sum_squares_;
redundant_count_ -= other.redundant_count_;
for (size_t index = 0; index < counts_.size(); ++index) {
counts_[index] -= other.counts_[index];
DCHECK_GE(counts_[index], 0);
}
}
bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
OffTheBooksMutexAutoLock locker(mutex_);
pickle->WriteInt64(sum_);
pickle->WriteInt64(redundant_count_);
pickle->WriteSize(counts_.size());
@ -792,6 +792,7 @@ bool Histogram::SampleSet::Serialize(Pickle* pickle) const {
}
bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) {
OffTheBooksMutexAutoLock locker(mutex_);
DCHECK_EQ(counts_.size(), 0u);
DCHECK_EQ(sum_, 0);
DCHECK_EQ(redundant_count_, 0);
@ -1013,7 +1014,8 @@ FlagHistogram::Accumulate(Sample value, Count count, size_t index)
void
FlagHistogram::AddSampleSet(const SampleSet& sample) {
DCHECK_EQ(bucket_count(), sample.size());
OffTheBooksMutexAutoLock locker(sample.mutex());
DCHECK_EQ(bucket_count(), sample.size(locker));
// We can't be sure the SampleSet provided came from another FlagHistogram,
// so we take the following steps:
// - If our flag has already been set do nothing.
@ -1027,12 +1029,12 @@ FlagHistogram::AddSampleSet(const SampleSet& sample) {
return;
}
if (sample.sum() != 1) {
if (sample.sum(locker) != 1) {
return;
}
size_t one_index = BucketIndex(1);
if (sample.counts(one_index) == 1) {
if (sample.counts(locker, one_index) == 1) {
Accumulate(1, 1, one_index);
}
}
@ -1084,18 +1086,20 @@ CountHistogram::Accumulate(Sample value, Count count, size_t index)
void
CountHistogram::AddSampleSet(const SampleSet& sample) {
DCHECK_EQ(bucket_count(), sample.size());
OffTheBooksMutexAutoLock locker(sample.mutex());
DCHECK_EQ(bucket_count(), sample.size(locker));
// We can't be sure the SampleSet provided came from another CountHistogram,
// so we at least check that the unused buckets are empty.
const size_t indices[] = { BucketIndex(0), BucketIndex(1), BucketIndex(2) };
if (sample.counts(indices[1]) != 0 || sample.counts(indices[2]) != 0) {
if (sample.counts(locker, indices[1]) != 0 ||
sample.counts(locker, indices[2]) != 0) {
return;
}
if (sample.counts(indices[0]) != 0) {
Accumulate(1, sample.counts(indices[0]), indices[0]);
if (sample.counts(locker, indices[0]) != 0) {
Accumulate(1, sample.counts(locker, indices[0]), indices[0]);
}
}

Просмотреть файл

@ -43,6 +43,7 @@
#include "mozilla/Atomics.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Mutex.h"
#include <map>
#include <string>
@ -54,6 +55,10 @@
class Pickle;
namespace base {
using mozilla::OffTheBooksMutex;
using mozilla::OffTheBooksMutexAutoLock;
//------------------------------------------------------------------------------
// Provide easy general purpose histogram in a macro, just like stats counters.
// The first four macros use 50 buckets.
@ -329,32 +334,79 @@ class Histogram {
explicit SampleSet();
~SampleSet();
// This class contains a mozilla::OffTheBooksMutex, |mutex_|.
// Most of the methods are thread-safe: they acquire and release
// the mutex themselves. A few are not thread-safe, and require
// the caller to provide evidence that the object is locked, by
// supplying a const OffTheBooksMutexAutoLock& parameter. The
// parameter is ignored but must be present. |mutex_| must be an
// OffTheBooks variant because some of the containing SampleSet
// objects are leaked until shutdown, so a standard Mutex can't be
// used, since that does leak checking, and causes test failures.
//---------------- THREAD SAFE METHODS ----------------//
//
// The caller must not already hold |this.mutex_|, otherwise we
// will end up deadlocking.
// Adjust size of counts_ for use with given histogram.
void Resize(const Histogram& histogram);
void CheckSize(const Histogram& histogram) const;
// Accessor for histogram to make routine additions.
void AccumulateWithLinearStats(Sample value, Count count, size_t index);
// Accessor methods.
Count counts(size_t i) const { return counts_[i]; }
Count TotalCount() const;
int64_t sum() const { return sum_; }
uint64_t sum_squares() const { return sum_squares_; }
double log_sum() const { return log_sum_; }
double log_sum_squares() const { return log_sum_squares_; }
int64_t redundant_count() const { return redundant_count_; }
size_t size() const { return counts_.size(); }
// Arithmetic manipulation of corresponding elements of the set.
void Add(const SampleSet& other);
void Subtract(const SampleSet& other);
bool Serialize(Pickle* pickle) const;
bool Deserialize(void** iter, const Pickle& pickle);
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
//---------------- THREAD UNSAFE METHODS ----------------//
//
// The caller must hold |this.mutex_|, and must supply evidence by passing
// a const reference to the relevant OffTheBooksMutexAutoLock used.
Count counts(const OffTheBooksMutexAutoLock& ev, size_t i) const {
return counts_[i];
}
Count TotalCount(const OffTheBooksMutexAutoLock& ev) const;
int64_t sum(const OffTheBooksMutexAutoLock& ev) const {
return sum_;
}
uint64_t sum_squares(const OffTheBooksMutexAutoLock& ev) const {
return sum_squares_;
}
double log_sum(const OffTheBooksMutexAutoLock& ev) const {
return log_sum_;
}
double log_sum_squares(const OffTheBooksMutexAutoLock& ev) const {
return log_sum_squares_;
}
int64_t redundant_count(const OffTheBooksMutexAutoLock& ev) const {
return redundant_count_;
}
size_t size(const OffTheBooksMutexAutoLock& ev) const {
return counts_.size();
}
// An assignment operator. The presence of mozilla::OffTheBooksMutex
// in this class causes the default assignment operator to be deleted.
const SampleSet& operator=(const SampleSet& other) {
counts_ = other.counts_;
sum_ = other.sum_;
sum_squares_ = other.sum_squares_;
log_sum_ = other.log_sum_;
log_sum_squares_ = other.log_sum_squares_;
redundant_count_ = other.redundant_count_;
return *this;
}
private:
void Accumulate(const OffTheBooksMutexAutoLock& ev,
Sample value, Count count, size_t index);
protected:
// Actual histogram data is stored in buckets, showing the count of values
// that fit into each bucket.
@ -371,9 +423,6 @@ class Histogram {
double log_sum_; // sum of logs of samples.
double log_sum_squares_; // sum of squares of logs of samples
private:
void Accumulate(Sample value, Count count, size_t index);
// To help identify memory corruption, we reduntantly save the number of
// samples we've accumulated into all of our buckets. We can compare this
// count to the sum of the counts in all buckets, and detect problems. Note
@ -382,6 +431,13 @@ class Histogram {
// and also the snapshotting code may asynchronously get a mismatch (though
// generally either race based mismatch cause is VERY rare).
int64_t redundant_count_;
private:
// Protects all data fields.
mutable OffTheBooksMutex mutex_;
public:
OffTheBooksMutex& mutex() const { return mutex_; }
};
//----------------------------------------------------------------------------
@ -454,7 +510,9 @@ class Histogram {
// produce a false-alarm if a race occurred in the reading of the data during
// a SnapShot process, but should otherwise be false at all times (unless we
// have memory over-writes, or DRAM failures).
virtual Inconsistencies FindCorruption(const SampleSet& snapshot) const;
virtual Inconsistencies FindCorruption(const SampleSet& snapshot,
const OffTheBooksMutexAutoLock&
snapshotLockEvidence) const;
//----------------------------------------------------------------------------
// Accessors for factory constuction, serialization and testing.
@ -466,8 +524,10 @@ class Histogram {
virtual Sample ranges(size_t i) const;
uint32_t range_checksum() const { return range_checksum_; }
virtual size_t bucket_count() const;
// Snapshot the current complete set of sample data.
// Override with atomic/locked snapshot if needed.
// Do a safe atomic snapshot of sample data. The caller is assumed to
// have exclusive access to the destination, |*sample|, and no locking
// of it is done here. This routine does lock the source sample though.
virtual void SnapshotSample(SampleSet* sample) const;
virtual bool HasConstructorArguments(Sample minimum, Sample maximum,
@ -543,10 +603,13 @@ class Histogram {
// Helpers for emitting Ascii graphic. Each method appends data to output.
// Find out how large the (graphically) the largest bucket will appear to be.
double GetPeakBucketSize(const SampleSet& snapshot) const;
double GetPeakBucketSize(const SampleSet& snapshot,
const OffTheBooksMutexAutoLock&
snapshotLockEvidence) const;
// Write a common header message describing this histogram.
void WriteAsciiHeader(const SampleSet& snapshot,
const OffTheBooksMutexAutoLock& snapshotLockEvidence,
Count sample_count, std::string* output) const;
// Write information about previous, current, and next buckets.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -29,15 +29,150 @@ namespace wasm {
enum class Expr : uint8_t
{
Ret,
// Control opcodes
Nop,
Block,
IfThen,
Loop,
If,
IfElse,
Switch,
Select,
Br,
BrIf,
TableSwitch,
Return,
Unreachable,
While,
// Calls
CallInternal,
CallIndirect,
CallImport,
// Constants and calls
I8Const,
I32Const,
I64Const,
F64Const,
F32Const,
GetLocal,
SetLocal,
LoadGlobal,
StoreGlobal,
// I32 opcodes
I32Add,
I32Sub,
I32Mul,
I32DivS,
I32DivU,
I32RemS,
I32RemU,
I32Ior,
I32And,
I32Xor,
I32Shl,
I32ShrU,
I32ShrS,
I32Eq,
I32Ne,
I32LtS,
I32LeS,
I32LtU,
I32LeU,
I32GtS,
I32GeS,
I32GtU,
I32GeU,
I32Clz,
I32Ctz,
I32Popcnt,
// F32 opcodes
F32Add,
F32Sub,
F32Mul,
F32Div,
F32Min,
F32Max,
F32Abs,
F32Neg,
F32CopySign,
F32Ceil,
F32Floor,
F32Trunc,
F32NearestInt,
F32Sqrt,
F32Eq,
F32Ne,
F32Lt,
F32Le,
F32Gt,
F32Ge,
// F64 opcodes
F64Add,
F64Sub,
F64Mul,
F64Div,
F64Min,
F64Max,
F64Abs,
F64Neg,
F64CopySign,
F64Ceil,
F64Floor,
F64Trunc,
F64NearestInt,
F64Sqrt,
F64Eq,
F64Ne,
F64Lt,
F64Le,
F64Gt,
F64Ge,
// Conversions
I32SConvertF32,
I32SConvertF64,
I32UConvertF32,
I32UConvertF64,
I32ConvertI64,
I64SConvertF32,
I64SConvertF64,
I64UConvertF32,
I64UConvertF64,
I64SConvertI32,
I64UConvertI32,
// Load/store operations
I32LoadMem8S,
I32LoadMem8U,
I32LoadMem16S,
I32LoadMem16U,
I32LoadMem,
I64LoadMem8S,
I64LoadMem8U,
I64LoadMem16S,
I64LoadMem16U,
I64LoadMem32S,
I64LoadMem32U,
I64LoadMem,
F32LoadMem,
F64LoadMem,
I32StoreMem8,
I32StoreMem16,
I64StoreMem8,
I64StoreMem16,
I64StoreMem32,
I32StoreMem,
I64StoreMem,
F32StoreMem,
F64StoreMem,
// asm.js specific
Ternary, // to be merged with IfElse
While, // all CFG ops to be deleted in favor of Loop/Br/BrIf
DoWhile,
ForInitInc,
@ -51,201 +186,97 @@ enum class Expr : uint8_t
Break,
BreakLabel,
GetLocal,
SetLocal,
CallInternal,
CallIndirect,
CallImport,
AtomicsFence,
I32Expr,
I32Expr, // to be removed
F32Expr,
F64Expr,
I32X4Expr,
F32X4Expr,
B32X4Expr,
// asm.js specific
Id,
Noop,
LoadGlobal,
StoreGlobal,
InterruptCheckHead,
InterruptCheckLoop,
DebugCheckPoint,
Bad,
// I32 opcodes
I32CallInternal,
I32CallIndirect,
I32CallImport,
I32Conditional,
I32Comma,
I32Literal,
// Binary arith opcodes
I32Add,
I32Sub,
I32Mul,
I32SDiv,
I32SMod,
I32UDiv,
I32UMod,
I32Min,
I32Max,
// Unary arith opcodes
I32Not,
I32Neg,
// Bitwise opcodes
I32BitOr,
I32BitAnd,
I32BitXor,
I32BitNot,
I32Lsh,
I32ArithRsh,
I32LogicRsh,
// Conversion opcodes
I32FromF32,
I32FromF64,
// Math builtin opcodes
I32Clz,
I32Abs,
// Comparison opcodes
// Ordering matters (EmitComparison expects signed opcodes to be placed
// before unsigned opcodes)
I32EqI32,
I32NeI32,
I32SLtI32,
I32SLeI32,
I32SGtI32,
I32SGeI32,
I32ULtI32,
I32ULeI32,
I32UGtI32,
I32UGeI32,
I32EqF32,
I32NeF32,
I32LtF32,
I32LeF32,
I32GtF32,
I32GeF32,
I32EqF64,
I32NeF64,
I32LtF64,
I32LeF64,
I32GtF64,
I32GeF64,
// Heap accesses opcodes
I32SLoad8,
I32SLoad16,
I32SLoad32,
I32ULoad8,
I32ULoad16,
I32ULoad32,
I32Store8,
I32Store16,
I32Store32,
// Atomics opcodes
// Atomics
AtomicsFence,
I32AtomicsCompareExchange,
I32AtomicsExchange,
I32AtomicsLoad,
I32AtomicsStore,
I32AtomicsBinOp,
// SIMD opcodes
// SIMD
I32X4Const,
B32X4Const,
F32X4Const,
I32X4Expr,
F32X4Expr,
B32X4Expr,
I32I32X4ExtractLane,
I32B32X4ExtractLane,
I32B32X4AllTrue,
I32B32X4AnyTrue,
// Specific to AsmJS
I32Id,
F32F32X4ExtractLane,
// F32 opcdoes
// Common opcodes
F32CallInternal,
F32CallIndirect,
F32CallImport,
I32X4Ctor,
I32X4Unary,
I32X4Binary,
I32X4BinaryBitwise,
I32X4BinaryShift,
I32X4ReplaceLane,
I32X4FromF32X4,
I32X4FromF32X4Bits,
I32X4Swizzle,
I32X4Shuffle,
I32X4Select,
I32X4Splat,
I32X4Load,
I32X4Store,
F32Conditional,
F32Comma,
F32X4Ctor,
F32X4Unary,
F32X4Binary,
F32X4ReplaceLane,
F32X4FromI32X4,
F32X4FromI32X4Bits,
F32X4Swizzle,
F32X4Shuffle,
F32X4Select,
F32X4Splat,
F32X4Load,
F32X4Store,
F32Literal,
B32X4Ctor,
B32X4Unary,
B32X4Binary,
B32X4BinaryCompI32X4,
B32X4BinaryCompF32X4,
B32X4BinaryBitwise,
B32X4ReplaceLane,
B32X4Splat,
// Binary arith opcodes
F32Add,
F32Sub,
F32Mul,
F32Div,
F32Min,
F32Max,
F32Neg,
// I32 asm.js opcodes
I32Not,
I32Neg,
I32BitNot,
I32Abs,
// Math builtin opcodes
F32Abs,
F32Sqrt,
F32Ceil,
F32Floor,
// Conversion opcodes
// F32 asm.js opcodes
F32FromF64,
F32FromS32,
F32FromU32,
// Heap accesses opcodes
F32Load,
F32StoreF32,
F32StoreF64,
F32StoreMemF64,
// SIMD opcodes
F32F32X4ExtractLane,
// asm.js specific
F32Id,
// F64 opcodes
// Common opcodes
F64CallInternal,
F64CallIndirect,
F64CallImport,
F64Conditional,
F64Comma,
F64Literal,
// Binary arith opcodes
F64Add,
F64Sub,
F64Mul,
F64Div,
F64Min,
F64Max,
// F64 asm.js opcodes
F64Mod,
F64Neg,
// Math builtin opcodes
F64Abs,
F64Sqrt,
F64Ceil,
F64Floor,
F64Sin,
F64Cos,
F64Tan,
@ -257,115 +288,11 @@ enum class Expr : uint8_t
F64Pow,
F64Atan2,
// Conversions opcodes
F64FromF32,
F64FromS32,
F64FromU32,
// Heap accesses opcodes
F64Load,
F64StoreF32,
F64StoreF64,
// asm.js specific
F64Id,
// I32X4 opcodes
// Common opcodes
I32X4CallInternal,
I32X4CallIndirect,
I32X4CallImport,
I32X4Conditional,
I32X4Comma,
I32X4Literal,
// Specific opcodes
I32X4Ctor,
I32X4Unary,
I32X4Binary,
I32X4BinaryBitwise,
I32X4BinaryShift,
I32X4ReplaceLane,
I32X4FromF32X4,
I32X4FromF32X4Bits,
I32X4Swizzle,
I32X4Shuffle,
I32X4Select,
I32X4Splat,
I32X4Load,
I32X4Store,
// asm.js specific
I32X4Id,
// F32X4 opcodes
// Common opcodes
F32X4CallInternal,
F32X4CallIndirect,
F32X4CallImport,
F32X4Conditional,
F32X4Comma,
F32X4Literal,
// Specific opcodes
F32X4Ctor,
F32X4Unary,
F32X4Binary,
F32X4ReplaceLane,
F32X4FromI32X4,
F32X4FromI32X4Bits,
F32X4Swizzle,
F32X4Shuffle,
F32X4Select,
F32X4Splat,
F32X4Load,
F32X4Store,
// asm.js specific
F32X4Id,
// B32X4 opcodes
// Common opcodes
B32X4CallInternal,
B32X4CallIndirect,
B32X4CallImport,
B32X4Conditional,
B32X4Comma,
B32X4Literal,
// Specific opcodes
B32X4Ctor,
B32X4Unary,
B32X4Binary,
B32X4BinaryCompI32X4,
B32X4BinaryCompF32X4,
B32X4BinaryBitwise,
B32X4ReplaceLane,
B32X4Splat,
// asm.js specific
B32X4Id
F64StoreMemF32
};
enum NeedsBoundsCheck : uint8_t
@ -382,7 +309,7 @@ typedef UniquePtr<Bytecode> UniqueBytecode;
class Encoder
{
UniqueBytecode bytecode_;
mozilla::DebugOnly<bool> done_;
DebugOnly<bool> done_;
template<class T>
MOZ_WARN_UNUSED_RESULT
@ -398,9 +325,9 @@ class Encoder
done_(false)
{}
bool init(UniqueBytecode bytecode) {
bool init(UniqueBytecode bytecode = UniqueBytecode()) {
if (bytecode) {
bytecode_ = mozilla::Move(bytecode);
bytecode_ = Move(bytecode);
bytecode_->clear();
return true;
}
@ -414,7 +341,20 @@ class Encoder
UniqueBytecode finish() {
MOZ_ASSERT(!done_);
done_ = true;
return mozilla::Move(bytecode_);
return Move(bytecode_);
}
MOZ_WARN_UNUSED_RESULT bool
writeVarU32(uint32_t i) {
do {
uint8_t byte = i & 0x7F;
i >>= 7;
if (i != 0)
byte |= 0x80;
if (!writeU8(byte))
return false;
} while(i != 0);
return true;
}
MOZ_WARN_UNUSED_RESULT bool
@ -453,7 +393,7 @@ class Encoder
bool pcIsPatchable(size_t pc, unsigned size) const {
bool patchable = true;
for (unsigned i = 0; patchable && i < size; i++)
patchable &= Expr((*bytecode_)[pc]) == Expr::Bad;
patchable &= Expr((*bytecode_)[pc]) == Expr::Unreachable;
return patchable;
}
#endif
@ -470,11 +410,6 @@ class Encoder
MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t)));
memcpy(&(*bytecode_)[pc], &i, sizeof(uint32_t));
}
void patchSig(size_t pc, const LifoSig* ptr) {
MOZ_ASSERT(pcIsPatchable(pc, sizeof(LifoSig*)));
memcpy(&(*bytecode_)[pc], &ptr, sizeof(LifoSig*));
}
};
class Decoder
@ -525,7 +460,6 @@ class Decoder
MOZ_WARN_UNUSED_RESULT bool readF32(float* f) { return read(f); }
MOZ_WARN_UNUSED_RESULT bool readU32(uint32_t* u) { return read(u); }
MOZ_WARN_UNUSED_RESULT bool readF64(double* d) { return read(d); }
MOZ_WARN_UNUSED_RESULT bool readSig(const LifoSig* sig) { return read(sig); }
MOZ_WARN_UNUSED_RESULT bool readI32X4(jit::SimdConstant* c) {
int32_t v[4] = { 0, 0, 0, 0 };
@ -546,6 +480,26 @@ class Decoder
return true;
}
MOZ_WARN_UNUSED_RESULT bool readVarU32(uint32_t* decoded) {
*decoded = 0;
uint8_t byte;
uint32_t shift = 0;
do {
if (!readU8(&byte))
return false;
if (!(byte & 0x80)) {
*decoded |= uint32_t(byte & 0x7F) << shift;
return true;
}
*decoded |= uint32_t(byte & 0x7F) << shift;
shift += 7;
} while (shift != 28);
if (!readU8(&byte) || (byte & 0xF0))
return false;
*decoded |= uint32_t(byte) << 28;
return true;
}
// The infallible unpacking API should be used when we are sure that the
// bytecode is well-formed.
uint8_t uncheckedReadU8 () { return uncheckedRead<uint8_t>(); }
@ -553,7 +507,6 @@ class Decoder
float uncheckedReadF32() { return uncheckedRead<float>(); }
uint32_t uncheckedReadU32() { return uncheckedRead<uint32_t>(); }
double uncheckedReadF64() { return uncheckedRead<double>(); }
const LifoSig* uncheckedReadSig() { return uncheckedRead<const LifoSig*>(); }
jit::SimdConstant uncheckedReadI32X4() {
int32_t v[4] = { 0, 0, 0, 0 };
@ -567,6 +520,25 @@ class Decoder
v[i] = uncheckedReadF32();
return jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]);
}
uint32_t uncheckedReadVarU32() {
uint32_t decoded = 0;
uint32_t shift = 0;
uint8_t byte;
do {
byte = uncheckedReadU8();
if (!(byte & 0x80)) {
decoded |= uint32_t(byte & 0x7F) << shift;
return decoded;
}
decoded |= uint32_t(byte & 0x7F) << shift;
shift += 7;
} while (shift != 28);
byte = uncheckedReadU8();
MOZ_ASSERT(!(byte & 0xF0));
decoded |= uint32_t(byte) << 28;
return decoded;
}
};
// Source coordinates for a call site. As they're read sequentially, we
@ -579,7 +551,6 @@ struct SourceCoords {
};
typedef Vector<SourceCoords, 0, SystemAllocPolicy> SourceCoordsVector;
typedef Vector<ValType, 0, SystemAllocPolicy> ValTypeVector;
// The FuncBytecode class contains the intermediate representation of a
// parsed/decoded and validated asm.js/WebAssembly function. The FuncBytecode
@ -595,7 +566,7 @@ class FuncBytecode
SourceCoordsVector callSourceCoords_;
uint32_t index_;
const LifoSig& sig_;
const DeclaredSig& sig_;
UniqueBytecode bytecode_;
ValTypeVector localVars_;
unsigned generateTime_;
@ -606,22 +577,22 @@ class FuncBytecode
unsigned column,
SourceCoordsVector&& sourceCoords,
uint32_t index,
const LifoSig& sig,
const DeclaredSig& sig,
UniqueBytecode bytecode,
ValTypeVector&& localVars,
unsigned generateTime)
: name_(name),
line_(line),
column_(column),
callSourceCoords_(mozilla::Move(sourceCoords)),
callSourceCoords_(Move(sourceCoords)),
index_(index),
sig_(sig),
bytecode_(mozilla::Move(bytecode)),
localVars_(mozilla::Move(localVars)),
bytecode_(Move(bytecode)),
localVars_(Move(localVars)),
generateTime_(generateTime)
{}
UniqueBytecode recycleBytecode() { return mozilla::Move(bytecode_); }
UniqueBytecode recycleBytecode() { return Move(bytecode_); }
PropertyName* name() const { return name_; }
unsigned line() const { return line_; }
@ -629,7 +600,7 @@ class FuncBytecode
const SourceCoords& sourceCoords(size_t i) const { return callSourceCoords_[i]; }
uint32_t index() const { return index_; }
const LifoSig& sig() const { return sig_; }
const DeclaredSig& sig() const { return sig_; }
const Bytecode& bytecode() const { return *bytecode_; }
size_t numLocalVars() const { return localVars_.length(); }

Просмотреть файл

@ -35,14 +35,11 @@ static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
: cx_(cx),
jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
slowFuncs_(cx),
lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
alloc_(&lifo_),
masm_(MacroAssembler::AsmJSToken(), alloc_),
sigs_(cx),
funcEntryOffsets_(cx),
exportFuncIndices_(cx),
funcIndexToExport_(cx),
parallel_(false),
outstanding_(0),
@ -105,20 +102,26 @@ ParallelCompilationEnabled(ExclusiveContext* cx)
}
bool
ModuleGenerator::init()
ModuleGenerator::init(UniqueModuleGeneratorData shared)
{
module_ = cx_->make_unique<ModuleData>();
module_ = MakeUnique<ModuleData>();
if (!module_)
return false;
module_->globalBytes = InitialGlobalDataBytes;
module_->compileArgs = CompileArgs(cx_);
link_ = cx_->make_unique<StaticLinkData>();
link_ = MakeUnique<StaticLinkData>();
if (!link_)
return false;
if (!sigs_.init() || !funcIndexToExport_.init())
shared_ = Move(shared);
threadView_ = MakeUnique<ModuleGeneratorThreadView>(*shared_);
if (!threadView_)
return false;
if (!funcIndexToExport_.init())
return false;
uint32_t numTasks;
@ -142,9 +145,9 @@ ModuleGenerator::init()
if (!tasks_.initCapacity(numTasks))
return false;
JSRuntime* runtime = cx_->compartment()->runtimeFromAnyThread();
JSRuntime* rt = cx_->compartment()->runtimeFromAnyThread();
for (size_t i = 0; i < numTasks; i++)
tasks_.infallibleEmplaceBack(runtime, args(), COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
tasks_.infallibleEmplaceBack(rt, args(), *threadView_, COMPILATION_LIFO_DEFAULT_CHUNK_SIZE);
if (!freeTasks_.reserve(numTasks))
return false;
@ -154,23 +157,6 @@ ModuleGenerator::init()
return true;
}
bool
ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
{
uint32_t globalBytes = module_->globalBytes;
uint32_t pad = ComputeByteAlignment(globalBytes, align);
if (UINT32_MAX - globalBytes < pad + bytes)
return false;
globalBytes += pad;
*globalDataOffset = globalBytes;
globalBytes += bytes;
module_->globalBytes = globalBytes;
return true;
}
bool
ModuleGenerator::finishOutstandingTask()
{
@ -210,6 +196,7 @@ ModuleGenerator::finishTask(IonCompileTask* task)
results.offsets().offsetBy(offsetInWhole);
// Record the non-profiling entry for whole-module linking later.
// Cannot simply append because funcIndex order is nonlinear.
if (func.index() >= funcEntryOffsets_.length()) {
if (!funcEntryOffsets_.resize(func.index() + 1))
return false;
@ -243,18 +230,21 @@ ModuleGenerator::finishTask(IonCompileTask* task)
return true;
}
const LifoSig*
ModuleGenerator::newLifoSig(const MallocSig& sig)
bool
ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
{
SigSet::AddPtr p = sigs_.lookupForAdd(sig);
if (p)
return *p;
uint32_t globalBytes = module_->globalBytes;
LifoSig* lifoSig = LifoSig::new_(lifo_, sig);
if (!lifoSig || !sigs_.add(p, lifoSig))
return nullptr;
uint32_t pad = ComputeByteAlignment(globalBytes, align);
if (UINT32_MAX - globalBytes < pad + bytes)
return false;
return lifoSig;
globalBytes += pad;
*globalDataOffset = globalBytes;
globalBytes += bytes;
module_->globalBytes = globalBytes;
return true;
}
bool
@ -279,17 +269,35 @@ ModuleGenerator::allocateGlobalVar(ValType type, uint32_t* globalDataOffset)
return allocateGlobalBytes(width, width, globalDataOffset);
}
bool
ModuleGenerator::declareImport(MallocSig&& sig, unsigned* index)
void
ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
{
static_assert(Module::SizeOfImportExit % sizeof(void*) == 0, "word aligned");
MOZ_ASSERT(shared_->sigs[sigIndex] == Sig());
shared_->sigs[sigIndex] = Move(sig);
}
uint32_t globalDataOffset;
if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
const DeclaredSig&
ModuleGenerator::sig(uint32_t index) const
{
return shared_->sigs[index];
}
bool
ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset)
{
MOZ_ASSERT(importIndex == module_->imports.length());
Sig copy;
if (!copy.clone(sig(sigIndex)))
return false;
if (!module_->imports.emplaceBack(Move(copy), globalDataOffset))
return false;
*index = unsigned(module_->imports.length());
return module_->imports.emplaceBack(Move(sig), globalDataOffset);
ModuleImportGeneratorData& import = shared_->imports[importIndex];
MOZ_ASSERT(!import.sig);
import.sig = &shared_->sigs[sigIndex];
import.globalDataOffset = globalDataOffset;
return true;
}
uint32_t
@ -298,16 +306,11 @@ ModuleGenerator::numImports() const
return module_->imports.length();
}
uint32_t
ModuleGenerator::importExitGlobalDataOffset(uint32_t index) const
const ModuleImportGeneratorData&
ModuleGenerator::import(uint32_t index) const
{
return module_->imports[index].exitGlobalDataOffset();
}
const MallocSig&
ModuleGenerator::importSig(uint32_t index) const
{
return module_->imports[index].sig();
MOZ_ASSERT(shared_->imports[index].sig);
return shared_->imports[index];
}
bool
@ -321,7 +324,7 @@ ModuleGenerator::defineImport(uint32_t index, ProfilingOffsets interpExit, Profi
}
bool
ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* exportIndex)
ModuleGenerator::declareExport(uint32_t funcIndex, uint32_t* exportIndex)
{
FuncIndexMap::AddPtr p = funcIndexToExport_.lookupForAdd(funcIndex);
if (p) {
@ -329,9 +332,13 @@ ModuleGenerator::declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* ex
return true;
}
Sig copy;
if (!copy.clone(funcSig(funcIndex)))
return false;
*exportIndex = module_->exports.length();
return funcIndexToExport_.add(p, funcIndex, *exportIndex) &&
module_->exports.append(Move(sig)) &&
module_->exports.append(Move(copy)) &&
exportFuncIndices_.append(funcIndex);
}
@ -341,7 +348,7 @@ ModuleGenerator::exportFuncIndex(uint32_t index) const
return exportFuncIndices_[index];
}
const MallocSig&
const Sig&
ModuleGenerator::exportSig(uint32_t index) const
{
return module_->exports[index].sig();
@ -360,6 +367,21 @@ ModuleGenerator::defineExport(uint32_t index, Offsets offsets)
return module_->codeRanges.emplaceBack(CodeRange::Entry, offsets);
}
bool
ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
{
MOZ_ASSERT(!shared_->funcSigs[funcIndex]);
shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex];
return true;
}
const DeclaredSig&
ModuleGenerator::funcSig(uint32_t funcIndex) const
{
MOZ_ASSERT(shared_->funcSigs[funcIndex]);
return *shared_->funcSigs[funcIndex];
}
bool
ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
UniqueBytecode* recycled, FunctionGenerator* fg)
@ -384,21 +406,21 @@ ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
}
bool
ModuleGenerator::finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueBytecode bytecode,
unsigned generateTime, FunctionGenerator* fg)
ModuleGenerator::finishFunc(uint32_t funcIndex, UniqueBytecode bytecode, unsigned generateTime,
FunctionGenerator* fg)
{
MOZ_ASSERT(activeFunc_ == fg);
UniqueFuncBytecode func = cx_->make_unique<FuncBytecode>(fg->name_,
fg->line_,
fg->column_,
Move(fg->callSourceCoords_),
funcIndex,
sig,
Move(bytecode),
Move(fg->localVars_),
generateTime
);
UniqueFuncBytecode func =
js::MakeUnique<FuncBytecode>(fg->name_,
fg->line_,
fg->column_,
Move(fg->callSourceCoords_),
funcIndex,
funcSig(funcIndex),
Move(bytecode),
Move(fg->localVars_),
generateTime);
if (!func)
return false;

Просмотреть файл

@ -28,9 +28,11 @@ namespace js {
namespace wasm {
class FunctionGenerator;
typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
// A slow function describes a function that took longer than msThreshold to
// validate and compile.
struct SlowFunction
{
SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
@ -46,53 +48,96 @@ struct SlowFunction
};
typedef Vector<SlowFunction> SlowFunctionVector;
// The ModuleGeneratorData holds all the state shared between the
// ModuleGenerator and ModuleGeneratorThreadView. The ModuleGeneratorData is
// encapsulated by ModuleGenerator/ModuleGeneratorThreadView classes which
// present a race-free interface to the code in each thread assuming any given
// element is initialized by the ModuleGenerator thread before an index to that
// element is written to Bytecode sent to a ModuleGeneratorThreadView thread.
// Once created, the Vectors are never resized.
struct ModuleImportGeneratorData
{
DeclaredSig* sig;
uint32_t globalDataOffset;
};
typedef Vector<ModuleImportGeneratorData, 0, SystemAllocPolicy> ModuleImportGeneratorDataVector;
struct ModuleGeneratorData
{
DeclaredSigVector sigs;
DeclaredSigPtrVector funcSigs;
ModuleImportGeneratorDataVector imports;
};
typedef UniquePtr<ModuleGeneratorData> UniqueModuleGeneratorData;
// The ModuleGeneratorThreadView class presents a restricted, read-only view of
// the shared state needed by helper threads. There is only one
// ModuleGeneratorThreadView object owned by ModuleGenerator and referenced by
// all compile tasks.
class ModuleGeneratorThreadView
{
const ModuleGeneratorData& shared_;
public:
explicit ModuleGeneratorThreadView(const ModuleGeneratorData& shared)
: shared_(shared)
{}
const DeclaredSig& sig(uint32_t sigIndex) const {
return shared_.sigs[sigIndex];
}
const DeclaredSig& funcSig(uint32_t funcIndex) const {
MOZ_ASSERT(shared_.funcSigs[funcIndex]);
return *shared_.funcSigs[funcIndex];
}
const ModuleImportGeneratorData& import(uint32_t importIndex) const {
MOZ_ASSERT(shared_.imports[importIndex].sig);
return shared_.imports[importIndex];
}
};
// A ModuleGenerator encapsulates the creation of a wasm module. During the
// lifetime of a ModuleGenerator, a sequence of FunctionGenerators are created
// and destroyed to compile the individual function bodies. After generating all
// functions, ModuleGenerator::finish() must be called to complete the
// compilation and extract the resulting wasm module.
class MOZ_STACK_CLASS ModuleGenerator
{
typedef Vector<uint32_t> FuncOffsetVector;
typedef Vector<uint32_t> FuncIndexVector;
typedef UniquePtr<ModuleGeneratorThreadView> UniqueModuleGeneratorThreadView;
typedef HashMap<uint32_t, uint32_t> FuncIndexMap;
struct SigHashPolicy
{
typedef const MallocSig& Lookup;
static HashNumber hash(Lookup l) { return l.hash(); }
static bool match(const LifoSig* lhs, Lookup rhs) { return *lhs == rhs; }
};
typedef HashSet<const LifoSig*, SigHashPolicy> SigSet;
ExclusiveContext* cx_;
ExclusiveContext* cx_;
jit::JitContext jcx_;
// Data handed back to the caller in finish()
UniqueModuleData module_;
UniqueStaticLinkData link_;
SlowFunctionVector slowFuncs_;
UniqueModuleData module_;
UniqueStaticLinkData link_;
SlowFunctionVector slowFuncs_;
// Data scoped to the ModuleGenerator's lifetime
LifoAlloc lifo_;
jit::JitContext jcx_;
jit::TempAllocator alloc_;
jit::MacroAssembler masm_;
SigSet sigs_;
FuncOffsetVector funcEntryOffsets_;
FuncIndexVector exportFuncIndices_;
FuncIndexMap funcIndexToExport_;
UniqueModuleGeneratorData shared_;
LifoAlloc lifo_;
jit::TempAllocator alloc_;
jit::MacroAssembler masm_;
Uint32Vector funcEntryOffsets_;
Uint32Vector exportFuncIndices_;
FuncIndexMap funcIndexToExport_;
// Parallel compilation
bool parallel_;
uint32_t outstanding_;
Vector<IonCompileTask> tasks_;
Vector<IonCompileTask*> freeTasks_;
bool parallel_;
uint32_t outstanding_;
UniqueModuleGeneratorThreadView threadView_;
Vector<IonCompileTask> tasks_;
Vector<IonCompileTask*> freeTasks_;
// Assertions
DebugOnly<FunctionGenerator*> activeFunc_;
DebugOnly<bool> finishedFuncs_;
DebugOnly<FunctionGenerator*> activeFunc_;
DebugOnly<bool> finishedFuncs_;
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
bool finishOutstandingTask();
bool finishTask(IonCompileTask* task);
@ -100,36 +145,40 @@ class MOZ_STACK_CLASS ModuleGenerator
explicit ModuleGenerator(ExclusiveContext* cx);
~ModuleGenerator();
bool init();
bool init(UniqueModuleGeneratorData shared);
CompileArgs args() const { return module_->compileArgs; }
jit::MacroAssembler& masm() { return masm_; }
const FuncOffsetVector& funcEntryOffsets() const { return funcEntryOffsets_; }
const LifoSig* newLifoSig(const MallocSig& sig);
const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; }
// Global data:
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
bool allocateGlobalVar(ValType type, uint32_t* globalDataOffset);
// Signatures:
void initSig(uint32_t sigIndex, Sig&& sig);
const DeclaredSig& sig(uint32_t sigIndex) const;
// Imports:
bool declareImport(MallocSig&& sig, uint32_t* index);
bool initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset);
uint32_t numImports() const;
uint32_t importExitGlobalDataOffset(uint32_t index) const;
const MallocSig& importSig(uint32_t index) const;
const ModuleImportGeneratorData& import(uint32_t index) const;
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
// Exports:
bool declareExport(MallocSig&& sig, uint32_t funcIndex, uint32_t* exportIndex);
bool declareExport(uint32_t funcIndex, uint32_t* exportIndex);
uint32_t numExports() const;
uint32_t exportFuncIndex(uint32_t index) const;
const MallocSig& exportSig(uint32_t index) const;
const Sig& exportSig(uint32_t index) const;
bool defineExport(uint32_t index, Offsets offsets);
// Functions:
bool initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
const DeclaredSig& funcSig(uint32_t funcIndex) const;
bool startFunc(PropertyName* name, unsigned line, unsigned column, UniqueBytecode* recycled,
FunctionGenerator* fg);
bool finishFunc(uint32_t funcIndex, const LifoSig& sig, UniqueBytecode bytecode,
unsigned generateTime, FunctionGenerator* fg);
bool finishFunc(uint32_t funcIndex, UniqueBytecode bytecode, unsigned generateTime,
FunctionGenerator* fg);
bool finishFuncs();
// Function-pointer tables:
@ -160,6 +209,7 @@ class MOZ_STACK_CLASS ModuleGenerator
// anything else. After the body is complete, ModuleGenerator::finishFunc must
// be called before the FunctionGenerator is destroyed and the next function is
// started.
class MOZ_STACK_CLASS FunctionGenerator
{
friend class ModuleGenerator;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -25,6 +25,12 @@
namespace js {
namespace wasm {
class ModuleGeneratorThreadView;
typedef Vector<jit::MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef jit::ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef jit::ABIArgIter<ValTypeVector> ABIArgValTypeIter;
// The FuncCompileResults contains the results of compiling a single function
// body, ready to be merged into the whole-module MacroAssembler.
class FuncCompileResults
@ -62,6 +68,7 @@ class IonCompileTask
{
JSRuntime* const runtime_;
const CompileArgs args_;
ModuleGeneratorThreadView& mg_;
LifoAlloc lifo_;
UniqueFuncBytecode func_;
mozilla::Maybe<FuncCompileResults> results_;
@ -70,9 +77,10 @@ class IonCompileTask
IonCompileTask& operator=(const IonCompileTask&) = delete;
public:
IonCompileTask(JSRuntime* runtime, CompileArgs args, size_t defaultChunkSize)
: runtime_(runtime),
IonCompileTask(JSRuntime* rt, CompileArgs args, ModuleGeneratorThreadView& mg, size_t defaultChunkSize)
: runtime_(rt),
args_(args),
mg_(mg),
lifo_(defaultChunkSize),
func_(nullptr)
{}
@ -85,6 +93,9 @@ class IonCompileTask
CompileArgs args() const {
return args_;
}
ModuleGeneratorThreadView& mg() const {
return mg_;
}
void init(UniqueFuncBytecode func) {
MOZ_ASSERT(!func_);
func_ = mozilla::Move(func);

Просмотреть файл

@ -243,14 +243,14 @@ StaticLinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
}
static size_t
SerializedSigSize(const MallocSig& sig)
SerializedSigSize(const Sig& sig)
{
return sizeof(ExprType) +
SerializedPodVectorSize(sig.args());
}
static uint8_t*
SerializeSig(uint8_t* cursor, const MallocSig& sig)
SerializeSig(uint8_t* cursor, const Sig& sig)
{
cursor = WriteScalar<ExprType>(cursor, sig.ret());
cursor = SerializePodVector(cursor, sig.args());
@ -258,33 +258,22 @@ SerializeSig(uint8_t* cursor, const MallocSig& sig)
}
static const uint8_t*
DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, MallocSig* sig)
DeserializeSig(ExclusiveContext* cx, const uint8_t* cursor, Sig* sig)
{
ExprType ret;
cursor = ReadScalar<ExprType>(cursor, &ret);
MallocSig::ArgVector args;
ValTypeVector args;
cursor = DeserializePodVector(cx, cursor, &args);
if (!cursor)
return nullptr;
sig->init(Move(args), ret);
*sig = Sig(Move(args), ret);
return cursor;
}
static bool
CloneSig(JSContext* cx, const MallocSig& sig, MallocSig* out)
{
MallocSig::ArgVector args;
if (!ClonePodVector(cx, sig.args(), &args))
return false;
out->init(Move(args), sig.ret());
return true;
}
static size_t
SizeOfSigExcludingThis(const MallocSig& sig, MallocSizeOf mallocSizeOf)
SizeOfSigExcludingThis(const Sig& sig, MallocSizeOf mallocSizeOf)
{
return sig.args().sizeOfExcludingThis(mallocSizeOf);
}
@ -316,7 +305,7 @@ bool
Export::clone(JSContext* cx, Export* out) const
{
out->pod = pod;
return CloneSig(cx, sig_, &out->sig_);
return out->sig_.clone(sig_);
}
size_t
@ -352,7 +341,7 @@ bool
Import::clone(JSContext* cx, Import* out) const
{
out->pod = pod;
return CloneSig(cx, sig_, &out->sig_);
return out->sig_.clone(sig_);
}
size_t

Просмотреть файл

@ -103,14 +103,14 @@ typedef UniquePtr<StaticLinkData> UniqueStaticLinkData;
class Export
{
MallocSig sig_;
Sig sig_;
struct CacheablePod {
uint32_t stubOffset_;
} pod;
public:
Export() = default;
explicit Export(MallocSig&& sig)
explicit Export(Sig&& sig)
: sig_(Move(sig))
{
pod.stubOffset_ = UINT32_MAX;
@ -128,7 +128,7 @@ class Export
uint32_t stubOffset() const {
return pod.stubOffset_;
}
const MallocSig& sig() const {
const Sig& sig() const {
return sig_;
}
@ -143,7 +143,7 @@ typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
class Import
{
MallocSig sig_;
Sig sig_;
struct CacheablePod {
uint32_t exitGlobalDataOffset_;
uint32_t interpExitCodeOffset_;
@ -153,7 +153,7 @@ class Import
public:
Import() {}
Import(Import&& rhs) : sig_(Move(rhs.sig_)), pod(rhs.pod) {}
Import(MallocSig&& sig, uint32_t exitGlobalDataOffset)
Import(Sig&& sig, uint32_t exitGlobalDataOffset)
: sig_(Move(sig))
{
pod.exitGlobalDataOffset_ = exitGlobalDataOffset;
@ -170,7 +170,7 @@ class Import
pod.jitExitCodeOffset_ = off;
}
const MallocSig& sig() const {
const Sig& sig() const {
return sig_;
}
uint32_t exitGlobalDataOffset() const {

Просмотреть файл

@ -30,10 +30,6 @@ using namespace js::wasm;
using mozilla::ArrayLength;
using mozilla::MakeEnumeratedRange;
typedef Vector<MIRType, 8, SystemAllocPolicy> MIRTypeVector;
typedef ABIArgIter<MIRTypeVector> ABIArgMIRTypeIter;
typedef ABIArgIter<MallocSig::ArgVector> ABIArgValTypeIter;
static void
AssertStackAlignment(MacroAssembler& masm, uint32_t alignment, uint32_t addBeforeAssert = 0)
{
@ -102,7 +98,7 @@ static bool
GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.exportSig(exportIndex);
const Sig& sig = mg.exportSig(exportIndex);
masm.haltingAlign(CodeAlignment);
@ -293,7 +289,7 @@ GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
}
static void
FillArgumentArray(MacroAssembler& masm, const MallocSig::ArgVector& args, unsigned argOffset,
FillArgumentArray(MacroAssembler& masm, const ValTypeVector& args, unsigned argOffset,
unsigned offsetToCallerStackArgs, Register scratch)
{
for (ABIArgValTypeIter i(args); !i.done(); i++) {
@ -340,7 +336,7 @@ GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLa
ProfilingOffsets* offsets)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.importSig(importIndex);
const Sig& sig = *mg.import(importIndex).sig;
masm.setFramePushed(0);
@ -445,7 +441,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
Label* throwLabel, ProfilingOffsets* offsets)
{
MacroAssembler& masm = mg.masm();
const MallocSig& sig = mg.importSig(importIndex);
const Sig& sig = *mg.import(importIndex).sig;
masm.setFramePushed(0);
@ -475,7 +471,7 @@ GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
Register scratch = ABIArgGenerator::NonArgReturnReg1; // repeatedly clobbered
// 2.1. Get ExitDatum
unsigned globalDataOffset = mg.importExitGlobalDataOffset(importIndex);
unsigned globalDataOffset = mg.import(importIndex).globalDataOffset;
#if defined(JS_CODEGEN_X64)
masm.append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
#elif defined(JS_CODEGEN_X86)

Просмотреть файл

@ -57,18 +57,14 @@ enum class ValType
B32x4
};
typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
static inline bool
IsSimdType(ValType vt)
{
return vt == ValType::I32x4 || vt == ValType::F32x4 || vt == ValType::B32x4;
}
static inline bool
IsSimdBoolType(ValType vt)
{
return vt == ValType::B32x4;
}
static inline jit::MIRType
ToMIRType(ValType vt)
{
@ -179,6 +175,12 @@ IsSimdType(ExprType et)
return IsVoid(et) ? false : IsSimdType(ValType(et));
}
static inline bool
IsSimdBoolType(ExprType vt)
{
return vt == ExprType::B32x4;
}
static inline jit::MIRType
ToMIRType(ExprType et)
{
@ -195,30 +197,32 @@ ToMIRType(ExprType et)
// duration of module validation+compilation). Thus, long-lived objects like
// WasmModule must use malloced allocation.
template <class AllocPolicy>
class Sig
{
public:
typedef Vector<ValType, 4, AllocPolicy> ArgVector;
private:
ArgVector args_;
ValTypeVector args_;
ExprType ret_;
protected:
explicit Sig(AllocPolicy alloc = AllocPolicy()) : args_(alloc) {}
Sig(Sig&& rhs) : args_(Move(rhs.args_)), ret_(rhs.ret_) {}
Sig(ArgVector&& args, ExprType ret) : args_(Move(args)), ret_(ret) {}
Sig(const Sig&) = delete;
Sig& operator=(const Sig&) = delete;
public:
void init(ArgVector&& args, ExprType ret) {
Sig() : args_(), ret_(ExprType::Void) {}
Sig(Sig&& rhs) : args_(Move(rhs.args_)), ret_(rhs.ret_) {}
Sig(ValTypeVector&& args, ExprType ret) : args_(Move(args)), ret_(ret) {}
bool clone(const Sig& rhs) {
ret_ = rhs.ret_;
MOZ_ASSERT(args_.empty());
args_ = Move(args);
ret_ = ret;
return args_.appendAll(rhs.args_);
}
Sig& operator=(Sig&& rhs) {
ret_ = rhs.ret_;
args_ = Move(rhs.args_);
return *this;
}
ValType arg(unsigned i) const { return args_[i]; }
const ArgVector& args() const { return args_; }
const ValTypeVector& args() const { return args_; }
const ExprType& ret() const { return ret_; }
HashNumber hash() const {
@ -227,9 +231,7 @@ class Sig
hn = mozilla::AddToHash(hn, HashNumber(args_[i]));
return hn;
}
template <class AllocPolicy2>
bool operator==(const Sig<AllocPolicy2>& rhs) const {
bool operator==(const Sig& rhs) const {
if (ret() != rhs.ret())
return false;
if (args().length() != rhs.args().length())
@ -240,39 +242,27 @@ class Sig
}
return true;
}
template <class AllocPolicy2>
bool operator!=(const Sig<AllocPolicy2>& rhs) const {
bool operator!=(const Sig& rhs) const {
return !(*this == rhs);
}
};
class MallocSig : public Sig<SystemAllocPolicy>
{
typedef Sig<SystemAllocPolicy> BaseSig;
// A "declared" signature is a Sig object that is created and owned by the
// ModuleGenerator. These signature objects are read-only and have the same
// lifetime as the ModuleGenerator. This type is useful since some uses of Sig
// need this extended lifetime and want to statically distinguish from the
// common stack-allocated Sig objects that get passed around.
public:
MallocSig() = default;
MallocSig(MallocSig&& rhs) : BaseSig(Move(rhs)) {}
MallocSig(ArgVector&& args, ExprType ret) : BaseSig(Move(args), ret) {}
struct DeclaredSig : Sig
{
DeclaredSig() = default;
DeclaredSig(DeclaredSig&& rhs) : Sig(Move(rhs)) {}
explicit DeclaredSig(Sig&& sig) : Sig(Move(sig)) {}
void operator=(Sig&& rhs) { Sig& base = *this; base = Move(rhs); }
};
class LifoSig : public Sig<LifoAllocPolicy<Fallible>>
{
typedef Sig<LifoAllocPolicy<Fallible>> BaseSig;
LifoSig(ArgVector&& args, ExprType ret) : BaseSig(Move(args), ret) {}
public:
static LifoSig* new_(LifoAlloc& lifo, const MallocSig& src) {
void* mem = lifo.alloc(sizeof(LifoSig));
if (!mem)
return nullptr;
ArgVector args(lifo);
if (!args.appendAll(src.args()))
return nullptr;
return new (mem) LifoSig(Move(args), src.ret());
}
};
typedef Vector<DeclaredSig, 0, SystemAllocPolicy> DeclaredSigVector;
typedef Vector<const DeclaredSig*, 0, SystemAllocPolicy> DeclaredSigPtrVector;
// The (,Profiling,Func)Offsets classes are used to record the offsets of
// different key points in a CodeRange during compilation.

Просмотреть файл

@ -270,7 +270,7 @@ EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFrame
EvalScriptGuard esg(cx);
if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
if (evalType == DIRECT_EVAL && caller.isFunctionFrame())
esg.lookupInEvalCache(linearStr, callerScript, pc);
if (!esg.foundScript()) {

Просмотреть файл

@ -1607,7 +1607,7 @@ ShellObjectMetadataCallback(JSContext* cx, JSObject*)
RootedId id(cx);
RootedValue callee(cx);
for (NonBuiltinScriptFrameIter iter(cx); !iter.done(); ++iter) {
if (iter.isNonEvalFunctionFrame() && iter.compartment() == cx->compartment()) {
if (iter.isFunctionFrame() && iter.compartment() == cx->compartment()) {
id = INT_TO_JSID(stackIndex);
RootedObject callee(cx, iter.callee(cx));
if (!JS_DefinePropertyById(cx, stack, id, callee, 0,

Просмотреть файл

@ -1221,6 +1221,9 @@ for (var i = 1; i < 10; ++i) {
asmLink(asmCompile('glob', 'ffi', c), this, ffi)();
}
// Bug 1240524
assertAsmTypeFail(USE_ASM + B32 + 'var x = b4(0, 0, 0, 0); frd(x);');
// Passing boolean results to extern functions.
// Verify that these functions are typed correctly.
function isone(x) { return (x===1)|0 }

Просмотреть файл

@ -263,7 +263,7 @@ jit::EnsureHasScopeObjects(JSContext* cx, AbstractFramePtr fp)
// Ion does not compile eval scripts.
MOZ_ASSERT(!fp.isEvalFrame());
if (fp.isNonEvalFunctionFrame() &&
if (fp.isFunctionFrame() &&
fp.callee()->needsCallObject() &&
!fp.hasCallObj())
{

Просмотреть файл

@ -1723,7 +1723,7 @@ CopyFromRematerializedFrame(JSContext* cx, JitActivation* act, uint8_t* fp, size
frame->setScopeChain(rematFrame->scopeChain());
if (frame->isNonEvalFunctionFrame())
if (frame->isFunctionFrame())
frame->thisArgument() = rematFrame->thisArgument();
for (unsigned i = 0; i < frame->numActualArgs(); i++)

Просмотреть файл

@ -357,13 +357,7 @@ BaselineCompiler::emitPrologue()
// is passed in R1, so we have to be careful not to clobber it.
// Initialize BaselineFrame::flags.
uint32_t flags = 0;
if (script->isForEval())
flags |= BaselineFrame::EVAL;
masm.store32(Imm32(flags), frame.addressOfFlags());
if (script->isForEval())
masm.storePtr(ImmGCPtr(script), frame.addressOfEvalScript());
masm.store32(Imm32(0), frame.addressOfFlags());
// Handle scope chain pre-initialization (in case GC gets run
// during stack check). For global and eval scripts, the scope

Просмотреть файл

@ -33,7 +33,7 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator)
replaceCalleeToken(MarkCalleeToken(trc, calleeToken()));
// Mark |this|, actual and formal args.
if (isNonEvalFunctionFrame()) {
if (isFunctionFrame()) {
TraceRoot(trc, &thisArgument(), "baseline-this");
unsigned numArgs = js::Max(numActualArgs(), numFormalArgs());
@ -48,11 +48,8 @@ BaselineFrame::trace(JSTracer* trc, JitFrameIterator& frameIterator)
if (hasReturnValue())
TraceRoot(trc, returnValue().address(), "baseline-rval");
if (isEvalFrame()) {
TraceRoot(trc, &evalScript_, "baseline-evalscript");
if (script()->isDirectEvalInFunction())
TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget");
}
if (isEvalFrame() && script()->isDirectEvalInFunction())
TraceRoot(trc, evalNewTargetAddress(), "baseline-evalNewTarget");
if (hasArgsObj())
TraceRoot(trc, &argsObj_, "baseline-args-obj");
@ -126,7 +123,7 @@ BaselineFrame::initStrictEvalScopeObjects(JSContext* cx)
bool
BaselineFrame::initFunctionScopeObjects(JSContext* cx)
{
MOZ_ASSERT(isNonEvalFunctionFrame());
MOZ_ASSERT(isFunctionFrame());
MOZ_ASSERT(callee()->needsCallObject());
CallObject* callobj = CallObject::createForFunction(cx, this);
@ -148,11 +145,6 @@ BaselineFrame::initForOsr(InterpreterFrame* fp, uint32_t numStackValues)
if (fp->hasCallObjUnchecked())
flags_ |= BaselineFrame::HAS_CALL_OBJ;
if (fp->isEvalFrame()) {
flags_ |= BaselineFrame::EVAL;
evalScript_ = fp->script();
}
if (fp->script()->needsArgsObj() && fp->hasArgsObj()) {
flags_ |= BaselineFrame::HAS_ARGS_OBJ;
argsObj_ = &fp->argsObj();

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше