mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Auto merge of #12841 - emilio:hit-test, r=pcwalton
Fix hit testing to take into account nested stacking contexts #12777 <!-- Please describe your changes on the following line: --> Fixes #12833. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #12833 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12841) <!-- Reviewable:end -->
This commit is contained in:
commit
9b4713f536
3 changed files with 100 additions and 17 deletions
|
@ -719,28 +719,32 @@ impl StackingContext {
|
||||||
geometry::f32_rect_to_au_rect(overflow)
|
geometry::f32_rect_to_au_rect(overflow)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_test<'a>(&self,
|
fn hit_test<'a>(&self,
|
||||||
traversal: &mut DisplayListTraversal<'a>,
|
traversal: &mut DisplayListTraversal<'a>,
|
||||||
translated_point: &Point2D<Au>,
|
translated_point: &Point2D<Au>,
|
||||||
client_point: &Point2D<Au>,
|
client_point: &Point2D<Au>,
|
||||||
scroll_offsets: &ScrollOffsetMap,
|
scroll_offsets: &ScrollOffsetMap,
|
||||||
result: &mut Vec<DisplayItemMetadata>) {
|
result: &mut Vec<DisplayItemMetadata>) {
|
||||||
let is_fixed = match self.layer_info {
|
let is_fixed = match self.layer_info {
|
||||||
Some(ref layer_info) => layer_info.scroll_policy == ScrollPolicy::FixedPosition,
|
Some(ref layer_info) => layer_info.scroll_policy == ScrollPolicy::FixedPosition,
|
||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let effective_point = if is_fixed { client_point } else { translated_point };
|
// Convert the parent translated point into stacking context local
|
||||||
|
// transform space if the stacking context isn't fixed.
|
||||||
// Convert the point into stacking context local transform space.
|
//
|
||||||
let mut point = if self.context_type == StackingContextType::Real {
|
// If it's fixed, we need to use the client point anyway, and if it's a
|
||||||
let point = *effective_point - self.bounds.origin;
|
// pseudo-stacking context, our parent's is enough.
|
||||||
|
let mut translated_point = if is_fixed {
|
||||||
|
*client_point
|
||||||
|
} else if self.context_type == StackingContextType::Real {
|
||||||
|
let point = *translated_point - self.bounds.origin;
|
||||||
let inv_transform = self.transform.inverse().unwrap();
|
let inv_transform = self.transform.inverse().unwrap();
|
||||||
let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(),
|
let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(),
|
||||||
point.y.to_f32_px()));
|
point.y.to_f32_px()));
|
||||||
Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y))
|
Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y))
|
||||||
} else {
|
} else {
|
||||||
*effective_point
|
*translated_point
|
||||||
};
|
};
|
||||||
|
|
||||||
// Adjust the translated point to account for the scroll offset if
|
// Adjust the translated point to account for the scroll offset if
|
||||||
|
@ -751,22 +755,23 @@ impl StackingContext {
|
||||||
// `Window::hit_test_query()`) by now.
|
// `Window::hit_test_query()`) by now.
|
||||||
if !is_fixed && self.id != StackingContextId::root() {
|
if !is_fixed && self.id != StackingContextId::root() {
|
||||||
if let Some(scroll_offset) = scroll_offsets.get(&self.id) {
|
if let Some(scroll_offset) = scroll_offsets.get(&self.id) {
|
||||||
point.x -= Au::from_f32_px(scroll_offset.x);
|
translated_point.x -= Au::from_f32_px(scroll_offset.x);
|
||||||
point.y -= Au::from_f32_px(scroll_offset.y);
|
translated_point.y -= Au::from_f32_px(scroll_offset.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for child in self.children() {
|
for child in self.children() {
|
||||||
while let Some(item) = traversal.advance(self) {
|
while let Some(item) = traversal.advance(self) {
|
||||||
if let Some(meta) = item.hit_test(point) {
|
if let Some(meta) = item.hit_test(translated_point) {
|
||||||
result.push(meta);
|
result.push(meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
child.hit_test(traversal, translated_point, client_point, scroll_offsets, result);
|
child.hit_test(traversal, &translated_point, client_point,
|
||||||
|
scroll_offsets, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(item) = traversal.advance(self) {
|
while let Some(item) = traversal.advance(self) {
|
||||||
if let Some(meta) = item.hit_test(point) {
|
if let Some(meta) = item.hit_test(translated_point) {
|
||||||
result.push(meta);
|
result.push(meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6756,6 +6756,12 @@
|
||||||
"url": "/_mozilla/mozilla/global.html"
|
"url": "/_mozilla/mozilla/global.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"mozilla/hit_test_nested_sc.html": [
|
||||||
|
{
|
||||||
|
"path": "mozilla/hit_test_nested_sc.html",
|
||||||
|
"url": "/_mozilla/mozilla/hit_test_nested_sc.html"
|
||||||
|
}
|
||||||
|
],
|
||||||
"mozilla/hit_test_pos_fixed.html": [
|
"mozilla/hit_test_pos_fixed.html": [
|
||||||
{
|
{
|
||||||
"path": "mozilla/hit_test_pos_fixed.html",
|
"path": "mozilla/hit_test_pos_fixed.html",
|
||||||
|
|
72
tests/wpt/mozilla/tests/mozilla/hit_test_nested_sc.html
Normal file
72
tests/wpt/mozilla/tests/mozilla/hit_test_nested_sc.html
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test: Hit testing works well with nested stacking contexts</title>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<style>
|
||||||
|
.outer-sc {
|
||||||
|
transform: translate(-50px, -50px);
|
||||||
|
position: absolute;
|
||||||
|
width: 250px;
|
||||||
|
height: 250px;
|
||||||
|
top: 250px;
|
||||||
|
left: 250px;
|
||||||
|
background: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer-sc:hover {
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-sc {
|
||||||
|
transform: translate(50px, 50px);
|
||||||
|
position: absolute;
|
||||||
|
width: 250px;
|
||||||
|
height: 250px;
|
||||||
|
top: 250px;
|
||||||
|
left: 250px;
|
||||||
|
background: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-sc:hover {
|
||||||
|
background: yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body { margin: 0; padding: 0; }
|
||||||
|
</style>
|
||||||
|
<div class="outer-sc">
|
||||||
|
<div class="inner-sc">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
async_test(function(t) {
|
||||||
|
window.onload = t.step_func(function() {
|
||||||
|
// This should be in the middle of the squares:
|
||||||
|
// +-------------+
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// +-------------+
|
||||||
|
// . <-- here
|
||||||
|
// +-------------+
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// +-------------+
|
||||||
|
//
|
||||||
|
// If we have forgotten to take into account the nested transformations,
|
||||||
|
// then it will report that the inner stacking context is at that point
|
||||||
|
// (but obviously shouldn't).
|
||||||
|
var points = document.elementsFromPoint(450 + 25, 450 + 25);
|
||||||
|
assert_true(points.length > 0,
|
||||||
|
"Should report at least one element");
|
||||||
|
assert_true(points[0].className !== "inner-sc",
|
||||||
|
"Should not be the inner stacking context");
|
||||||
|
t.done();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue