Auto merge of #15768 - ferjm:issue-15592-document-elementsFromPoint, r=emilio

Trigger reflow on document.elementsFromPoint

As [suggested](https://github.com/servo/servo/issues/15592#issuecomment-280379805) by @jdm `Document::nodes_from_point` now triggers a reflow.

I added a new reftest that panics with `ERROR:servo: Tried to hit test without a DisplayList` if this patch is not applied.

- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #15592.
- [X] There are tests for these changes

<!-- 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/15768)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-03-02 11:43:17 -08:00 committed by GitHub
commit fa32d50c7a
9 changed files with 71 additions and 33 deletions

View file

@ -73,7 +73,7 @@ mod linked_list;
mod list_item;
mod model;
mod multicol;
mod opaque_node;
pub mod opaque_node;
pub mod parallel;
mod persistent_list;
pub mod query;

View file

@ -94,6 +94,9 @@ pub struct LayoutThreadData {
/// A list of images requests that need to be initiated.
pub pending_images: Vec<PendingImage>,
/// A queued response for the list of nodes at a given point.
pub nodes_from_point_response: Vec<UntrustedNodeAddress>,
}
pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutThreadData>>);
@ -144,33 +147,10 @@ impl LayoutRPC for LayoutRPCImpl {
}
}
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 = {
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress> {
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(&page_point,
&client_point,
&rw_data.stacking_context_scroll_offsets)
}
};
result
};
nodes_from_point_list.iter()
.rev()
.map(|metadata| metadata.node.to_untrusted_node_address())
.collect()
rw_data.nodes_from_point_response.clone()
}
fn node_geometry(&self) -> NodeGeometryResponse {

View file

@ -63,6 +63,7 @@ use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwne
use layout::flow_ref::FlowRef;
use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT};
use layout::layout_debug;
use layout::opaque_node::OpaqueNodeMethods;
use layout::parallel;
use layout::query::{LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request};
use layout::query::{process_margin_style_query, process_node_overflow_request, process_resolved_style_request};
@ -459,6 +460,7 @@ impl LayoutThread {
stacking_context_scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None),
pending_images: vec![],
nodes_from_point_response: vec![],
})),
error_reporter: CSSErrorReporter {
pipelineid: id,
@ -977,6 +979,9 @@ impl LayoutThread {
ReflowQueryType::HitTestQuery(..) => {
rw_data.hit_test_response = (None, false);
},
ReflowQueryType::NodesFromPoint(..) => {
rw_data.nodes_from_point_response = Vec::new();
},
ReflowQueryType::NodeGeometryQuery(_) => {
rw_data.client_rect_response = Rect::zero();
},
@ -1279,6 +1284,29 @@ impl LayoutThread {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.margin_style_response = process_margin_style_query(node);
},
ReflowQueryType::NodesFromPoint(page_point, client_point) => {
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 result = match rw_data.display_list {
None => panic!("Tried to hit test without a DisplayList"),
Some(ref display_list) => {
display_list.hit_test(&page_point,
&client_point,
&rw_data.stacking_context_scroll_offsets)
}
};
result
};
rw_data.nodes_from_point_response = nodes_from_point_list.iter()
.rev()
.map(|metadata| metadata.node.to_untrusted_node_address())
.collect()
},
ReflowQueryType::NoQuery => {}
}
}
@ -1585,7 +1613,8 @@ fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
/// or false if it only needs stacking-relative positions.
fn reflow_query_type_needs_display_list(query_type: &ReflowQueryType) -> bool {
match *query_type {
ReflowQueryType::HitTestQuery(..) | ReflowQueryType::TextIndexQuery(..) => true,
ReflowQueryType::HitTestQuery(..) | ReflowQueryType::TextIndexQuery(..) |
ReflowQueryType::NodesFromPoint(..) => true,
ReflowQueryType::ContentBoxQuery(_) | ReflowQueryType::ContentBoxesQuery(_) |
ReflowQueryType::NodeGeometryQuery(_) | ReflowQueryType::NodeScrollGeometryQuery(_) |
ReflowQueryType::NodeOverflowQuery(_) | ReflowQueryType::NodeScrollRootIdQuery(_) |

View file

@ -1877,7 +1877,13 @@ impl Document {
Point2D::new(client_point.x + self.window.PageXOffset() as f32,
client_point.y + self.window.PageYOffset() as f32);
self.window.layout().nodes_from_point(page_point, *client_point)
if !self.window.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodesFromPoint(page_point, *client_point),
ReflowReason::Query) {
return vec!();
};
self.window.layout().nodes_from_point_response()
}
}

View file

@ -1811,6 +1811,7 @@ fn debug_reflow_events(id: PipelineId, goal: &ReflowGoal, query_type: &ReflowQue
ReflowQueryType::ContentBoxQuery(_n) => "\tContentBoxQuery",
ReflowQueryType::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
ReflowQueryType::HitTestQuery(..) => "\tHitTestQuery",
ReflowQueryType::NodesFromPoint(..) => "\tNodesFromPoint",
ReflowQueryType::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
ReflowQueryType::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery",
ReflowQueryType::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery",

View file

@ -101,6 +101,7 @@ pub enum ReflowQueryType {
OffsetParentQuery(TrustedNodeAddress),
MarginStyleQuery(TrustedNodeAddress),
TextIndexQuery(TrustedNodeAddress, i32, i32),
NodesFromPoint(Point2D<f32>, Point2D<f32>),
}
/// Information needed for a reflow.

View file

@ -40,7 +40,8 @@ pub trait LayoutRPC {
fn margin_style(&self) -> MarginStyleResponse;
/// Requests the list of not-yet-loaded images that were encountered in the last reflow.
fn pending_images(&self) -> Vec<PendingImage>;
fn nodes_from_point(&self, page_point: Point2D<f32>, client_point: Point2D<f32>) -> Vec<UntrustedNodeAddress>;
/// Requests the list of nodes from the given point.
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>;
fn text_index(&self) -> TextIndexResponse;
}

View file

@ -12598,6 +12598,12 @@
{}
]
],
"mozilla/document_elementsFromPoint.html": [
[
"/_mozilla/mozilla/document_elementsFromPoint.html",
{}
]
],
"mozilla/document_getElementById.html": [
[
"/_mozilla/mozilla/document_getElementById.html",
@ -25137,6 +25143,10 @@
"12eef74e9ba1a62b0302df79633cab5c48f4977f",
"testharness"
],
"mozilla/document_elementsFromPoint.html": [
"00da2141b1143619351c6fbb66d677ca9002a4a8",
"testharness"
],
"mozilla/document_getElementById.html": [
"ae5270afde878c8fbe9b4a89c5c3f8ff609220dc",
"testharness"

View file

@ -0,0 +1,10 @@
<!doctype html>
<meta charset="utf-8">
<title>document.elementsFromPoint does not crash due to a missing display list</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(function() {
document.elementsFromPoint(10, 10);
}, "doesn't crash");
</script>