mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Switch to using WebRender hit testing
This trades quite a bit of complicated code in Servo for few more messages and a significant performance improvement. In particular, WebRender can search the entire display list at once instead of ping-ponging down the pipeline tree. This allows us to send mouse events to the correct pipeline immediately.
This commit is contained in:
parent
00e2a1c62a
commit
b5d51dd263
20 changed files with 381 additions and 555 deletions
|
@ -206,79 +206,6 @@ impl DisplayList {
|
|||
}
|
||||
}
|
||||
|
||||
// Return all nodes containing the point of interest, bottommost first, and
|
||||
// respecting the `pointer-events` CSS property.
|
||||
pub fn hit_test(&self,
|
||||
point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap)
|
||||
-> Vec<DisplayItemMetadata> {
|
||||
let mut result = Vec::new();
|
||||
let mut traversal = DisplayListTraversal::new(self);
|
||||
self.hit_test_contents(&mut traversal,
|
||||
point,
|
||||
&mut ScrollOffsetLookup::new(&mut HashMap::new(), scroll_offsets),
|
||||
&mut result);
|
||||
result
|
||||
}
|
||||
|
||||
fn hit_test_contents<'a>(&self,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
point: &Point2D<Au>,
|
||||
offset_lookup: &mut ScrollOffsetLookup,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
while let Some(item) = traversal.next() {
|
||||
match item {
|
||||
&DisplayItem::PushStackingContext(ref context_item) => {
|
||||
self.hit_test_stacking_context(&context_item.stacking_context,
|
||||
item.scroll_node_id(),
|
||||
traversal,
|
||||
point,
|
||||
offset_lookup,
|
||||
result);
|
||||
}
|
||||
&DisplayItem::PopStackingContext(_) => return,
|
||||
&DisplayItem::DefineClipScrollNode(ref item) => {
|
||||
offset_lookup.add_clip_scroll_node(&item.node);
|
||||
}
|
||||
_ => {
|
||||
if let Some(meta) = item.hit_test(*point, offset_lookup) {
|
||||
result.push(meta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hit_test_stacking_context<'a>(&self,
|
||||
stacking_context: &StackingContext,
|
||||
clip_id: ClipId,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
point: &Point2D<Au>,
|
||||
offset_lookup: &mut ScrollOffsetLookup,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
debug_assert!(stacking_context.context_type == StackingContextType::Real);
|
||||
|
||||
let mut point = *point - stacking_context.bounds.origin.to_vector();
|
||||
if stacking_context.scroll_policy == ScrollPolicy::Fixed {
|
||||
let old_offset = offset_lookup.calculated_total_offsets.get(&clip_id).cloned();
|
||||
offset_lookup.calculated_total_offsets.insert(clip_id, Vector2D::zero());
|
||||
|
||||
self.hit_test_contents(traversal, &point, offset_lookup, result);
|
||||
|
||||
match old_offset {
|
||||
Some(offset) => offset_lookup.calculated_total_offsets.insert(clip_id, offset),
|
||||
None => offset_lookup.calculated_total_offsets.remove(&clip_id),
|
||||
};
|
||||
} else if let Some(transform) = stacking_context.transform {
|
||||
if let Some(ref mut sublookup) =
|
||||
offset_lookup.new_for_reference_frame(clip_id, &transform, &mut point) {
|
||||
self.hit_test_contents(traversal, &point, sublookup, result);
|
||||
}
|
||||
} else {
|
||||
self.hit_test_contents(traversal, &point, offset_lookup, result);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self) {
|
||||
let mut print_tree = PrintTree::new("Display List".to_owned());
|
||||
self.print_with_tree(&mut print_tree);
|
||||
|
@ -1283,60 +1210,6 @@ impl DisplayItem {
|
|||
}
|
||||
println!("{}+ {:?}", indent, self);
|
||||
}
|
||||
|
||||
fn hit_test(&self,
|
||||
point: Point2D<Au>,
|
||||
offset_lookup: &mut ScrollOffsetLookup)
|
||||
-> Option<DisplayItemMetadata> {
|
||||
// TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit
|
||||
// test elements with `border-radius`, for example.
|
||||
let base_item = self.base();
|
||||
|
||||
let scroll_offset = offset_lookup.full_offset_for_clip_scroll_node(&self.scroll_node_id());
|
||||
let point = Point2D::new(point.x - Au::from_f32_px(scroll_offset.x),
|
||||
point.y - Au::from_f32_px(scroll_offset.y));
|
||||
|
||||
if !base_item.local_clip.clip_rect().contains(&point.to_pointf()) {
|
||||
// Clipped out.
|
||||
return None;
|
||||
}
|
||||
if !self.bounds().contains(&point) {
|
||||
// Can't possibly hit.
|
||||
return None;
|
||||
}
|
||||
if base_item.metadata.pointing.is_none() {
|
||||
// `pointer-events` is `none`. Ignore this item.
|
||||
return None;
|
||||
}
|
||||
|
||||
match *self {
|
||||
DisplayItem::Border(ref border) => {
|
||||
// If the point is inside the border, it didn't hit the border!
|
||||
let interior_rect =
|
||||
Rect::new(
|
||||
Point2D::new(border.base.bounds.origin.x +
|
||||
border.border_widths.left,
|
||||
border.base.bounds.origin.y +
|
||||
border.border_widths.top),
|
||||
Size2D::new(border.base.bounds.size.width -
|
||||
(border.border_widths.left +
|
||||
border.border_widths.right),
|
||||
border.base.bounds.size.height -
|
||||
(border.border_widths.top +
|
||||
border.border_widths.bottom)));
|
||||
if interior_rect.contains(&point) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
DisplayItem::BoxShadow(_) => {
|
||||
// Box shadows can never be hit.
|
||||
return None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Some(base_item.metadata)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DisplayItem {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue