aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorRizky Luthfianto <mrluthfianto@gmail.com>2015-11-19 23:30:39 +0700
committerRizky Luthfianto <mrluthfianto@gmail.com>2015-11-19 23:30:39 +0700
commit2c7117d73bf3536aa563f52b7b7c2a578015709d (patch)
treeee69cdaeb9b237fdbd8573152267bf93a73823df /components
parenta5babb89a02b9b84a8cd62554a5ceef9efb0d481 (diff)
downloadservo-2c7117d73bf3536aa563f52b7b7c2a578015709d.tar.gz
servo-2c7117d73bf3536aa563f52b7b7c2a578015709d.zip
refactor(layout_task.rs): move some functions to query.rs
Diffstat (limited to 'components')
-rw-r--r--components/layout/layout_task.rs305
-rw-r--r--components/layout/query.rs290
2 files changed, 297 insertions, 298 deletions
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 0ce3744d9ac..e4cfcefeaf5 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -13,7 +13,6 @@ use azure::azure::AzColor;
use canvas_traits::CanvasMsg;
use construct::ConstructionResult;
use context::{SharedLayoutContext, StylistWrapper, heap_size_of_local_context};
-use cssparser::ToCss;
use data::LayoutDataWrapper;
use display_list_builder::ToGfxColor;
use euclid::Matrix4;
@@ -24,7 +23,6 @@ use euclid::size::Size2D;
use flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use flow_ref::{self, FlowRef};
use fnv::FnvHasher;
-use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{ClippingRegion, DisplayList, LayerInfo, OpaqueNode, StackingContext};
use gfx::font_cache_task::FontCacheTask;
use gfx::font_context;
@@ -40,21 +38,18 @@ use msg::compositor_msg::{Epoch, LayerId, ScrollPolicy};
use msg::constellation_msg::ScriptMsg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, Failure, PipelineId};
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheResult, ImageCacheTask};
-use opaque_node::OpaqueNodeMethods;
use parallel::{self, WorkQueueData};
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
use profile_traits::time::{self, TimerMetadata, profile};
use query::{LayoutRPCImpl, process_content_box_request, process_content_boxes_request};
-use query::{MarginPadding, MarginRetrievingFragmentBorderBoxIterator, PositionProperty};
-use query::{PositionRetrievingFragmentBorderBoxIterator, Side};
+use query::{process_node_geometry_request, process_offset_parent_query, process_resolved_style_request};
use script::dom::node::LayoutData;
use script::layout_interface::Animation;
use script::layout_interface::{LayoutRPC, OffsetParentResponse};
use script::layout_interface::{Msg, NewLayoutTaskInfo, Reflow, ReflowGoal, ReflowQueryType};
-use script::layout_interface::{ScriptLayoutChan, ScriptReflow, TrustedNodeAddress};
+use script::layout_interface::{ScriptLayoutChan, ScriptReflow};
use script_traits::{ConstellationControlMsg, LayoutControlMsg, OpaqueScriptLayoutChannel};
-use selectors::parser::PseudoElement;
use sequential;
use serde_json;
use std::borrow::ToOwned;
@@ -65,16 +60,12 @@ use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::{channel, Sender, Receiver};
use std::sync::{Arc, Mutex, MutexGuard};
-use string_cache::Atom;
-use style::computed_values::{self, filter, mix_blend_mode};
+use style::computed_values::{filter, mix_blend_mode};
use style::media_queries::{Device, MediaType};
-use style::properties::longhands::{display, position};
-use style::properties::style_structs;
use style::selector_matching::{Stylist, USER_OR_USER_AGENT_STYLESHEETS};
use style::stylesheets::{CSSRuleIteratorExt, Stylesheet};
-use style::values::AuExtensionMethods;
use url::Url;
-use util::geometry::{MAX_RECT, ZERO_POINT};
+use util::geometry::MAX_RECT;
use util::ipc::OptionalIpcSender;
use util::logical_geometry::LogicalPoint;
use util::mem::HeapSizeOf;
@@ -82,7 +73,7 @@ use util::opts;
use util::task::spawn_named_with_send_on_failure;
use util::task_state;
use util::workqueue::WorkQueue;
-use wrapper::{LayoutDocument, LayoutElement, LayoutNode, ServoLayoutNode, ThreadSafeLayoutNode};
+use wrapper::{LayoutDocument, LayoutElement, LayoutNode, ServoLayoutNode};
/// The number of screens of data we're allowed to generate display lists for in each direction.
pub const DISPLAY_PORT_SIZE_FACTOR: i32 = 8;
@@ -810,168 +801,6 @@ impl LayoutTask {
traversal);
}
- fn process_node_geometry_request(&self,
- requested_node: TrustedNodeAddress,
- layout_root: &mut FlowRef)
- -> Rect<i32> {
- let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
- let mut iterator = FragmentLocatingFragmentIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.client_rect
- }
-
- /// Return the resolved value of property for a given (pseudo)element.
- /// https://drafts.csswg.org/cssom/#resolved-value
- fn process_resolved_style_request(&self,
- requested_node: TrustedNodeAddress,
- pseudo: &Option<PseudoElement>,
- property: &Atom,
- layout_root: &mut FlowRef)
- -> Option<String> {
- let node = unsafe { ServoLayoutNode::new(&requested_node) };
-
- let layout_node = ThreadSafeLayoutNode::new(&node);
- let layout_node = match pseudo {
- &Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
- &Some(PseudoElement::After) => layout_node.get_after_pseudo(),
- _ => Some(layout_node)
- };
-
- let layout_node = match layout_node {
- None => {
- // The pseudo doesn't exist, return nothing. Chrome seems to query
- // the element itself in this case, Firefox uses the resolved value.
- // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29006
- return None;
- }
- Some(layout_node) => layout_node
- };
-
- let style = &*layout_node.style();
-
- let positioned = match style.get_box().position {
- position::computed_value::T::relative |
- /*position::computed_value::T::sticky |*/
- position::computed_value::T::fixed |
- position::computed_value::T::absolute => true,
- _ => false
- };
-
- //TODO: determine whether requested property applies to the element.
- // eg. width does not apply to non-replaced inline elements.
- // Existing browsers disagree about when left/top/right/bottom apply
- // (Chrome seems to think they never apply and always returns resolved values).
- // There are probably other quirks.
- let applies = true;
-
- fn used_value_for_position_property(layout_node: ThreadSafeLayoutNode,
- layout_root: &mut FlowRef,
- requested_node: TrustedNodeAddress,
- property: &Atom) -> Option<String> {
- let layout_data = layout_node.borrow_layout_data();
- let position = layout_data.as_ref().map(|layout_data| {
- match layout_data.data.flow_construction_result {
- ConstructionResult::Flow(ref flow_ref, _) =>
- flow::base(flow_ref.deref()).stacking_relative_position,
- // TODO(dzbarsky) search parents until we find node with a flow ref.
- // https://github.com/servo/servo/issues/8307
- _ => ZERO_POINT
- }
- }).unwrap_or(ZERO_POINT);
- let property = match *property {
- atom!("bottom") => PositionProperty::Bottom,
- atom!("top") => PositionProperty::Top,
- atom!("left") => PositionProperty::Left,
- atom!("right") => PositionProperty::Right,
- atom!("width") => PositionProperty::Width,
- atom!("height") => PositionProperty::Height,
- _ => unreachable!()
- };
- let requested_node: OpaqueNode =
- OpaqueNodeMethods::from_script_node(requested_node);
- let mut iterator =
- PositionRetrievingFragmentBorderBoxIterator::new(requested_node,
- property,
- position);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root,
- &mut iterator);
- iterator.result.map(|r| r.to_css_string())
- }
-
- // TODO: we will return neither the computed nor used value for margin and padding.
- // Firefox returns blank strings for the computed value of shorthands,
- // so this should be web-compatible.
- match *property {
- atom!("margin-bottom") | atom!("margin-top") |
- atom!("margin-left") | atom!("margin-right") |
- atom!("padding-bottom") | atom!("padding-top") |
- atom!("padding-left") | atom!("padding-right")
- if applies && style.get_box().display != display::computed_value::T::none => {
- let (margin_padding, side) = match *property {
- atom!("margin-bottom") => (MarginPadding::Margin, Side::Bottom),
- atom!("margin-top") => (MarginPadding::Margin, Side::Top),
- atom!("margin-left") => (MarginPadding::Margin, Side::Left),
- atom!("margin-right") => (MarginPadding::Margin, Side::Right),
- atom!("padding-bottom") => (MarginPadding::Padding, Side::Bottom),
- atom!("padding-top") => (MarginPadding::Padding, Side::Top),
- atom!("padding-left") => (MarginPadding::Padding, Side::Left),
- atom!("padding-right") => (MarginPadding::Padding, Side::Right),
- _ => unreachable!()
- };
- let requested_node: OpaqueNode =
- OpaqueNodeMethods::from_script_node(requested_node);
- let mut iterator =
- MarginRetrievingFragmentBorderBoxIterator::new(requested_node,
- side,
- margin_padding,
- style.writing_mode);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root,
- &mut iterator);
- iterator.result.map(|r| r.to_css_string())
- },
-
- atom!("bottom") | atom!("top") | atom!("right") |
- atom!("left")
- if applies && positioned && style.get_box().display !=
- display::computed_value::T::none => {
- used_value_for_position_property(layout_node, layout_root, requested_node, property)
- }
- atom!("width") | atom!("height")
- if applies && style.get_box().display !=
- display::computed_value::T::none => {
- used_value_for_position_property(layout_node, layout_root, requested_node, property)
- }
- // FIXME: implement used value computation for line-height
- ref property => {
- style.computed_value_to_string(property.as_slice()).ok()
- }
- }
- }
-
- fn process_offset_parent_query(&self,
- requested_node: TrustedNodeAddress,
- layout_root: &mut FlowRef)
- -> OffsetParentResponse {
- let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
- let mut iterator = ParentOffsetBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- let parent_info_index = iterator.parent_nodes.iter().rposition(|info| info.is_some());
- match parent_info_index {
- Some(parent_info_index) => {
- let parent = iterator.parent_nodes[parent_info_index].as_ref().unwrap();
- let origin = iterator.node_border_box.origin - parent.border_box.origin;
- let size = iterator.node_border_box.size;
- OffsetParentResponse {
- node_address: Some(parent.node_address.to_untrusted_node_address()),
- rect: Rect::new(origin, size),
- }
- }
- None => {
- OffsetParentResponse::empty()
- }
- }
- }
-
fn compute_abs_pos_and_build_display_list(&mut self,
data: &Reflow,
layout_root: &mut FlowRef,
@@ -1203,16 +1032,13 @@ impl LayoutTask {
ReflowQueryType::ContentBoxesQuery(node) =>
rw_data.content_boxes_response = process_content_boxes_request(node, &mut root_flow),
ReflowQueryType::NodeGeometryQuery(node) =>
- rw_data.client_rect_response = self.process_node_geometry_request(node, &mut root_flow),
+ rw_data.client_rect_response = process_node_geometry_request(node, &mut root_flow),
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => {
rw_data.resolved_style_response =
- self.process_resolved_style_request(node,
- pseudo,
- property,
- &mut root_flow)
+ process_resolved_style_request(node, pseudo, property, &mut root_flow)
}
ReflowQueryType::OffsetParentQuery(node) =>
- rw_data.offset_parent_response = self.process_offset_parent_query(node, &mut root_flow),
+ rw_data.offset_parent_response = process_offset_parent_query(node, &mut root_flow),
ReflowQueryType::NoQuery => {}
}
}
@@ -1461,121 +1287,6 @@ impl LayoutTask {
}
}
-struct FragmentLocatingFragmentIterator {
- node_address: OpaqueNode,
- client_rect: Rect<i32>,
-}
-
-impl FragmentLocatingFragmentIterator {
- fn new(node_address: OpaqueNode) -> FragmentLocatingFragmentIterator {
- FragmentLocatingFragmentIterator {
- node_address: node_address,
- client_rect: Rect::zero()
- }
- }
-}
-
-struct ParentBorderBoxInfo {
- node_address: OpaqueNode,
- border_box: Rect<Au>,
-}
-
-struct ParentOffsetBorderBoxIterator {
- node_address: OpaqueNode,
- last_level: i32,
- has_found_node: bool,
- node_border_box: Rect<Au>,
- parent_nodes: Vec<Option<ParentBorderBoxInfo>>,
-}
-
-impl ParentOffsetBorderBoxIterator {
- fn new(node_address: OpaqueNode) -> ParentOffsetBorderBoxIterator {
- ParentOffsetBorderBoxIterator {
- node_address: node_address,
- last_level: -1,
- has_found_node: false,
- node_border_box: Rect::zero(),
- parent_nodes: Vec::new(),
- }
- }
-}
-
-impl FragmentBorderBoxIterator for FragmentLocatingFragmentIterator {
- fn process(&mut self, fragment: &Fragment, _: i32, border_box: &Rect<Au>) {
- let style_structs::Border {
- border_top_width: top_width,
- border_right_width: right_width,
- border_bottom_width: bottom_width,
- border_left_width: left_width,
- ..
- } = *fragment.style.get_border();
- self.client_rect.origin.y = top_width.to_px();
- self.client_rect.origin.x = left_width.to_px();
- self.client_rect.size.width = (border_box.size.width - left_width - right_width).to_px();
- self.client_rect.size.height = (border_box.size.height - top_width - bottom_width).to_px();
- }
-
- fn should_process(&mut self, fragment: &Fragment) -> bool {
- fragment.node == self.node_address
- }
-}
-
-// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
-impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
- fn process(&mut self, fragment: &Fragment, level: i32, border_box: &Rect<Au>) {
- if fragment.node == self.node_address {
- // Found the fragment in the flow tree that matches the
- // DOM node being looked for.
- self.has_found_node = true;
- self.node_border_box = *border_box;
-
- // offsetParent returns null if the node is fixed.
- if fragment.style.get_box().position == computed_values::position::T::fixed {
- self.parent_nodes.clear();
- }
- } else if level > self.last_level {
- // TODO(gw): Is there a less fragile way of checking whether this
- // fragment is the body element, rather than just checking that
- // the parent nodes stack contains the root node only?
- let is_body_element = self.parent_nodes.len() == 1;
-
- let is_valid_parent = match (is_body_element,
- fragment.style.get_box().position,
- &fragment.specific) {
- // Spec says it's valid if any of these are true:
- // 1) Is the body element
- // 2) Is static position *and* is a table or table cell
- // 3) Is not static position
- (true, _, _) |
- (false, computed_values::position::T::static_, &SpecificFragmentInfo::Table) |
- (false, computed_values::position::T::static_, &SpecificFragmentInfo::TableCell) |
- (false, computed_values::position::T::absolute, _) |
- (false, computed_values::position::T::relative, _) |
- (false, computed_values::position::T::fixed, _) => true,
-
- // Otherwise, it's not a valid parent
- (false, computed_values::position::T::static_, _) => false,
- };
-
- let parent_info = if is_valid_parent {
- Some(ParentBorderBoxInfo {
- border_box: *border_box,
- node_address: fragment.node,
- })
- } else {
- None
- };
-
- self.parent_nodes.push(parent_info);
- } else if level < self.last_level {
- self.parent_nodes.pop();
- }
- }
-
- fn should_process(&mut self, _: &Fragment) -> bool {
- !self.has_found_node
- }
-}
// The default computed value for background-color is transparent (see
// http://dev.w3.org/csswg/css-backgrounds/#background-color). However, we
diff --git a/components/layout/query.rs b/components/layout/query.rs
index c1aa95859c9..5ed91c7a24c 100644
--- a/components/layout/query.rs
+++ b/components/layout/query.rs
@@ -4,11 +4,15 @@
//! Utilities for querying the layout, as needed by the layout task.
+#![allow(unsafe_code)]
+
use app_units::Au;
+use construct::ConstructionResult;
use euclid::point::Point2D;
use euclid::rect::Rect;
+use flow;
use flow_ref::FlowRef;
-use fragment::{Fragment, FragmentBorderBoxIterator};
+use fragment::{Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
use gfx::display_list::{DisplayItemMetadata, OpaqueNode};
use layout_task::LayoutTaskData;
use msg::constellation_msg::ConstellationChan;
@@ -17,10 +21,19 @@ 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 selectors::parser::PseudoElement;
use sequential;
+use std::ops::Deref;
use std::sync::{Arc, Mutex};
+use string_cache::Atom;
+use style::computed_values;
+use style::properties::longhands::{display, position};
+use style::properties::style_structs;
+use style::values::AuExtensionMethods;
use util::cursor::Cursor;
+use util::geometry::ZERO_POINT;
use util::logical_geometry::WritingMode;
+use wrapper::{ServoLayoutNode, ThreadSafeLayoutNode};
pub struct LayoutRPCImpl(pub Arc<Mutex<LayoutTaskData>>);
@@ -305,3 +318,278 @@ pub fn process_content_boxes_request(requested_node: TrustedNodeAddress,
sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
iterator.rects
}
+
+struct FragmentLocatingFragmentIterator {
+ node_address: OpaqueNode,
+ client_rect: Rect<i32>,
+}
+
+impl FragmentLocatingFragmentIterator {
+ fn new(node_address: OpaqueNode) -> FragmentLocatingFragmentIterator {
+ FragmentLocatingFragmentIterator {
+ node_address: node_address,
+ client_rect: Rect::zero()
+ }
+ }
+}
+
+struct ParentBorderBoxInfo {
+ node_address: OpaqueNode,
+ border_box: Rect<Au>,
+}
+
+struct ParentOffsetBorderBoxIterator {
+ node_address: OpaqueNode,
+ last_level: i32,
+ has_found_node: bool,
+ node_border_box: Rect<Au>,
+ parent_nodes: Vec<Option<ParentBorderBoxInfo>>,
+}
+
+impl ParentOffsetBorderBoxIterator {
+ fn new(node_address: OpaqueNode) -> ParentOffsetBorderBoxIterator {
+ ParentOffsetBorderBoxIterator {
+ node_address: node_address,
+ last_level: -1,
+ has_found_node: false,
+ node_border_box: Rect::zero(),
+ parent_nodes: Vec::new(),
+ }
+ }
+}
+
+impl FragmentBorderBoxIterator for FragmentLocatingFragmentIterator {
+ fn process(&mut self, fragment: &Fragment, _: i32, border_box: &Rect<Au>) {
+ let style_structs::Border {
+ border_top_width: top_width,
+ border_right_width: right_width,
+ border_bottom_width: bottom_width,
+ border_left_width: left_width,
+ ..
+ } = *fragment.style.get_border();
+ self.client_rect.origin.y = top_width.to_px();
+ self.client_rect.origin.x = left_width.to_px();
+ self.client_rect.size.width = (border_box.size.width - left_width - right_width).to_px();
+ self.client_rect.size.height = (border_box.size.height - top_width - bottom_width).to_px();
+ }
+
+ fn should_process(&mut self, fragment: &Fragment) -> bool {
+ fragment.node == self.node_address
+ }
+}
+
+// https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface
+impl FragmentBorderBoxIterator for ParentOffsetBorderBoxIterator {
+ fn process(&mut self, fragment: &Fragment, level: i32, border_box: &Rect<Au>) {
+ if fragment.node == self.node_address {
+ // Found the fragment in the flow tree that matches the
+ // DOM node being looked for.
+ self.has_found_node = true;
+ self.node_border_box = *border_box;
+
+ // offsetParent returns null if the node is fixed.
+ if fragment.style.get_box().position == computed_values::position::T::fixed {
+ self.parent_nodes.clear();
+ }
+ } else if level > self.last_level {
+ // TODO(gw): Is there a less fragile way of checking whether this
+ // fragment is the body element, rather than just checking that
+ // the parent nodes stack contains the root node only?
+ let is_body_element = self.parent_nodes.len() == 1;
+
+ let is_valid_parent = match (is_body_element,
+ fragment.style.get_box().position,
+ &fragment.specific) {
+ // Spec says it's valid if any of these are true:
+ // 1) Is the body element
+ // 2) Is static position *and* is a table or table cell
+ // 3) Is not static position
+ (true, _, _) |
+ (false, computed_values::position::T::static_, &SpecificFragmentInfo::Table) |
+ (false, computed_values::position::T::static_, &SpecificFragmentInfo::TableCell) |
+ (false, computed_values::position::T::absolute, _) |
+ (false, computed_values::position::T::relative, _) |
+ (false, computed_values::position::T::fixed, _) => true,
+
+ // Otherwise, it's not a valid parent
+ (false, computed_values::position::T::static_, _) => false,
+ };
+
+ let parent_info = if is_valid_parent {
+ Some(ParentBorderBoxInfo {
+ border_box: *border_box,
+ node_address: fragment.node,
+ })
+ } else {
+ None
+ };
+
+ self.parent_nodes.push(parent_info);
+ } else if level < self.last_level {
+ self.parent_nodes.pop();
+ }
+ }
+
+ fn should_process(&mut self, _: &Fragment) -> bool {
+ !self.has_found_node
+ }
+}
+
+pub fn process_node_geometry_request(requested_node: TrustedNodeAddress,
+ layout_root: &mut FlowRef)
+ -> Rect<i32> {
+ let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
+ let mut iterator = FragmentLocatingFragmentIterator::new(requested_node);
+ sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
+ iterator.client_rect
+}
+
+/// Return the resolved value of property for a given (pseudo)element.
+/// https://drafts.csswg.org/cssom/#resolved-value
+pub fn process_resolved_style_request(requested_node: TrustedNodeAddress,
+ pseudo: &Option<PseudoElement>,
+ property: &Atom,
+ layout_root: &mut FlowRef)
+ -> Option<String> {
+ let node = unsafe { ServoLayoutNode::new(&requested_node) };
+
+ let layout_node = ThreadSafeLayoutNode::new(&node);
+ let layout_node = match pseudo {
+ &Some(PseudoElement::Before) => layout_node.get_before_pseudo(),
+ &Some(PseudoElement::After) => layout_node.get_after_pseudo(),
+ _ => Some(layout_node)
+ };
+
+ let layout_node = match layout_node {
+ None => {
+ // The pseudo doesn't exist, return nothing. Chrome seems to query
+ // the element itself in this case, Firefox uses the resolved value.
+ // https://www.w3.org/Bugs/Public/show_bug.cgi?id=29006
+ return None;
+ }
+ Some(layout_node) => layout_node
+ };
+
+ let style = &*layout_node.style();
+
+ let positioned = match style.get_box().position {
+ position::computed_value::T::relative |
+ /*position::computed_value::T::sticky |*/
+ position::computed_value::T::fixed |
+ position::computed_value::T::absolute => true,
+ _ => false
+ };
+
+ //TODO: determine whether requested property applies to the element.
+ // eg. width does not apply to non-replaced inline elements.
+ // Existing browsers disagree about when left/top/right/bottom apply
+ // (Chrome seems to think they never apply and always returns resolved values).
+ // There are probably other quirks.
+ let applies = true;
+
+ fn used_value_for_position_property(layout_node: ThreadSafeLayoutNode,
+ layout_root: &mut FlowRef,
+ requested_node: TrustedNodeAddress,
+ property: &Atom) -> Option<String> {
+ let layout_data = layout_node.borrow_layout_data();
+ let position = layout_data.as_ref().map(|layout_data| {
+ match layout_data.data.flow_construction_result {
+ ConstructionResult::Flow(ref flow_ref, _) =>
+ flow::base(flow_ref.deref()).stacking_relative_position,
+ // TODO(dzbarsky) search parents until we find node with a flow ref.
+ // https://github.com/servo/servo/issues/8307
+ _ => ZERO_POINT
+ }
+ }).unwrap_or(ZERO_POINT);
+ let property = match *property {
+ atom!("bottom") => PositionProperty::Bottom,
+ atom!("top") => PositionProperty::Top,
+ atom!("left") => PositionProperty::Left,
+ atom!("right") => PositionProperty::Right,
+ atom!("width") => PositionProperty::Width,
+ atom!("height") => PositionProperty::Height,
+ _ => unreachable!()
+ };
+ let requested_node: OpaqueNode =
+ OpaqueNodeMethods::from_script_node(requested_node);
+ let mut iterator =
+ PositionRetrievingFragmentBorderBoxIterator::new(requested_node,
+ property,
+ position);
+ sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root,
+ &mut iterator);
+ iterator.result.map(|r| r.to_css_string())
+ }
+
+ // TODO: we will return neither the computed nor used value for margin and padding.
+ // Firefox returns blank strings for the computed value of shorthands,
+ // so this should be web-compatible.
+ match *property {
+ atom!("margin-bottom") | atom!("margin-top") |
+ atom!("margin-left") | atom!("margin-right") |
+ atom!("padding-bottom") | atom!("padding-top") |
+ atom!("padding-left") | atom!("padding-right")
+ if applies && style.get_box().display != display::computed_value::T::none => {
+ let (margin_padding, side) = match *property {
+ atom!("margin-bottom") => (MarginPadding::Margin, Side::Bottom),
+ atom!("margin-top") => (MarginPadding::Margin, Side::Top),
+ atom!("margin-left") => (MarginPadding::Margin, Side::Left),
+ atom!("margin-right") => (MarginPadding::Margin, Side::Right),
+ atom!("padding-bottom") => (MarginPadding::Padding, Side::Bottom),
+ atom!("padding-top") => (MarginPadding::Padding, Side::Top),
+ atom!("padding-left") => (MarginPadding::Padding, Side::Left),
+ atom!("padding-right") => (MarginPadding::Padding, Side::Right),
+ _ => unreachable!()
+ };
+ let requested_node: OpaqueNode =
+ OpaqueNodeMethods::from_script_node(requested_node);
+ let mut iterator =
+ MarginRetrievingFragmentBorderBoxIterator::new(requested_node,
+ side,
+ margin_padding,
+ style.writing_mode);
+ sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root,
+ &mut iterator);
+ iterator.result.map(|r| r.to_css_string())
+ },
+
+ atom!("bottom") | atom!("top") | atom!("right") |
+ atom!("left")
+ if applies && positioned && style.get_box().display !=
+ display::computed_value::T::none => {
+ used_value_for_position_property(layout_node, layout_root, requested_node, property)
+ }
+ atom!("width") | atom!("height")
+ if applies && style.get_box().display !=
+ display::computed_value::T::none => {
+ used_value_for_position_property(layout_node, layout_root, requested_node, property)
+ }
+ // FIXME: implement used value computation for line-height
+ ref property => {
+ style.computed_value_to_string(property.as_slice()).ok()
+ }
+ }
+}
+
+pub fn process_offset_parent_query(requested_node: TrustedNodeAddress,
+ layout_root: &mut FlowRef)
+ -> OffsetParentResponse {
+ let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
+ let mut iterator = ParentOffsetBorderBoxIterator::new(requested_node);
+ sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
+ let parent_info_index = iterator.parent_nodes.iter().rposition(|info| info.is_some());
+ match parent_info_index {
+ Some(parent_info_index) => {
+ let parent = iterator.parent_nodes[parent_info_index].as_ref().unwrap();
+ let origin = iterator.node_border_box.origin - parent.border_box.origin;
+ let size = iterator.node_border_box.size;
+ OffsetParentResponse {
+ node_address: Some(parent.node_address.to_untrusted_node_address()),
+ rect: Rect::new(origin, size),
+ }
+ }
+ None => {
+ OffsetParentResponse::empty()
+ }
+ }
+}