mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #8606 - rilut:refactor-layout-task, r=Ms2ger
refactor(layout_task.rs): move some functions to query.rs Should fixes #8470 <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8606) <!-- Reviewable:end -->
This commit is contained in:
commit
ff4171170d
2 changed files with 297 additions and 298 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue