mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Extract layout query code into query.rs
This commit is contained in:
parent
eecd975209
commit
f114304a3a
3 changed files with 320 additions and 294 deletions
|
@ -20,6 +20,8 @@ use incremental::{LayoutDamageComputation, REFLOW, REFLOW_ENTIRE_DOCUMENT, REPAI
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use opaque_node::OpaqueNodeMethods;
|
use opaque_node::OpaqueNodeMethods;
|
||||||
use parallel::{self, WorkQueueData};
|
use parallel::{self, WorkQueueData};
|
||||||
|
use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request, MarginPadding, Side};
|
||||||
|
use query::{MarginRetrievingFragmentBorderBoxIterator, PositionProperty, PositionRetrievingFragmentBorderBoxIterator};
|
||||||
use sequential;
|
use sequential;
|
||||||
use wrapper::LayoutNode;
|
use wrapper::LayoutNode;
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ use euclid::rect::Rect;
|
||||||
use euclid::scale_factor::ScaleFactor;
|
use euclid::scale_factor::ScaleFactor;
|
||||||
use euclid::size::Size2D;
|
use euclid::size::Size2D;
|
||||||
use gfx_traits::color;
|
use gfx_traits::color;
|
||||||
use gfx::display_list::{ClippingRegion, DisplayItemMetadata, DisplayList, OpaqueNode};
|
use gfx::display_list::{ClippingRegion, DisplayList, OpaqueNode};
|
||||||
use gfx::display_list::StackingContext;
|
use gfx::display_list::StackingContext;
|
||||||
use gfx::font_cache_task::FontCacheTask;
|
use gfx::font_cache_task::FontCacheTask;
|
||||||
use gfx::paint_task::{LayoutToPaintMsg, PaintLayer};
|
use gfx::paint_task::{LayoutToPaintMsg, PaintLayer};
|
||||||
|
@ -52,10 +54,10 @@ use net_traits::{load_bytes_iter, PendingAsyncLoad};
|
||||||
use net_traits::image_cache_task::{ImageCacheTask, ImageCacheResult, ImageCacheChan};
|
use net_traits::image_cache_task::{ImageCacheTask, ImageCacheResult, ImageCacheChan};
|
||||||
use script::dom::bindings::js::LayoutJS;
|
use script::dom::bindings::js::LayoutJS;
|
||||||
use script::dom::node::{LayoutData, Node};
|
use script::dom::node::{LayoutData, Node};
|
||||||
use script::layout_interface::{Animation, ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
|
use script::layout_interface::Animation;
|
||||||
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, OffsetParentResponse};
|
use script::layout_interface::{LayoutChan, LayoutRPC, OffsetParentResponse};
|
||||||
use script::layout_interface::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType};
|
use script::layout_interface::{NewLayoutTaskInfo, Msg, Reflow, ReflowGoal, ReflowQueryType};
|
||||||
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
|
use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
|
||||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel};
|
use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel};
|
||||||
use script_traits::{ScriptControlChan, StylesheetLoadResponder};
|
use script_traits::{ScriptControlChan, StylesheetLoadResponder};
|
||||||
use selectors::parser::PseudoElement;
|
use selectors::parser::PseudoElement;
|
||||||
|
@ -76,10 +78,9 @@ use style::properties::longhands::{display, position};
|
||||||
use style::selector_matching::Stylist;
|
use style::selector_matching::Stylist;
|
||||||
use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt};
|
use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::cursor::Cursor;
|
|
||||||
use util::geometry::{Au, MAX_RECT, ZERO_POINT};
|
use util::geometry::{Au, MAX_RECT, ZERO_POINT};
|
||||||
use util::ipc::OptionalIpcSender;
|
use util::ipc::OptionalIpcSender;
|
||||||
use util::logical_geometry::{LogicalPoint, WritingMode};
|
use util::logical_geometry::LogicalPoint;
|
||||||
use util::mem::HeapSizeOf;
|
use util::mem::HeapSizeOf;
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::task::spawn_named_with_send_on_failure;
|
use util::task::spawn_named_with_send_on_failure;
|
||||||
|
@ -284,7 +285,7 @@ impl LayoutTaskFactory for LayoutTask {
|
||||||
/// The `LayoutTask` `rw_data` lock must remain locked until the first reflow,
|
/// The `LayoutTask` `rw_data` lock must remain locked until the first reflow,
|
||||||
/// as RPC calls don't make sense until then. Use this in combination with
|
/// as RPC calls don't make sense until then. Use this in combination with
|
||||||
/// `LayoutTask::lock_rw_data` and `LayoutTask::return_rw_data`.
|
/// `LayoutTask::lock_rw_data` and `LayoutTask::return_rw_data`.
|
||||||
enum RWGuard<'a> {
|
pub enum RWGuard<'a> {
|
||||||
/// If the lock was previously held, from when the task started.
|
/// If the lock was previously held, from when the task started.
|
||||||
Held(MutexGuard<'a, LayoutTaskData>),
|
Held(MutexGuard<'a, LayoutTaskData>),
|
||||||
/// If the lock was just used, and has been returned since there has been
|
/// If the lock was just used, and has been returned since there has been
|
||||||
|
@ -846,33 +847,6 @@ impl LayoutTask {
|
||||||
fn verify_flow_tree(&self, _: &mut FlowRef) {
|
fn verify_flow_tree(&self, _: &mut FlowRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_content_box_request<'a>(&'a self,
|
|
||||||
requested_node: TrustedNodeAddress,
|
|
||||||
layout_root: &mut FlowRef,
|
|
||||||
rw_data: &mut RWGuard<'a>) {
|
|
||||||
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
|
|
||||||
// stuff. So the position is wrong in most cases.
|
|
||||||
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
|
|
||||||
let mut iterator = UnioningFragmentBorderBoxIterator::new(requested_node);
|
|
||||||
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
|
|
||||||
rw_data.content_box_response = match iterator.rect {
|
|
||||||
Some(rect) => rect,
|
|
||||||
None => Rect::zero()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_content_boxes_request<'a>(&'a self,
|
|
||||||
requested_node: TrustedNodeAddress,
|
|
||||||
layout_root: &mut FlowRef,
|
|
||||||
rw_data: &mut RWGuard<'a>) {
|
|
||||||
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
|
|
||||||
// stuff. So the position is wrong in most cases.
|
|
||||||
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
|
|
||||||
let mut iterator = CollectingFragmentBorderBoxIterator::new(requested_node);
|
|
||||||
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
|
|
||||||
rw_data.content_boxes_response = iterator.rects;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process_node_geometry_request<'a>(&'a self,
|
fn process_node_geometry_request<'a>(&'a self,
|
||||||
requested_node: TrustedNodeAddress,
|
requested_node: TrustedNodeAddress,
|
||||||
layout_root: &mut FlowRef,
|
layout_root: &mut FlowRef,
|
||||||
|
@ -1218,9 +1192,9 @@ impl LayoutTask {
|
||||||
let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone();
|
let mut root_flow = (*rw_data.root_flow.as_ref().unwrap()).clone();
|
||||||
match data.query_type {
|
match data.query_type {
|
||||||
ReflowQueryType::ContentBoxQuery(node) =>
|
ReflowQueryType::ContentBoxQuery(node) =>
|
||||||
self.process_content_box_request(node, &mut root_flow, &mut rw_data),
|
process_content_box_request(node, &mut root_flow, &mut rw_data),
|
||||||
ReflowQueryType::ContentBoxesQuery(node) =>
|
ReflowQueryType::ContentBoxesQuery(node) =>
|
||||||
self.process_content_boxes_request(node, &mut root_flow, &mut rw_data),
|
process_content_boxes_request(node, &mut root_flow, &mut rw_data),
|
||||||
ReflowQueryType::NodeGeometryQuery(node) =>
|
ReflowQueryType::NodeGeometryQuery(node) =>
|
||||||
self.process_node_geometry_request(node, &mut root_flow, &mut rw_data),
|
self.process_node_geometry_request(node, &mut root_flow, &mut rw_data),
|
||||||
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) =>
|
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) =>
|
||||||
|
@ -1452,162 +1426,6 @@ impl LayoutTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LayoutRPCImpl(Arc<Mutex<LayoutTaskData>>);
|
|
||||||
|
|
||||||
impl LayoutRPC for LayoutRPCImpl {
|
|
||||||
// The neat thing here is that in order to answer the following two queries we only
|
|
||||||
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
|
|
||||||
fn content_box(&self) -> ContentBoxResponse {
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
ContentBoxResponse(rw_data.content_box_response)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
|
|
||||||
fn content_boxes(&self) -> ContentBoxesResponse {
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
ContentBoxesResponse(rw_data.content_boxes_response.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn node_geometry(&self) -> NodeGeometryResponse {
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
NodeGeometryResponse {
|
|
||||||
client_rect: rw_data.client_rect_response
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the resolved value for a CSS style property.
|
|
||||||
fn resolved_style(&self) -> ResolvedStyleResponse {
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
ResolvedStyleResponse(rw_data.resolved_style_response.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Requests the node containing the point of interest.
|
|
||||||
fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> {
|
|
||||||
let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
|
|
||||||
let resp = {
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
match rw_data.stacking_context {
|
|
||||||
None => panic!("no root stacking context!"),
|
|
||||||
Some(ref stacking_context) => {
|
|
||||||
let mut result = Vec::new();
|
|
||||||
stacking_context.hit_test(point, &mut result, true);
|
|
||||||
if !result.is_empty() {
|
|
||||||
Some(HitTestResponse(result[0].node.to_untrusted_node_address()))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if resp.is_some() {
|
|
||||||
return Ok(resp.unwrap());
|
|
||||||
}
|
|
||||||
Err(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>)
|
|
||||||
-> Result<MouseOverResponse, ()> {
|
|
||||||
let mut mouse_over_list: Vec<DisplayItemMetadata> = vec!();
|
|
||||||
let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
|
|
||||||
{
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
match rw_data.stacking_context {
|
|
||||||
None => panic!("no root stacking context!"),
|
|
||||||
Some(ref stacking_context) => {
|
|
||||||
stacking_context.hit_test(point, &mut mouse_over_list, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the new cursor.
|
|
||||||
let cursor = if !mouse_over_list.is_empty() {
|
|
||||||
mouse_over_list[0].pointing.unwrap()
|
|
||||||
} else {
|
|
||||||
Cursor::DefaultCursor
|
|
||||||
};
|
|
||||||
let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan;
|
|
||||||
constellation_chan.send(ConstellationMsg::SetCursor(cursor)).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if mouse_over_list.is_empty() {
|
|
||||||
Err(())
|
|
||||||
} else {
|
|
||||||
let response_list =
|
|
||||||
mouse_over_list.iter()
|
|
||||||
.map(|metadata| metadata.node.to_untrusted_node_address())
|
|
||||||
.collect();
|
|
||||||
Ok(MouseOverResponse(response_list))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn offset_parent(&self) -> OffsetParentResponse {
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock().unwrap();
|
|
||||||
rw_data.offset_parent_response.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UnioningFragmentBorderBoxIterator {
|
|
||||||
node_address: OpaqueNode,
|
|
||||||
rect: Option<Rect<Au>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnioningFragmentBorderBoxIterator {
|
|
||||||
fn new(node_address: OpaqueNode) -> UnioningFragmentBorderBoxIterator {
|
|
||||||
UnioningFragmentBorderBoxIterator {
|
|
||||||
node_address: node_address,
|
|
||||||
rect: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for UnioningFragmentBorderBoxIterator {
|
|
||||||
fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) {
|
|
||||||
self.rect = match self.rect {
|
|
||||||
Some(rect) => {
|
|
||||||
Some(rect.union(border_box))
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
Some(*border_box)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
|
||||||
fragment.contains_node(self.node_address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CollectingFragmentBorderBoxIterator {
|
|
||||||
node_address: OpaqueNode,
|
|
||||||
rects: Vec<Rect<Au>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CollectingFragmentBorderBoxIterator {
|
|
||||||
fn new(node_address: OpaqueNode) -> CollectingFragmentBorderBoxIterator {
|
|
||||||
CollectingFragmentBorderBoxIterator {
|
|
||||||
node_address: node_address,
|
|
||||||
rects: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for CollectingFragmentBorderBoxIterator {
|
|
||||||
fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) {
|
|
||||||
self.rects.push(*border_box);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
|
||||||
fragment.contains_node(self.node_address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FragmentLocatingFragmentIterator {
|
struct FragmentLocatingFragmentIterator {
|
||||||
node_address: OpaqueNode,
|
node_address: OpaqueNode,
|
||||||
client_rect: Rect<i32>,
|
client_rect: Rect<i32>,
|
||||||
|
@ -1667,108 +1485,6 @@ impl FragmentBorderBoxIterator for FragmentLocatingFragmentIterator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Side {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Bottom,
|
|
||||||
Top
|
|
||||||
}
|
|
||||||
|
|
||||||
enum MarginPadding {
|
|
||||||
Margin,
|
|
||||||
Padding
|
|
||||||
}
|
|
||||||
|
|
||||||
enum PositionProperty {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Top,
|
|
||||||
Bottom,
|
|
||||||
Width,
|
|
||||||
Height,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PositionRetrievingFragmentBorderBoxIterator {
|
|
||||||
node_address: OpaqueNode,
|
|
||||||
result: Option<Au>,
|
|
||||||
position: Point2D<Au>,
|
|
||||||
property: PositionProperty,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PositionRetrievingFragmentBorderBoxIterator {
|
|
||||||
fn new(node_address: OpaqueNode,
|
|
||||||
property: PositionProperty,
|
|
||||||
position: Point2D<Au>) -> PositionRetrievingFragmentBorderBoxIterator {
|
|
||||||
PositionRetrievingFragmentBorderBoxIterator {
|
|
||||||
node_address: node_address,
|
|
||||||
position: position,
|
|
||||||
property: property,
|
|
||||||
result: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for PositionRetrievingFragmentBorderBoxIterator {
|
|
||||||
fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) {
|
|
||||||
self.result =
|
|
||||||
Some(match self.property {
|
|
||||||
PositionProperty::Left => self.position.x,
|
|
||||||
PositionProperty::Top => self.position.y,
|
|
||||||
PositionProperty::Width => border_box.size.width,
|
|
||||||
PositionProperty::Height => border_box.size.height,
|
|
||||||
// TODO: the following 2 calculations are completely wrong.
|
|
||||||
// They should return the difference between the parent's and this
|
|
||||||
// fragment's border boxes.
|
|
||||||
PositionProperty::Right => border_box.max_x() + self.position.x,
|
|
||||||
PositionProperty::Bottom => border_box.max_y() + self.position.y,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
|
||||||
fragment.contains_node(self.node_address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MarginRetrievingFragmentBorderBoxIterator {
|
|
||||||
node_address: OpaqueNode,
|
|
||||||
result: Option<Au>,
|
|
||||||
writing_mode: WritingMode,
|
|
||||||
margin_padding: MarginPadding,
|
|
||||||
side: Side,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MarginRetrievingFragmentBorderBoxIterator {
|
|
||||||
fn new(node_address: OpaqueNode, side: Side, margin_padding:
|
|
||||||
MarginPadding, writing_mode: WritingMode) -> MarginRetrievingFragmentBorderBoxIterator {
|
|
||||||
MarginRetrievingFragmentBorderBoxIterator {
|
|
||||||
node_address: node_address,
|
|
||||||
side: side,
|
|
||||||
margin_padding: margin_padding,
|
|
||||||
result: None,
|
|
||||||
writing_mode: writing_mode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FragmentBorderBoxIterator for MarginRetrievingFragmentBorderBoxIterator {
|
|
||||||
fn process(&mut self, fragment: &Fragment, _: i32, _: &Rect<Au>) {
|
|
||||||
let rect = match self.margin_padding {
|
|
||||||
MarginPadding::Margin => &fragment.margin,
|
|
||||||
MarginPadding::Padding => &fragment.border_padding
|
|
||||||
};
|
|
||||||
self.result = Some(match self.side {
|
|
||||||
Side::Left => rect.left(self.writing_mode),
|
|
||||||
Side::Right => rect.right(self.writing_mode),
|
|
||||||
Side::Bottom => rect.bottom(self.writing_mode),
|
|
||||||
Side::Top => rect.top(self.writing_mode)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
|
||||||
fragment.contains_node(self.node_address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
|
// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
|
||||||
impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
|
impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
|
||||||
fn process(&mut self, fragment: &Fragment, level: i32, border_box: &Rect<Au>) {
|
fn process(&mut self, fragment: &Fragment, level: i32, border_box: &Rect<Au>) {
|
||||||
|
|
|
@ -85,6 +85,7 @@ pub mod model;
|
||||||
pub mod multicol;
|
pub mod multicol;
|
||||||
pub mod opaque_node;
|
pub mod opaque_node;
|
||||||
pub mod parallel;
|
pub mod parallel;
|
||||||
|
pub mod query;
|
||||||
pub mod sequential;
|
pub mod sequential;
|
||||||
pub mod table_wrapper;
|
pub mod table_wrapper;
|
||||||
pub mod table;
|
pub mod table;
|
||||||
|
|
309
components/layout/query.rs
Normal file
309
components/layout/query.rs
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
//! Utilities for querying the layout, as needed by the layout task.
|
||||||
|
|
||||||
|
use layout_task::{LayoutTaskData, RWGuard};
|
||||||
|
|
||||||
|
use euclid::point::Point2D;
|
||||||
|
use euclid::rect::Rect;
|
||||||
|
use flow_ref::FlowRef;
|
||||||
|
use fragment::{Fragment, FragmentBorderBoxIterator};
|
||||||
|
use gfx::display_list::{DisplayItemMetadata, OpaqueNode};
|
||||||
|
use msg::constellation_msg::Msg as ConstellationMsg;
|
||||||
|
use msg::constellation_msg::ConstellationChan;
|
||||||
|
use opaque_node::OpaqueNodeMethods;
|
||||||
|
use script::layout_interface::{ContentBoxResponse, ContentBoxesResponse, NodeGeometryResponse};
|
||||||
|
use script::layout_interface::{HitTestResponse, LayoutRPC, MouseOverResponse, OffsetParentResponse};
|
||||||
|
use script::layout_interface::{ResolvedStyleResponse, ScriptLayoutChan, TrustedNodeAddress};
|
||||||
|
use sequential;
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use util::geometry::Au;
|
||||||
|
use util::cursor::Cursor;
|
||||||
|
use util::logical_geometry::WritingMode;
|
||||||
|
|
||||||
|
pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutTaskData>>);
|
||||||
|
|
||||||
|
impl LayoutRPC for LayoutRPCImpl {
|
||||||
|
|
||||||
|
// The neat thing here is that in order to answer the following two queries we only
|
||||||
|
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
|
||||||
|
fn content_box(&self) -> ContentBoxResponse {
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
ContentBoxResponse(rw_data.content_box_response)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
|
||||||
|
fn content_boxes(&self) -> ContentBoxesResponse {
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
ContentBoxesResponse(rw_data.content_boxes_response.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_geometry(&self) -> NodeGeometryResponse {
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
NodeGeometryResponse {
|
||||||
|
client_rect: rw_data.client_rect_response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the resolved value for a CSS style property.
|
||||||
|
fn resolved_style(&self) -> ResolvedStyleResponse {
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
ResolvedStyleResponse(rw_data.resolved_style_response.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Requests the node containing the point of interest.
|
||||||
|
fn hit_test(&self, _: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()> {
|
||||||
|
let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
|
||||||
|
let resp = {
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
match rw_data.stacking_context {
|
||||||
|
None => panic!("no root stacking context!"),
|
||||||
|
Some(ref stacking_context) => {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
stacking_context.hit_test(point, &mut result, true);
|
||||||
|
if !result.is_empty() {
|
||||||
|
Some(HitTestResponse(result[0].node.to_untrusted_node_address()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if resp.is_some() {
|
||||||
|
return Ok(resp.unwrap());
|
||||||
|
}
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_over(&self, _: TrustedNodeAddress, point: Point2D<f32>)
|
||||||
|
-> Result<MouseOverResponse, ()> {
|
||||||
|
let mut mouse_over_list: Vec<DisplayItemMetadata> = vec!();
|
||||||
|
let point = Point2D::new(Au::from_f32_px(point.x), Au::from_f32_px(point.y));
|
||||||
|
{
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
match rw_data.stacking_context {
|
||||||
|
None => panic!("no root stacking context!"),
|
||||||
|
Some(ref stacking_context) => {
|
||||||
|
stacking_context.hit_test(point, &mut mouse_over_list, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the new cursor.
|
||||||
|
let cursor = if !mouse_over_list.is_empty() {
|
||||||
|
mouse_over_list[0].pointing.unwrap()
|
||||||
|
} else {
|
||||||
|
Cursor::DefaultCursor
|
||||||
|
};
|
||||||
|
let ConstellationChan(ref constellation_chan) = rw_data.constellation_chan;
|
||||||
|
constellation_chan.send(ConstellationMsg::SetCursor(cursor)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if mouse_over_list.is_empty() {
|
||||||
|
Err(())
|
||||||
|
} else {
|
||||||
|
let response_list =
|
||||||
|
mouse_over_list.iter()
|
||||||
|
.map(|metadata| metadata.node.to_untrusted_node_address())
|
||||||
|
.collect();
|
||||||
|
Ok(MouseOverResponse(response_list))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_parent(&self) -> OffsetParentResponse {
|
||||||
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let rw_data = rw_data.lock().unwrap();
|
||||||
|
rw_data.offset_parent_response.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct UnioningFragmentBorderBoxIterator {
|
||||||
|
pub node_address: OpaqueNode,
|
||||||
|
pub rect: Option<Rect<Au>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnioningFragmentBorderBoxIterator {
|
||||||
|
pub fn new(node_address: OpaqueNode) -> UnioningFragmentBorderBoxIterator {
|
||||||
|
UnioningFragmentBorderBoxIterator {
|
||||||
|
node_address: node_address,
|
||||||
|
rect: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FragmentBorderBoxIterator for UnioningFragmentBorderBoxIterator {
|
||||||
|
fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) {
|
||||||
|
self.rect = match self.rect {
|
||||||
|
Some(rect) => {
|
||||||
|
Some(rect.union(border_box))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
Some(*border_box)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
||||||
|
fragment.contains_node(self.node_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CollectingFragmentBorderBoxIterator {
|
||||||
|
pub node_address: OpaqueNode,
|
||||||
|
pub rects: Vec<Rect<Au>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CollectingFragmentBorderBoxIterator {
|
||||||
|
pub fn new(node_address: OpaqueNode) -> CollectingFragmentBorderBoxIterator {
|
||||||
|
CollectingFragmentBorderBoxIterator {
|
||||||
|
node_address: node_address,
|
||||||
|
rects: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FragmentBorderBoxIterator for CollectingFragmentBorderBoxIterator {
|
||||||
|
fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) {
|
||||||
|
self.rects.push(*border_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
||||||
|
fragment.contains_node(self.node_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Side {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Bottom,
|
||||||
|
Top
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MarginPadding {
|
||||||
|
Margin,
|
||||||
|
Padding
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PositionProperty {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Top,
|
||||||
|
Bottom,
|
||||||
|
Width,
|
||||||
|
Height,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PositionRetrievingFragmentBorderBoxIterator {
|
||||||
|
node_address: OpaqueNode,
|
||||||
|
pub result: Option<Au>,
|
||||||
|
position: Point2D<Au>,
|
||||||
|
property: PositionProperty,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PositionRetrievingFragmentBorderBoxIterator {
|
||||||
|
pub fn new(node_address: OpaqueNode,
|
||||||
|
property: PositionProperty,
|
||||||
|
position: Point2D<Au>) -> PositionRetrievingFragmentBorderBoxIterator {
|
||||||
|
PositionRetrievingFragmentBorderBoxIterator {
|
||||||
|
node_address: node_address,
|
||||||
|
position: position,
|
||||||
|
property: property,
|
||||||
|
result: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FragmentBorderBoxIterator for PositionRetrievingFragmentBorderBoxIterator {
|
||||||
|
fn process(&mut self, _: &Fragment, _: i32, border_box: &Rect<Au>) {
|
||||||
|
self.result =
|
||||||
|
Some(match self.property {
|
||||||
|
PositionProperty::Left => self.position.x,
|
||||||
|
PositionProperty::Top => self.position.y,
|
||||||
|
PositionProperty::Width => border_box.size.width,
|
||||||
|
PositionProperty::Height => border_box.size.height,
|
||||||
|
// TODO: the following 2 calculations are completely wrong.
|
||||||
|
// They should return the difference between the parent's and this
|
||||||
|
// fragment's border boxes.
|
||||||
|
PositionProperty::Right => border_box.max_x() + self.position.x,
|
||||||
|
PositionProperty::Bottom => border_box.max_y() + self.position.y,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
||||||
|
fragment.contains_node(self.node_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MarginRetrievingFragmentBorderBoxIterator {
|
||||||
|
node_address: OpaqueNode,
|
||||||
|
pub result: Option<Au>,
|
||||||
|
writing_mode: WritingMode,
|
||||||
|
margin_padding: MarginPadding,
|
||||||
|
side: Side,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MarginRetrievingFragmentBorderBoxIterator {
|
||||||
|
pub fn new(node_address: OpaqueNode, side: Side, margin_padding:
|
||||||
|
MarginPadding, writing_mode: WritingMode) -> MarginRetrievingFragmentBorderBoxIterator {
|
||||||
|
MarginRetrievingFragmentBorderBoxIterator {
|
||||||
|
node_address: node_address,
|
||||||
|
side: side,
|
||||||
|
margin_padding: margin_padding,
|
||||||
|
result: None,
|
||||||
|
writing_mode: writing_mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FragmentBorderBoxIterator for MarginRetrievingFragmentBorderBoxIterator {
|
||||||
|
fn process(&mut self, fragment: &Fragment, _: i32, _: &Rect<Au>) {
|
||||||
|
let rect = match self.margin_padding {
|
||||||
|
MarginPadding::Margin => &fragment.margin,
|
||||||
|
MarginPadding::Padding => &fragment.border_padding
|
||||||
|
};
|
||||||
|
self.result = Some(match self.side {
|
||||||
|
Side::Left => rect.left(self.writing_mode),
|
||||||
|
Side::Right => rect.right(self.writing_mode),
|
||||||
|
Side::Bottom => rect.bottom(self.writing_mode),
|
||||||
|
Side::Top => rect.top(self.writing_mode)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
||||||
|
fragment.contains_node(self.node_address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_content_box_request<'a>(requested_node: TrustedNodeAddress,
|
||||||
|
layout_root: &mut FlowRef,
|
||||||
|
rw_data: &mut RWGuard<'a>) {
|
||||||
|
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
|
||||||
|
// stuff. So the position is wrong in most cases.
|
||||||
|
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
|
||||||
|
let mut iterator = UnioningFragmentBorderBoxIterator::new(requested_node);
|
||||||
|
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
|
||||||
|
rw_data.content_box_response = match iterator.rect {
|
||||||
|
Some(rect) => rect,
|
||||||
|
None => Rect::zero()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn process_content_boxes_request<'a>(requested_node: TrustedNodeAddress,
|
||||||
|
layout_root: &mut FlowRef,
|
||||||
|
rw_data: &mut RWGuard<'a>) {
|
||||||
|
// FIXME(pcwalton): This has not been updated to handle the stacking context relative
|
||||||
|
// stuff. So the position is wrong in most cases.
|
||||||
|
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
|
||||||
|
let mut iterator = CollectingFragmentBorderBoxIterator::new(requested_node);
|
||||||
|
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
|
||||||
|
rw_data.content_boxes_response = iterator.rects;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue