mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
Auto merge of #8671 - paulrouget:iframe-hit-test, r=pcwalton
Fix clip rect for iframes in hit testing code Fixes #8080 r? @mrobinson <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8671) <!-- Reviewable:end -->
This commit is contained in:
commit
8efc954531
3 changed files with 61 additions and 46 deletions
|
@ -242,7 +242,9 @@ enum ShutdownState {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HitTestResult {
|
struct HitTestResult {
|
||||||
|
/// The topmost layer containing the requested point
|
||||||
layer: Rc<Layer<CompositorData>>,
|
layer: Rc<Layer<CompositorData>>,
|
||||||
|
/// The point in client coordinates of the innermost window or frame containing `layer`
|
||||||
point: TypedPoint2D<LayerPixel, f32>,
|
point: TypedPoint2D<LayerPixel, f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1934,45 +1936,53 @@ impl<Window: WindowMethods> IOCompositor<Window> {
|
||||||
|
|
||||||
fn find_topmost_layer_at_point_for_layer(&self,
|
fn find_topmost_layer_at_point_for_layer(&self,
|
||||||
layer: Rc<Layer<CompositorData>>,
|
layer: Rc<Layer<CompositorData>>,
|
||||||
point: TypedPoint2D<LayerPixel, f32>,
|
point_in_parent_layer: TypedPoint2D<LayerPixel, f32>,
|
||||||
clip_rect: &TypedRect<LayerPixel, f32>)
|
clip_rect_in_parent_layer: &TypedRect<LayerPixel, f32>)
|
||||||
-> Option<HitTestResult> {
|
-> Option<HitTestResult> {
|
||||||
let layer_bounds = *layer.bounds.borrow();
|
let layer_bounds = *layer.bounds.borrow();
|
||||||
let masks_to_bounds = *layer.masks_to_bounds.borrow();
|
let masks_to_bounds = *layer.masks_to_bounds.borrow();
|
||||||
if layer_bounds.is_empty() && masks_to_bounds {
|
if layer_bounds.is_empty() && masks_to_bounds {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
let scroll_offset = layer.extra_data.borrow().scroll_offset;
|
||||||
|
|
||||||
let clipped_layer_bounds = match clip_rect.intersection(&layer_bounds) {
|
// Total offset from parent coordinates to this layer's coordinates.
|
||||||
|
// FIXME: This offset is incorrect for fixed-position layers.
|
||||||
|
let layer_offset = scroll_offset + layer_bounds.origin;
|
||||||
|
|
||||||
|
let clipped_layer_bounds = match clip_rect_in_parent_layer.intersection(&layer_bounds) {
|
||||||
Some(rect) => rect,
|
Some(rect) => rect,
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let clip_rect_for_children = if masks_to_bounds {
|
let clip_rect_for_children = if masks_to_bounds {
|
||||||
Rect::new(Point2D::zero(), clipped_layer_bounds.size)
|
&clipped_layer_bounds
|
||||||
} else {
|
} else {
|
||||||
clipped_layer_bounds.translate(&clip_rect.origin)
|
clip_rect_in_parent_layer
|
||||||
};
|
}.translate(&-layer_offset);
|
||||||
|
|
||||||
let child_point = point - layer_bounds.origin;
|
let child_point = point_in_parent_layer - layer_offset;
|
||||||
for child in layer.children().iter().rev() {
|
for child in layer.children().iter().rev() {
|
||||||
// Translate the clip rect into the child's coordinate system.
|
// Translate the clip rect into the child's coordinate system.
|
||||||
let clip_rect_for_child =
|
|
||||||
clip_rect_for_children.translate(&-*child.content_offset.borrow());
|
|
||||||
let result = self.find_topmost_layer_at_point_for_layer(child.clone(),
|
let result = self.find_topmost_layer_at_point_for_layer(child.clone(),
|
||||||
child_point,
|
child_point,
|
||||||
&clip_rect_for_child);
|
&clip_rect_for_children);
|
||||||
if result.is_some() {
|
if let Some(mut result) = result {
|
||||||
return result;
|
// Return the point in layer coordinates of the topmost frame containing the point.
|
||||||
|
let pipeline_id = layer.extra_data.borrow().pipeline_id;
|
||||||
|
let child_pipeline_id = result.layer.extra_data.borrow().pipeline_id;
|
||||||
|
if pipeline_id == child_pipeline_id {
|
||||||
|
result.point = result.point + layer_offset;
|
||||||
|
}
|
||||||
|
return Some(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let point = point - *layer.content_offset.borrow();
|
if !clipped_layer_bounds.contains(&point_in_parent_layer) {
|
||||||
if !clipped_layer_bounds.contains(&point) {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(HitTestResult { layer: layer, point: point });
|
return Some(HitTestResult { layer: layer, point: point_in_parent_layer });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_topmost_layer_at_point(&self,
|
fn find_topmost_layer_at_point(&self,
|
||||||
|
|
|
@ -121,7 +121,7 @@ pub trait CompositorLayer {
|
||||||
|
|
||||||
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
|
// Takes in a MouseWindowEvent, determines if it should be passed to children, and
|
||||||
// sends the event off to the appropriate pipeline. NB: the cursor position is in
|
// sends the event off to the appropriate pipeline. NB: the cursor position is in
|
||||||
// page coordinates.
|
// client coordinates.
|
||||||
fn send_mouse_event<Window>(&self,
|
fn send_mouse_event<Window>(&self,
|
||||||
compositor: &IOCompositor<Window>,
|
compositor: &IOCompositor<Window>,
|
||||||
event: MouseWindowEvent,
|
event: MouseWindowEvent,
|
||||||
|
|
|
@ -492,9 +492,9 @@ impl Document {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_test(&self, point: &Point2D<f32>) -> Option<UntrustedNodeAddress> {
|
pub fn hit_test(&self, page_point: &Point2D<f32>) -> Option<UntrustedNodeAddress> {
|
||||||
assert!(self.GetDocumentElement().is_some());
|
assert!(self.GetDocumentElement().is_some());
|
||||||
match self.window.layout().hit_test(*point) {
|
match self.window.layout().hit_test(*page_point) {
|
||||||
Ok(HitTestResponse(node_address)) => Some(node_address),
|
Ok(HitTestResponse(node_address)) => Some(node_address),
|
||||||
Err(()) => {
|
Err(()) => {
|
||||||
debug!("layout query error");
|
debug!("layout query error");
|
||||||
|
@ -503,9 +503,9 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nodes_under_mouse(&self, point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
|
pub fn get_nodes_under_mouse(&self, page_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
|
||||||
assert!(self.GetDocumentElement().is_some());
|
assert!(self.GetDocumentElement().is_some());
|
||||||
match self.window.layout().mouse_over(*point) {
|
match self.window.layout().mouse_over(*page_point) {
|
||||||
Ok(MouseOverResponse(node_address)) => node_address,
|
Ok(MouseOverResponse(node_address)) => node_address,
|
||||||
Err(()) => vec![],
|
Err(()) => vec![],
|
||||||
}
|
}
|
||||||
|
@ -604,15 +604,18 @@ impl Document {
|
||||||
pub fn handle_mouse_event(&self,
|
pub fn handle_mouse_event(&self,
|
||||||
js_runtime: *mut JSRuntime,
|
js_runtime: *mut JSRuntime,
|
||||||
_button: MouseButton,
|
_button: MouseButton,
|
||||||
point: Point2D<f32>,
|
client_point: Point2D<f32>,
|
||||||
mouse_event_type: MouseEventType) {
|
mouse_event_type: MouseEventType) {
|
||||||
let mouse_event_type_string = match mouse_event_type {
|
let mouse_event_type_string = match mouse_event_type {
|
||||||
MouseEventType::Click => "click".to_owned(),
|
MouseEventType::Click => "click".to_owned(),
|
||||||
MouseEventType::MouseUp => "mouseup".to_owned(),
|
MouseEventType::MouseUp => "mouseup".to_owned(),
|
||||||
MouseEventType::MouseDown => "mousedown".to_owned(),
|
MouseEventType::MouseDown => "mousedown".to_owned(),
|
||||||
};
|
};
|
||||||
debug!("{}: at {:?}", mouse_event_type_string, point);
|
debug!("{}: at {:?}", mouse_event_type_string, client_point);
|
||||||
let node = match self.hit_test(&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.hit_test(&page_point) {
|
||||||
Some(node_address) => {
|
Some(node_address) => {
|
||||||
debug!("node address is {:?}", node_address);
|
debug!("node address is {:?}", node_address);
|
||||||
node::from_untrusted_node_address(js_runtime, node_address)
|
node::from_untrusted_node_address(js_runtime, node_address)
|
||||||
|
@ -643,8 +646,8 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click
|
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click
|
||||||
let x = point.x as i32;
|
let client_x = client_point.x as i32;
|
||||||
let y = point.y as i32;
|
let client_y = client_point.y as i32;
|
||||||
let clickCount = 1;
|
let clickCount = 1;
|
||||||
let event = MouseEvent::new(&self.window,
|
let event = MouseEvent::new(&self.window,
|
||||||
DOMString::from(mouse_event_type_string),
|
DOMString::from(mouse_event_type_string),
|
||||||
|
@ -652,10 +655,10 @@ impl Document {
|
||||||
EventCancelable::Cancelable,
|
EventCancelable::Cancelable,
|
||||||
Some(&self.window),
|
Some(&self.window),
|
||||||
clickCount,
|
clickCount,
|
||||||
x,
|
client_x,
|
||||||
y,
|
client_y,
|
||||||
x,
|
client_x,
|
||||||
y, // TODO: Get real screen coordinates?
|
client_y, // TODO: Get real screen coordinates?
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -683,9 +686,9 @@ impl Document {
|
||||||
ReflowReason::MouseEvent);
|
ReflowReason::MouseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fire_mouse_event(&self, point: Point2D<f32>, target: &EventTarget, event_name: String) {
|
pub fn fire_mouse_event(&self, client_point: Point2D<f32>, target: &EventTarget, event_name: String) {
|
||||||
let x = point.x.to_i32().unwrap_or(0);
|
let client_x = client_point.x.to_i32().unwrap_or(0);
|
||||||
let y = point.y.to_i32().unwrap_or(0);
|
let client_y = client_point.y.to_i32().unwrap_or(0);
|
||||||
|
|
||||||
let mouse_event = MouseEvent::new(&self.window,
|
let mouse_event = MouseEvent::new(&self.window,
|
||||||
DOMString::from(event_name),
|
DOMString::from(event_name),
|
||||||
|
@ -693,10 +696,10 @@ impl Document {
|
||||||
EventCancelable::Cancelable,
|
EventCancelable::Cancelable,
|
||||||
Some(&self.window),
|
Some(&self.window),
|
||||||
0i32,
|
0i32,
|
||||||
x,
|
client_x,
|
||||||
y,
|
client_y,
|
||||||
x,
|
client_x,
|
||||||
y,
|
client_y,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
@ -709,12 +712,14 @@ impl Document {
|
||||||
|
|
||||||
pub fn handle_mouse_move_event(&self,
|
pub fn handle_mouse_move_event(&self,
|
||||||
js_runtime: *mut JSRuntime,
|
js_runtime: *mut JSRuntime,
|
||||||
point: Option<Point2D<f32>>,
|
client_point: Option<Point2D<f32>>,
|
||||||
prev_mouse_over_targets: &mut RootedVec<JS<Element>>) {
|
prev_mouse_over_targets: &mut RootedVec<JS<Element>>) {
|
||||||
// Build a list of elements that are currently under the mouse.
|
// Build a list of elements that are currently under the mouse.
|
||||||
let mouse_over_addresses = point.as_ref()
|
let mouse_over_addresses = client_point.as_ref().map(|client_point| {
|
||||||
.map(|point| self.get_nodes_under_mouse(point))
|
let page_point = Point2D::new(client_point.x + self.window.PageXOffset() as f32,
|
||||||
.unwrap_or(vec![]);
|
client_point.y + self.window.PageYOffset() as f32);
|
||||||
|
self.get_nodes_under_mouse(&page_point)
|
||||||
|
}).unwrap_or(vec![]);
|
||||||
let mut mouse_over_targets = mouse_over_addresses.iter().map(|node_address| {
|
let mut mouse_over_targets = mouse_over_addresses.iter().map(|node_address| {
|
||||||
node::from_untrusted_node_address(js_runtime, *node_address)
|
node::from_untrusted_node_address(js_runtime, *node_address)
|
||||||
.inclusive_ancestors()
|
.inclusive_ancestors()
|
||||||
|
@ -735,8 +740,8 @@ impl Document {
|
||||||
|
|
||||||
// FIXME: we should be dispatching this event but we lack an actual
|
// FIXME: we should be dispatching this event but we lack an actual
|
||||||
// point to pass to it.
|
// point to pass to it.
|
||||||
if let Some(point) = point {
|
if let Some(client_point) = client_point {
|
||||||
self.fire_mouse_event(point, &target, "mouseout".to_owned());
|
self.fire_mouse_event(client_point, &target, "mouseout".to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -751,8 +756,8 @@ impl Document {
|
||||||
|
|
||||||
let target = target.upcast();
|
let target = target.upcast();
|
||||||
|
|
||||||
if let Some(point) = point {
|
if let Some(client_point) = client_point {
|
||||||
self.fire_mouse_event(point, target, "mouseover".to_owned());
|
self.fire_mouse_event(client_point, target, "mouseover".to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,8 +768,8 @@ impl Document {
|
||||||
mouse_over_addresses[0]);
|
mouse_over_addresses[0]);
|
||||||
|
|
||||||
let target = top_most_node.upcast();
|
let target = top_most_node.upcast();
|
||||||
if let Some(point) = point {
|
if let Some(client_point) = client_point {
|
||||||
self.fire_mouse_event(point, target, "mousemove".to_owned());
|
self.fire_mouse_event(client_point, target, "mousemove".to_owned());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue