Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

layout: Take into account the client point for fixed positioned stacking contexts #12777

Merged
merged 1 commit into from Aug 11, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

layout: Take into account the client point for fixed positioned stack…

…ing contexts.
  • Loading branch information
emilio committed Aug 8, 2016
commit 28d7c2dca806301ad324a49da290dc236d384b44
@@ -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,17 +508,24 @@ 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,
current_item_index: 0,
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;
@@ -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)
}
};

@@ -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) => {
@@ -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(|| {
@@ -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",
@@ -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),
@@ -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>);
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.