diff options
author | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-08-08 12:00:14 -0700 |
---|---|---|
committer | Emilio Cobos Álvarez <ecoal95@gmail.com> | 2016-08-08 13:03:55 -0700 |
commit | 28d7c2dca806301ad324a49da290dc236d384b44 (patch) | |
tree | 8d62c0988cd1e66acb82390635204c4c66450f8e | |
parent | c420a870c1b1bca7e740e8bb737ef2bcdb1a139d (diff) | |
download | servo-28d7c2dca806301ad324a49da290dc236d384b44.tar.gz servo-28d7c2dca806301ad324a49da290dc236d384b44.zip |
layout: Take into account the client point for fixed positioned stacking contexts.
-rw-r--r-- | components/gfx/display_list/mod.rs | 48 | ||||
-rw-r--r-- | components/layout/query.rs | 12 | ||||
-rw-r--r-- | components/layout_thread/lib.rs | 17 | ||||
-rw-r--r-- | components/script/dom/document.rs | 29 | ||||
-rw-r--r-- | components/script/dom/window.rs | 14 | ||||
-rw-r--r-- | components/script_layout_interface/message.rs | 2 | ||||
-rw-r--r-- | components/script_layout_interface/rpc.rs | 2 |
7 files changed, 79 insertions, 45 deletions
diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index c7143457db9..2942c75ac67 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -61,7 +61,7 @@ pub static BLUR_INFLATION_FACTOR: i32 = 3; /// LayerInfo is used to store PaintLayer metadata during DisplayList construction. /// It is also used for tracking LayerIds when creating layers to preserve ordering when /// layered DisplayItems should render underneath unlayered DisplayItems. -#[derive(Clone, Copy, HeapSizeOf, Deserialize, Serialize)] +#[derive(Clone, Copy, HeapSizeOf, Deserialize, Serialize, Debug)] pub struct LayerInfo { /// The base LayerId of this layer. pub layer_id: LayerId, @@ -133,7 +133,7 @@ impl<'a> DisplayListTraversal<'a> { } } -#[derive(HeapSizeOf, Deserialize, Serialize)] +#[derive(HeapSizeOf, Deserialize, Serialize, Debug)] pub struct StackingContextOffsets { pub start: u32, pub block_backgrounds_and_borders: u32, @@ -508,9 +508,12 @@ impl DisplayList { &draw_target, &stacking_context.filters, stacking_context.blend_mode); } - /// 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) + /// Return all nodes containing the point of interest, bottommost first, and + /// respecting the `pointer-events` CSS property. + pub fn hit_test(&self, + translated_point: &Point2D<Au>, + client_point: &Point2D<Au>, + scroll_offsets: &ScrollOffsetMap) -> Vec<DisplayItemMetadata> { let mut traversal = DisplayListTraversal { display_list: self, @@ -518,7 +521,11 @@ impl DisplayList { last_item_index: self.list.len() - 1, }; let mut result = Vec::new(); - self.root_stacking_context.hit_test(&mut traversal, point, scroll_offsets, &mut result); + self.root_stacking_context.hit_test(&mut traversal, + translated_point, + client_point, + scroll_offsets, + &mut result); result } } @@ -633,27 +640,35 @@ impl StackingContext { pub fn hit_test<'a>(&self, traversal: &mut DisplayListTraversal<'a>, - point: &Point2D<Au>, + translated_point: &Point2D<Au>, + client_point: &Point2D<Au>, scroll_offsets: &ScrollOffsetMap, result: &mut Vec<DisplayItemMetadata>) { + let is_fixed = match self.layer_info { + Some(ref layer_info) => layer_info.scroll_policy == ScrollPolicy::FixedPosition, + None => false, + }; + + let effective_point = if is_fixed { client_point } else { translated_point }; + // Convert the point into stacking context local transform space. let mut point = if self.context_type == StackingContextType::Real { - let point = *point - self.bounds.origin; + let point = *effective_point - self.bounds.origin; let inv_transform = self.transform.invert(); let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(), point.y.to_f32_px())); Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y)) } else { - *point + *effective_point }; - // Adjust the point to account for the scroll offset if necessary. This can only happen - // when WebRender is in use. + // Adjust the translated point to account for the scroll offset if + // necessary. This can only happen when WebRender is in use. // - // We don't perform this adjustment on the root stacking context because the DOM-side code - // has already translated the point for us (e.g. in `Document::handle_mouse_move_event()`) - // by now. - if self.id != StackingContextId::root() { + // We don't perform this adjustment on the root stacking context because + // the DOM-side code has already translated the point for us (e.g. in + // `Window::hit_test_query()`) by now. + if !is_fixed && self.id != StackingContextId::root() { if let Some(scroll_offset) = scroll_offsets.get(&self.id) { point.x -= Au::from_f32_px(scroll_offset.x); point.y -= Au::from_f32_px(scroll_offset.y); @@ -666,7 +681,7 @@ impl StackingContext { result.push(meta); } } - child.hit_test(traversal, &point, scroll_offsets, result); + child.hit_test(traversal, translated_point, client_point, scroll_offsets, result); } while let Some(item) = traversal.advance(self) { @@ -1366,6 +1381,7 @@ impl DisplayItem { // 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(); + if !base_item.clip.might_intersect_point(&point) { // Clipped out. return None; diff --git a/components/layout/query.rs b/components/layout/query.rs index aed02a9392e..ee06f3865ba 100644 --- a/components/layout/query.rs +++ b/components/layout/query.rs @@ -133,15 +133,21 @@ impl LayoutRPC for LayoutRPCImpl { } } - fn nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress> { - let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y)); + fn nodes_from_point(&self, + page_point: Point2D<f32>, + client_point: Point2D<f32>) -> Vec<UntrustedNodeAddress> { + let page_point = Point2D::new(Au::from_f32_px(page_point.x), + Au::from_f32_px(page_point.y)); + let client_point = Point2D::new(Au::from_f32_px(client_point.x), + Au::from_f32_px(client_point.y)); + let nodes_from_point_list = { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); let result = match rw_data.display_list { None => panic!("Tried to hit test without a DisplayList"), Some(ref display_list) => { - display_list.hit_test(&point, &rw_data.stacking_context_scroll_offsets) + display_list.hit_test(&page_point, &client_point, &rw_data.stacking_context_scroll_offsets) } }; diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 958a2acc71e..c31e2de54ac 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -1038,7 +1038,7 @@ impl LayoutThread { ReflowQueryType::ContentBoxesQuery(_) => { rw_data.content_boxes_response = Vec::new(); }, - ReflowQueryType::HitTestQuery(_, _) => { + ReflowQueryType::HitTestQuery(..) => { rw_data.hit_test_response = (None, false); }, ReflowQueryType::NodeGeometryQuery(_) => { @@ -1202,12 +1202,21 @@ impl LayoutThread { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.content_boxes_response = process_content_boxes_request(node, &mut root_flow); }, - ReflowQueryType::HitTestQuery(point, update_cursor) => { - let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y)); + ReflowQueryType::HitTestQuery(translated_point, client_point, update_cursor) => { + let translated_point = + Point2D::new(Au::from_f32_px(translated_point.x), + Au::from_f32_px(translated_point.y)); + + let client_point = + Point2D::new(Au::from_f32_px(client_point.x), + Au::from_f32_px(client_point.y)); + let result = rw_data.display_list .as_ref() .expect("Tried to hit test with no display list") - .hit_test(&point, &rw_data.stacking_context_scroll_offsets); + .hit_test(&translated_point, + &client_point, + &rw_data.stacking_context_scroll_offsets); rw_data.hit_test_response = (result.last().cloned(), update_cursor); }, ReflowQueryType::NodeGeometryQuery(node) => { diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 9366f9e18bb..5f1bf4c45cd 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -672,9 +672,7 @@ impl Document { }; debug!("{}: at {:?}", mouse_event_type_string, client_point); - let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32, - client_point.y + self.window.PageYOffset() as f32); - let node = match self.window.hit_test_query(page_point, false) { + let node = match self.window.hit_test_query(client_point, false) { Some(node_address) => { debug!("node address is {:?}", node_address); node::from_untrusted_node_address(js_runtime, node_address) @@ -786,9 +784,7 @@ impl Document { return; } - let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32, - client_point.y + self.window.PageYOffset() as f32); - let node = match self.window.hit_test_query(page_point, false) { + let node = match self.window.hit_test_query(client_point, false) { Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address), None => return }; @@ -864,22 +860,17 @@ impl Document { js_runtime: *mut JSRuntime, client_point: Option<Point2D<f32>>, prev_mouse_over_target: &MutNullableHeap<JS<Element>>) { - let page_point = match client_point { + let client_point = match client_point { None => { // If there's no point, there's no target under the mouse // FIXME: dispatch mouseout here. We have no point. prev_mouse_over_target.set(None); return; } - Some(ref client_point) => { - Point2D::new(client_point.x + self.window.PageXOffset() as f32, - client_point.y + self.window.PageYOffset() as f32) - } + Some(client_point) => client_point, }; - let client_point = client_point.unwrap(); - - let maybe_new_target = self.window.hit_test_query(page_point, true).and_then(|address| { + let maybe_new_target = self.window.hit_test_query(client_point, true).and_then(|address| { let node = node::from_untrusted_node_address(js_runtime, address); node.inclusive_ancestors() .filter_map(Root::downcast::<Element>) @@ -1593,7 +1584,11 @@ impl Document { } pub fn nodes_from_point(&self, page_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> { - self.window.layout().nodes_from_point(*page_point) + let client_point = + Point2D::new(page_point.x - self.window.PageXOffset() as f32, + page_point.y - self.window.PageYOffset() as f32); + + self.window.layout().nodes_from_point(*page_point, client_point) } } @@ -2824,10 +2819,10 @@ impl DocumentMethods for Document { return None; } - let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) }; - match self.window.hit_test_query(*point, false) { Some(untrusted_node_address) => { + let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) }; + let node = node::from_untrusted_node_address(js_runtime, untrusted_node_address); let parent_node = node.GetParentNode().unwrap(); let element_ref = node.downcast::<Element>().unwrap_or_else(|| { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 8aaa0adcde3..e6e527aea9c 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1271,10 +1271,18 @@ impl Window { self.layout_rpc.node_geometry().client_rect } - pub fn hit_test_query(&self, hit_test_request: Point2D<f32>, update_cursor: bool) + pub fn hit_test_query(&self, + client_point: Point2D<f32>, + update_cursor: bool) -> Option<UntrustedNodeAddress> { + let translated_point = + Point2D::new(client_point.x + self.PageXOffset() as f32, + client_point.y + self.PageYOffset() as f32); + if !self.reflow(ReflowGoal::ForScriptQuery, - ReflowQueryType::HitTestQuery(hit_test_request, update_cursor), + ReflowQueryType::HitTestQuery(translated_point, + client_point, + update_cursor), ReflowReason::Query) { return None } @@ -1770,7 +1778,7 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue ReflowQueryType::NoQuery => "\tNoQuery", ReflowQueryType::ContentBoxQuery(_n) => "\tContentBoxQuery", ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery", - ReflowQueryType::HitTestQuery(_n, _o) => "\tHitTestQuery", + ReflowQueryType::HitTestQuery(..) => "\tHitTestQuery", ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", ReflowQueryType::NodeLayerIdQuery(_n) => "\tNodeLayerIdQuery", ReflowQueryType::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery", diff --git a/components/script_layout_interface/message.rs b/components/script_layout_interface/message.rs index 14580be4881..5d16aeffb0a 100644 --- a/components/script_layout_interface/message.rs +++ b/components/script_layout_interface/message.rs @@ -98,7 +98,7 @@ pub enum ReflowQueryType { ContentBoxQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress), NodeOverflowQuery(TrustedNodeAddress), - HitTestQuery(Point2D<f32>, bool), + HitTestQuery(Point2D<f32>, Point2D<f32>, bool), NodeGeometryQuery(TrustedNodeAddress), NodeLayerIdQuery(TrustedNodeAddress), NodeScrollGeometryQuery(TrustedNodeAddress), diff --git a/components/script_layout_interface/rpc.rs b/components/script_layout_interface/rpc.rs index 21cac2a19c2..a470e76286e 100644 --- a/components/script_layout_interface/rpc.rs +++ b/components/script_layout_interface/rpc.rs @@ -38,7 +38,7 @@ pub trait LayoutRPC { /// Query layout for the resolve values of the margin properties for an element. fn margin_style(&self) -> MarginStyleResponse; - fn nodes_from_point(&self, point: Point2D<f32>) -> Vec<UntrustedNodeAddress>; + fn nodes_from_point(&self, page_point: Point2D<f32>, client_point: Point2D<f32>) -> Vec<UntrustedNodeAddress>; } pub struct ContentBoxResponse(pub Rect<Au>); |