Properly handle scroll offsets in hit testing

Scroll roots are no longer nested containers holding items, so instead
we need to track the offsets of each, carefully handling fixed position
items and stacking contexts that create new reference frames.
Additionally, we remove the complexity of the pre-computed page scroll
offset, instead opting to send script scrolls to the layout task in
order to more quickly have a ScrollState there that matches the
script's idea of the scroll world.

Fixes #16405.
This commit is contained in:
Martin Robinson 2017-05-12 10:56:18 +02:00
parent 7ca393a960
commit 9fd2df5c09
8 changed files with 221 additions and 178 deletions

View file

@ -22,7 +22,7 @@ use net_traits::image::base::{Image, PixelFormat};
use profile_traits::time::{self, ProfilerCategory, profile}; use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::{AnimationState, AnimationTickType, ConstellationControlMsg}; use script_traits::{AnimationState, AnimationTickType, ConstellationControlMsg};
use script_traits::{ConstellationMsg, DevicePixel, LayoutControlMsg, LoadData, MouseButton}; use script_traits::{ConstellationMsg, DevicePixel, LayoutControlMsg, LoadData, MouseButton};
use script_traits::{MouseEventType, StackingContextScrollState}; use script_traits::{MouseEventType, ScrollState};
use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeData, WindowSizeType}; use script_traits::{TouchpadPressurePhase, TouchEventType, TouchId, WindowSizeData, WindowSizeType};
use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent}; use script_traits::CompositorEvent::{self, MouseMoveEvent, MouseButtonEvent, TouchEvent, TouchpadPressureEvent};
use servo_config::opts; use servo_config::opts;
@ -1368,29 +1368,26 @@ impl<Window: WindowMethods> IOCompositor<Window> {
} }
fn send_viewport_rects(&self) { fn send_viewport_rects(&self) {
let mut stacking_context_scroll_states_per_pipeline = HashMap::new(); let mut scroll_states_per_pipeline = HashMap::new();
for scroll_layer_state in self.webrender_api.get_scroll_node_state() { for scroll_layer_state in self.webrender_api.get_scroll_node_state() {
if scroll_layer_state.id.external_id().is_none() && if scroll_layer_state.id.external_id().is_none() &&
scroll_layer_state.id.is_root_scroll_node() { !scroll_layer_state.id.is_root_scroll_node() {
continue; continue;
} }
let stacking_context_scroll_state = StackingContextScrollState { let scroll_state = ScrollState {
scroll_root_id: scroll_layer_state.id, scroll_root_id: scroll_layer_state.id,
scroll_offset: scroll_layer_state.scroll_offset.to_untyped(), scroll_offset: scroll_layer_state.scroll_offset.to_untyped(),
}; };
stacking_context_scroll_states_per_pipeline scroll_states_per_pipeline.entry(scroll_layer_state.id.pipeline_id())
.entry(scroll_layer_state.id.pipeline_id())
.or_insert(vec![]) .or_insert(vec![])
.push(stacking_context_scroll_state); .push(scroll_state);
} }
for (pipeline_id, stacking_context_scroll_states) in for (pipeline_id, scroll_states) in scroll_states_per_pipeline {
stacking_context_scroll_states_per_pipeline {
if let Some(pipeline) = self.pipeline(pipeline_id.from_webrender()) { if let Some(pipeline) = self.pipeline(pipeline_id.from_webrender()) {
let msg = LayoutControlMsg::SetStackingContextScrollStates( let msg = LayoutControlMsg::SetScrollStates(scroll_states);
stacking_context_scroll_states);
let _ = pipeline.layout_chan.send(msg); let _ = pipeline.layout_chan.send(msg);
} }
} }

View file

@ -47,6 +47,74 @@ pub struct DisplayList {
pub list: Vec<DisplayItem>, pub list: Vec<DisplayItem>,
} }
struct ScrollOffsetLookup<'a> {
parents: &'a mut HashMap<ClipId, ClipId>,
calculated_total_offsets: ScrollOffsetMap,
raw_offsets: &'a ScrollOffsetMap,
}
impl<'a> ScrollOffsetLookup<'a> {
fn new(parents: &'a mut HashMap<ClipId, ClipId>,
raw_offsets: &'a ScrollOffsetMap)
-> ScrollOffsetLookup<'a> {
ScrollOffsetLookup {
parents: parents,
calculated_total_offsets: HashMap::new(),
raw_offsets: raw_offsets,
}
}
fn new_for_reference_frame(&mut self,
clip_id: ClipId,
transform: &Matrix4D<f32>,
point: &mut Point2D<Au>)
-> Option<ScrollOffsetLookup> {
// If a transform function causes the current transformation matrix of an object
// to be non-invertible, the object and its content do not get displayed.
let inv_transform = match transform.inverse() {
Some(transform) => transform,
None => return None,
};
let scroll_offset = self.full_offset_for_scroll_root(&clip_id);
*point = Point2D::new(point.x - Au::from_f32_px(scroll_offset.x),
point.y - Au::from_f32_px(scroll_offset.y));
let frac_point = inv_transform.transform_point(&Point2D::new(point.x.to_f32_px(),
point.y.to_f32_px()));
*point = Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y));
let mut sublookup = ScrollOffsetLookup {
parents: &mut self.parents,
calculated_total_offsets: HashMap::new(),
raw_offsets: self.raw_offsets,
};
sublookup.calculated_total_offsets.insert(clip_id, Point2D::zero());
Some(sublookup)
}
fn add_scroll_root(&mut self, scroll_root: &ScrollRoot) {
self.parents.insert(scroll_root.id, scroll_root.parent_id);
}
fn full_offset_for_scroll_root(&mut self, id: &ClipId) -> Point2D<f32> {
if let Some(offset) = self.calculated_total_offsets.get(id) {
return *offset;
}
let parent_offset = if !id.is_root_scroll_node() {
let parent_id = *self.parents.get(id).unwrap();
self.full_offset_for_scroll_root(&parent_id)
} else {
Point2D::zero()
};
let offset = parent_offset +
self.raw_offsets.get(id).cloned().unwrap_or_else(Point2D::zero);
self.calculated_total_offsets.insert(*id, offset);
offset
}
}
impl DisplayList { impl DisplayList {
/// Return the bounds of this display list based on the dimensions of the root /// Return the bounds of this display list based on the dimensions of the root
/// stacking context. /// stacking context.
@ -69,51 +137,36 @@ impl DisplayList {
self.text_index_contents(node, self.text_index_contents(node,
&mut traversal, &mut traversal,
client_point, client_point,
client_point, &mut ScrollOffsetLookup::new(&mut HashMap::new(), scroll_offsets),
scroll_offsets,
&mut result); &mut result);
result.pop() result.pop()
} }
pub fn text_index_contents<'a>(&self, fn text_index_contents<'a>(&self,
node: OpaqueNode, node: OpaqueNode,
traversal: &mut DisplayListTraversal<'a>, traversal: &mut DisplayListTraversal<'a>,
translated_point: &Point2D<Au>, point: &Point2D<Au>,
client_point: &Point2D<Au>, offset_lookup: &mut ScrollOffsetLookup,
scroll_offsets: &ScrollOffsetMap,
result: &mut Vec<usize>) { result: &mut Vec<usize>) {
while let Some(item) = traversal.next() { while let Some(item) = traversal.next() {
match item { match item {
&DisplayItem::PushStackingContext(ref stacking_context_item) => { &DisplayItem::PushStackingContext(ref context_item) => {
let mut point = *translated_point; self.text_index_stacking_context(&context_item.stacking_context,
DisplayList::translate_point(&stacking_context_item.stacking_context, item.base().scroll_root_id,
&mut point, node,
client_point);
self.text_index_contents(node,
traversal, traversal,
&point, point,
client_point, offset_lookup,
scroll_offsets,
result); result);
} }
&DisplayItem::DefineClip(ref item) => { &DisplayItem::DefineClip(ref item) => {
let mut point = *translated_point; offset_lookup.add_scroll_root(&item.scroll_root);
DisplayList::scroll_root(&item.scroll_root, }
&mut point,
scroll_offsets);
self.text_index_contents(node,
traversal,
&point,
client_point,
scroll_offsets,
result);
},
&DisplayItem::PopStackingContext(_) => return, &DisplayItem::PopStackingContext(_) => return,
&DisplayItem::Text(ref text) => { &DisplayItem::Text(ref text) => {
let base = item.base(); let base = item.base();
if base.metadata.node == node { if base.metadata.node == node {
let offset = *translated_point - text.baseline_origin; let offset = *point - text.baseline_origin;
let index = text.text_run.range_index_of_advance(&text.range, offset.x); let index = text.text_run.range_index_of_advance(&text.range, offset.x);
result.push(index); result.push(index);
} }
@ -123,56 +176,71 @@ impl DisplayList {
} }
} }
fn text_index_stacking_context<'a>(&self,
stacking_context: &StackingContext,
clip_id: ClipId,
node: OpaqueNode,
traversal: &mut DisplayListTraversal<'a>,
point: &Point2D<Au>,
offset_lookup: &mut ScrollOffsetLookup,
result: &mut Vec<usize>) {
let mut point = *point - stacking_context.bounds.origin;
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, Point2D::zero());
self.text_index_contents(node, 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.text_index_contents(node, traversal, &point, sublookup, result);
}
} else {
self.text_index_contents(node, traversal, &point, offset_lookup, result);
}
}
// Return all nodes containing the point of interest, bottommost first, and // Return all nodes containing the point of interest, bottommost first, and
// respecting the `pointer-events` CSS property. // respecting the `pointer-events` CSS property.
pub fn hit_test(&self, pub fn hit_test(&self,
translated_point: &Point2D<Au>, point: &Point2D<Au>,
client_point: &Point2D<Au>,
scroll_offsets: &ScrollOffsetMap) scroll_offsets: &ScrollOffsetMap)
-> Vec<DisplayItemMetadata> { -> Vec<DisplayItemMetadata> {
let mut result = Vec::new(); let mut result = Vec::new();
let mut traversal = DisplayListTraversal::new(self); let mut traversal = DisplayListTraversal::new(self);
self.hit_test_contents(&mut traversal, self.hit_test_contents(&mut traversal,
translated_point, point,
client_point, &mut ScrollOffsetLookup::new(&mut HashMap::new(), scroll_offsets),
scroll_offsets,
&mut result); &mut result);
result result
} }
pub fn hit_test_contents<'a>(&self, fn hit_test_contents<'a>(&self,
traversal: &mut DisplayListTraversal<'a>, traversal: &mut DisplayListTraversal<'a>,
translated_point: &Point2D<Au>, point: &Point2D<Au>,
client_point: &Point2D<Au>, offset_lookup: &mut ScrollOffsetLookup,
scroll_offsets: &ScrollOffsetMap,
result: &mut Vec<DisplayItemMetadata>) { result: &mut Vec<DisplayItemMetadata>) {
while let Some(item) = traversal.next() { while let Some(item) = traversal.next() {
match item { match item {
&DisplayItem::PushStackingContext(ref stacking_context_item) => { &DisplayItem::PushStackingContext(ref context_item) => {
let mut point = *translated_point; self.hit_test_stacking_context(&context_item.stacking_context,
DisplayList::translate_point(&stacking_context_item.stacking_context, item.base().scroll_root_id,
&mut point, traversal,
client_point); point,
self.hit_test_contents(traversal, offset_lookup,
&point,
client_point,
scroll_offsets,
result);
}
&DisplayItem::DefineClip(ref item) => {
let mut point = *translated_point;
DisplayList::scroll_root(&item.scroll_root,
&mut point,
scroll_offsets);
self.hit_test_contents(traversal,
&point,
client_point,
scroll_offsets,
result); result);
} }
&DisplayItem::PopStackingContext(_) => return, &DisplayItem::PopStackingContext(_) => return,
&DisplayItem::DefineClip(ref item) => {
offset_lookup.add_scroll_root(&item.scroll_root);
}
_ => { _ => {
if let Some(meta) = item.hit_test(*translated_point) { if let Some(meta) = item.hit_test(*point, offset_lookup) {
result.push(meta); result.push(meta);
} }
} }
@ -180,52 +248,33 @@ impl DisplayList {
} }
} }
#[inline] fn hit_test_stacking_context<'a>(&self,
fn translate_point<'a>(stacking_context: &StackingContext, stacking_context: &StackingContext,
translated_point: &mut Point2D<Au>, clip_id: ClipId,
client_point: &Point2D<Au>) { traversal: &mut DisplayListTraversal<'a>,
// Convert the parent translated point into stacking context local transform space if the point: &Point2D<Au>,
// stacking context isn't fixed. If it's fixed, we need to use the client point anyway. offset_lookup: &mut ScrollOffsetLookup,
result: &mut Vec<DisplayItemMetadata>) {
debug_assert!(stacking_context.context_type == StackingContextType::Real); debug_assert!(stacking_context.context_type == StackingContextType::Real);
let is_fixed = stacking_context.scroll_policy == ScrollPolicy::Fixed;
*translated_point = if is_fixed { let mut point = *point - stacking_context.bounds.origin;
*client_point 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, Point2D::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 { } else {
let point = *translated_point - stacking_context.bounds.origin; self.hit_test_contents(traversal, &point, offset_lookup, result);
match stacking_context.transform {
Some(transform) => {
let inv_transform = match transform.inverse() {
Some(transform) => transform,
None => {
// If a transform function causes the current transformation matrix of an object
// to be non-invertible, the object and its content do not get displayed.
return;
}
};
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))
}
None => {
point
}
}
};
}
#[inline]
fn scroll_root<'a>(scroll_root: &ScrollRoot,
translated_point: &mut Point2D<Au>,
scroll_offsets: &ScrollOffsetMap) {
// Adjust the translated point to account for the scroll offset if necessary.
//
// 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 let Some(scroll_offset) = scroll_offsets.get(&scroll_root.id) {
translated_point.x -= Au::from_f32_px(scroll_offset.x);
translated_point.y -= Au::from_f32_px(scroll_offset.y);
} }
} }
@ -1207,11 +1256,18 @@ impl DisplayItem {
println!("{}+ {:?}", indent, self); println!("{}+ {:?}", indent, self);
} }
fn hit_test(&self, point: Point2D<Au>) -> Option<DisplayItemMetadata> { 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 // TODO(pcwalton): Use a precise algorithm here. This will allow us to properly hit
// test elements with `border-radius`, for example. // test elements with `border-radius`, for example.
let base_item = self.base(); let base_item = self.base();
let scroll_offset = offset_lookup.full_offset_for_scroll_root(&base_item.scroll_root_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.clip.might_intersect_point(&point) { if !base_item.clip.might_intersect_point(&point) {
// Clipped out. // Clipped out.
return None; return None;

View file

@ -81,8 +81,8 @@ pub struct LayoutThreadData {
/// A queued response for the offset parent/rect of a node. /// A queued response for the offset parent/rect of a node.
pub margin_style_response: MarginStyleResponse, pub margin_style_response: MarginStyleResponse,
/// Scroll offsets of stacking contexts. This will only be populated if WebRender is in use. /// Scroll offsets of scrolling regions.
pub stacking_context_scroll_offsets: ScrollOffsetMap, pub scroll_offsets: ScrollOffsetMap,
/// Index in a text fragment. We need this do determine the insertion point. /// Index in a text fragment. We need this do determine the insertion point.
pub text_index_response: TextIndexResponse, pub text_index_response: TextIndexResponse,

View file

@ -88,7 +88,7 @@ use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowR
use script_layout_interface::rpc::TextIndexResponse; use script_layout_interface::rpc::TextIndexResponse;
use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::wrapper_traits::LayoutNode;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::{StackingContextScrollState, UntrustedNodeAddress}; use script_traits::{ScrollState, UntrustedNodeAddress};
use selectors::Element; use selectors::Element;
use servo_config::opts; use servo_config::opts;
use servo_config::prefs::PREFS; use servo_config::prefs::PREFS;
@ -506,7 +506,7 @@ impl LayoutThread {
resolved_style_response: String::new(), resolved_style_response: String::new(),
offset_parent_response: OffsetParentResponse::empty(), offset_parent_response: OffsetParentResponse::empty(),
margin_style_response: MarginStyleResponse::empty(), margin_style_response: MarginStyleResponse::empty(),
stacking_context_scroll_offsets: HashMap::new(), scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None), text_index_response: TextIndexResponse(None),
nodes_from_point_response: vec![], nodes_from_point_response: vec![],
})), })),
@ -600,9 +600,8 @@ impl LayoutThread {
}; };
match request { match request {
Request::FromPipeline(LayoutControlMsg::SetStackingContextScrollStates( Request::FromPipeline(LayoutControlMsg::SetScrollStates(new_scroll_states)) => {
new_scroll_states)) => { self.handle_request_helper(Msg::SetScrollStates(new_scroll_states),
self.handle_request_helper(Msg::SetStackingContextScrollStates(new_scroll_states),
possibly_locked_rw_data) possibly_locked_rw_data)
}, },
Request::FromPipeline(LayoutControlMsg::TickAnimations) => { Request::FromPipeline(LayoutControlMsg::TickAnimations) => {
@ -653,9 +652,12 @@ impl LayoutThread {
|| self.handle_reflow(&mut data, possibly_locked_rw_data)); || self.handle_reflow(&mut data, possibly_locked_rw_data));
}, },
Msg::TickAnimations => self.tick_all_animations(possibly_locked_rw_data), Msg::TickAnimations => self.tick_all_animations(possibly_locked_rw_data),
Msg::SetStackingContextScrollStates(new_scroll_states) => { Msg::SetScrollStates(new_scroll_states) => {
self.set_stacking_context_scroll_states(new_scroll_states, self.set_scroll_states(new_scroll_states, possibly_locked_rw_data);
possibly_locked_rw_data); }
Msg::UpdateScrollStateFromScript(state) => {
let mut rw_data = possibly_locked_rw_data.lock();
rw_data.scroll_offsets.insert(state.scroll_root_id, state.scroll_offset);
} }
Msg::ReapStyleAndLayoutData(dead_data) => { Msg::ReapStyleAndLayoutData(dead_data) => {
unsafe { unsafe {
@ -1306,19 +1308,13 @@ impl LayoutThread {
let node = unsafe { ServoLayoutNode::new(&node) }; let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.content_boxes_response = process_content_boxes_request(node, root_flow); rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
}, },
ReflowQueryType::HitTestQuery(translated_point, client_point, update_cursor) => { ReflowQueryType::HitTestQuery(client_point, update_cursor) => {
let mut translated_point = Point2D::new(Au::from_f32_px(translated_point.x), let point = Point2D::new(Au::from_f32_px(client_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)); Au::from_f32_px(client_point.y));
let result = rw_data.display_list let result = rw_data.display_list
.as_ref() .as_ref()
.expect("Tried to hit test with no display list") .expect("Tried to hit test with no display list")
.hit_test(&mut translated_point, .hit_test(&point, &rw_data.scroll_offsets);
&client_point,
&rw_data.stacking_context_scroll_offsets);
rw_data.hit_test_response = (result.last().cloned(), update_cursor); rw_data.hit_test_response = (result.last().cloned(), update_cursor);
}, },
ReflowQueryType::TextIndexQuery(node, mouse_x, mouse_y) => { ReflowQueryType::TextIndexQuery(node, mouse_x, mouse_y) => {
@ -1332,7 +1328,7 @@ impl LayoutThread {
.expect("Tried to hit test with no display list") .expect("Tried to hit test with no display list")
.text_index(opaque_node, .text_index(opaque_node,
&client_point, &client_point,
&rw_data.stacking_context_scroll_offsets)); &rw_data.scroll_offsets));
}, },
ReflowQueryType::NodeGeometryQuery(node) => { ReflowQueryType::NodeGeometryQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) }; let node = unsafe { ServoLayoutNode::new(&node) };
@ -1368,18 +1364,14 @@ impl LayoutThread {
let node = unsafe { ServoLayoutNode::new(&node) }; let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.margin_style_response = process_margin_style_query(node); rw_data.margin_style_response = process_margin_style_query(node);
}, },
ReflowQueryType::NodesFromPoint(page_point, client_point) => { ReflowQueryType::NodesFromPoint(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), let client_point = Point2D::new(Au::from_f32_px(client_point.x),
Au::from_f32_px(client_point.y)); Au::from_f32_px(client_point.y));
let nodes_from_point_list = { let nodes_from_point_list = {
let result = match rw_data.display_list { let result = match rw_data.display_list {
None => panic!("Tried to hit test without a DisplayList"), None => panic!("Tried to hit test without a DisplayList"),
Some(ref display_list) => { Some(ref display_list) => {
display_list.hit_test(&page_point, display_list.hit_test(&client_point, &rw_data.scroll_offsets)
&client_point,
&rw_data.stacking_context_scroll_offsets)
} }
}; };
@ -1395,9 +1387,8 @@ impl LayoutThread {
} }
} }
fn set_stacking_context_scroll_states<'a, 'b>( fn set_scroll_states<'a, 'b>(&mut self,
&mut self, new_scroll_states: Vec<ScrollState>,
new_scroll_states: Vec<StackingContextScrollState>,
possibly_locked_rw_data: &mut RwData<'a, 'b>) { possibly_locked_rw_data: &mut RwData<'a, 'b>) {
let mut rw_data = possibly_locked_rw_data.lock(); let mut rw_data = possibly_locked_rw_data.lock();
let mut script_scroll_states = vec![]; let mut script_scroll_states = vec![];
@ -1416,7 +1407,7 @@ impl LayoutThread {
} }
let _ = self.script_chan let _ = self.script_chan
.send(ConstellationControlMsg::SetScrollState(self.id, script_scroll_states)); .send(ConstellationControlMsg::SetScrollState(self.id, script_scroll_states));
rw_data.stacking_context_scroll_offsets = layout_scroll_states rw_data.scroll_offsets = layout_scroll_states
} }
fn tick_all_animations<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) { fn tick_all_animations<'a, 'b>(&mut self, possibly_locked_rw_data: &mut RwData<'a, 'b>) {

View file

@ -1955,12 +1955,8 @@ impl Document {
} }
pub fn nodes_from_point(&self, client_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> { pub fn nodes_from_point(&self, client_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
let page_point =
Point2D::new(client_point.x + self.window.PageXOffset() as f32,
client_point.y + self.window.PageYOffset() as f32);
if !self.window.reflow(ReflowGoal::ForScriptQuery, if !self.window.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodesFromPoint(page_point, *client_point), ReflowQueryType::NodesFromPoint(*client_point),
ReflowReason::Query) { ReflowReason::Query) {
return vec!(); return vec!();
}; };

View file

@ -75,11 +75,11 @@ use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, Lay
use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse}; use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse};
use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse}; use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory};
use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, RunnableWrapper}; use script_thread::{ImageCacheMsg, MainThreadScriptChan, MainThreadScriptMsg, Runnable};
use script_thread::{SendableMainThreadScriptChan, ImageCacheMsg, ScriptThread}; use script_thread::{RunnableWrapper, ScriptThread, SendableMainThreadScriptChan};
use script_traits::{ConstellationControlMsg, LoadData, MozBrowserEvent, UntrustedNodeAddress}; use script_traits::{ConstellationControlMsg, DocumentState, LoadData, MozBrowserEvent};
use script_traits::{DocumentState, TimerEvent, TimerEventId}; use script_traits::{ScriptMsg as ConstellationMsg, ScrollState, TimerEvent, TimerEventId};
use script_traits::{ScriptMsg as ConstellationMsg, TimerSchedulerMsg, WindowSizeData, WindowSizeType}; use script_traits::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use servo_atoms::Atom; use servo_atoms::Atom;
use servo_config::opts; use servo_config::opts;
@ -1111,6 +1111,11 @@ impl Window {
ScrollBehavior::Smooth => true ScrollBehavior::Smooth => true
}; };
self.layout_chan.send(Msg::UpdateScrollStateFromScript(ScrollState {
scroll_root_id: scroll_root_id,
scroll_offset: Point2D::new(-x, -y),
})).unwrap();
// TODO (farodin91): Raise an event to stop the current_viewport // TODO (farodin91): Raise an event to stop the current_viewport
self.update_viewport_for_scroll(x, y); self.update_viewport_for_scroll(x, y);
@ -1372,14 +1377,8 @@ impl Window {
client_point: Point2D<f32>, client_point: Point2D<f32>,
update_cursor: bool) update_cursor: bool)
-> Option<UntrustedNodeAddress> { -> 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, if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::HitTestQuery(translated_point, ReflowQueryType::HitTestQuery(client_point, update_cursor),
client_point,
update_cursor),
ReflowReason::Query) { ReflowReason::Query) {
return None return None
} }

View file

@ -12,8 +12,8 @@ use msg::constellation_msg::PipelineId;
use net_traits::image_cache::ImageCache; use net_traits::image_cache::ImageCache;
use profile_traits::mem::ReportsChan; use profile_traits::mem::ReportsChan;
use rpc::LayoutRPC; use rpc::LayoutRPC;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, UntrustedNodeAddress}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState, WindowSizeData}; use script_traits::{ScrollState, UntrustedNodeAddress, WindowSizeData};
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
@ -79,7 +79,11 @@ pub enum Msg {
SetFinalUrl(ServoUrl), SetFinalUrl(ServoUrl),
/// Tells layout about the new scrolling offsets of each scrollable stacking context. /// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetStackingContextScrollStates(Vec<StackingContextScrollState>), SetScrollStates(Vec<ScrollState>),
/// Tells layout about a single new scrolling offset from the script. The rest will
/// remain untouched and layout won't forward this back to script.
UpdateScrollStateFromScript(ScrollState),
} }
@ -90,7 +94,7 @@ pub enum ReflowQueryType {
ContentBoxQuery(TrustedNodeAddress), ContentBoxQuery(TrustedNodeAddress),
ContentBoxesQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress),
NodeOverflowQuery(TrustedNodeAddress), NodeOverflowQuery(TrustedNodeAddress),
HitTestQuery(Point2D<f32>, Point2D<f32>, bool), HitTestQuery(Point2D<f32>, bool),
NodeScrollRootIdQuery(TrustedNodeAddress), NodeScrollRootIdQuery(TrustedNodeAddress),
NodeGeometryQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress),
NodeScrollGeometryQuery(TrustedNodeAddress), NodeScrollGeometryQuery(TrustedNodeAddress),
@ -98,7 +102,7 @@ pub enum ReflowQueryType {
OffsetParentQuery(TrustedNodeAddress), OffsetParentQuery(TrustedNodeAddress),
MarginStyleQuery(TrustedNodeAddress), MarginStyleQuery(TrustedNodeAddress),
TextIndexQuery(TrustedNodeAddress, i32, i32), TextIndexQuery(TrustedNodeAddress, i32, i32),
NodesFromPoint(Point2D<f32>, Point2D<f32>), NodesFromPoint(Point2D<f32>),
} }
/// Information needed for a reflow. /// Information needed for a reflow.

View file

@ -122,7 +122,7 @@ pub enum LayoutControlMsg {
/// Asks layout to run another step in its animation. /// Asks layout to run another step in its animation.
TickAnimations, TickAnimations,
/// Tells layout about the new scrolling offsets of each scrollable stacking context. /// Tells layout about the new scrolling offsets of each scrollable stacking context.
SetStackingContextScrollStates(Vec<StackingContextScrollState>), SetScrollStates(Vec<ScrollState>),
/// Requests the current load state of Web fonts. `true` is returned if fonts are still loading /// Requests the current load state of Web fonts. `true` is returned if fonts are still loading
/// and `false` is returned if all fonts have loaded. /// and `false` is returned if all fonts have loaded.
GetWebFontLoadState(IpcSender<bool>), GetWebFontLoadState(IpcSender<bool>),
@ -673,7 +673,7 @@ pub enum AnimationTickType {
/// The scroll state of a stacking context. /// The scroll state of a stacking context.
#[derive(Copy, Clone, Debug, Deserialize, Serialize)] #[derive(Copy, Clone, Debug, Deserialize, Serialize)]
pub struct StackingContextScrollState { pub struct ScrollState {
/// The ID of the scroll root. /// The ID of the scroll root.
pub scroll_root_id: ClipId, pub scroll_root_id: ClipId,
/// The scrolling offset of this stacking context. /// The scrolling offset of this stacking context.