aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/query.rs
diff options
context:
space:
mode:
authorOriol Brufau <obrufau@igalia.com>2025-03-13 08:26:57 +0100
committerGitHub <noreply@github.com>2025-03-13 07:26:57 +0000
commit7594dc69916f3c4d8ce771e1ef990b6d223eb158 (patch)
treee5e014f4b1168d13518b4c373c6aa8f5f9a37824 /components/layout/query.rs
parentf93006af95dd75a07de2571e6a2edabcc64a46ac (diff)
downloadservo-7594dc69916f3c4d8ce771e1ef990b6d223eb158.tar.gz
servo-7594dc69916f3c4d8ce771e1ef990b6d223eb158.zip
Remove legacy layout (layout 2013) (#35943)
We were already not compiling it and not running tests on it by default. So it's simpler to just completely remove it. Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Diffstat (limited to 'components/layout/query.rs')
-rw-r--r--components/layout/query.rs1108
1 files changed, 0 insertions, 1108 deletions
diff --git a/components/layout/query.rs b/components/layout/query.rs
deleted file mode 100644
index 212e37856f0..00000000000
--- a/components/layout/query.rs
+++ /dev/null
@@ -1,1108 +0,0 @@
-/* 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 https://mozilla.org/MPL/2.0/. */
-
-//! Utilities for querying the layout, as needed by layout.
-
-use std::cmp::{max, min};
-use std::ops::Deref;
-
-use app_units::Au;
-use euclid::default::{Box2D, Point2D, Rect, Size2D, Vector2D};
-use script_layout_interface::wrapper_traits::{
- LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode,
-};
-use script_layout_interface::{LayoutElementType, LayoutNodeType, OffsetParentResponse};
-use servo_arc::Arc as ServoArc;
-use servo_url::ServoUrl;
-use style::computed_values::display::T as Display;
-use style::computed_values::position::T as Position;
-use style::computed_values::visibility::T as Visibility;
-use style::context::{QuirksMode, SharedStyleContext, StyleContext, ThreadLocalStyleContext};
-use style::dom::TElement;
-use style::logical_geometry::{BlockFlowDirection, InlineBaseDirection, WritingMode};
-use style::properties::style_structs::{self, Font};
-use style::properties::{
- ComputedValues, Importance, LonghandId, PropertyDeclarationBlock, PropertyDeclarationId,
- PropertyId, ShorthandId, SourcePropertyDeclaration, parse_one_declaration_into,
-};
-use style::selector_parser::PseudoElement;
-use style::shared_lock::SharedRwLock;
-use style::stylesheets::{CssRuleType, Origin, UrlExtraData};
-use style_traits::{ParsingMode, ToCss};
-
-use crate::construct::ConstructionResult;
-use crate::display_list::IndexableText;
-use crate::display_list::items::OpaqueNode;
-use crate::flow::{Flow, GetBaseFlow};
-use crate::fragment::{Fragment, FragmentBorderBoxIterator, FragmentFlags, SpecificFragmentInfo};
-use crate::inline::InlineFragmentNodeFlags;
-use crate::sequential;
-use crate::wrapper::ThreadSafeLayoutNodeHelpers;
-
-// https://drafts.csswg.org/cssom-view/#overflow-directions
-fn overflow_direction(writing_mode: &WritingMode) -> OverflowDirection {
- match (
- writing_mode.block_flow_direction(),
- writing_mode.inline_base_direction(),
- ) {
- (BlockFlowDirection::TopToBottom, InlineBaseDirection::LeftToRight) |
- (BlockFlowDirection::LeftToRight, InlineBaseDirection::LeftToRight) => {
- OverflowDirection::RightAndDown
- },
- (BlockFlowDirection::TopToBottom, InlineBaseDirection::RightToLeft) |
- (BlockFlowDirection::RightToLeft, InlineBaseDirection::LeftToRight) => {
- OverflowDirection::LeftAndDown
- },
- (BlockFlowDirection::RightToLeft, InlineBaseDirection::RightToLeft) => {
- OverflowDirection::LeftAndUp
- },
- (BlockFlowDirection::LeftToRight, InlineBaseDirection::RightToLeft) => {
- OverflowDirection::RightAndUp
- },
- }
-}
-
-struct UnioningFragmentBorderBoxIterator {
- node_address: OpaqueNode,
- rect: Option<Rect<Au>>,
-}
-
-impl UnioningFragmentBorderBoxIterator {
- fn new(node_address: OpaqueNode) -> UnioningFragmentBorderBoxIterator {
- UnioningFragmentBorderBoxIterator {
- 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,
- 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)
- }
-}
-
-enum Side {
- Left,
- Right,
- Bottom,
- Top,
-}
-
-enum MarginPadding {
- Margin,
- Padding,
-}
-
-enum PositionProperty {
- Left,
- Right,
- Top,
- Bottom,
- Width,
- Height,
-}
-
-#[derive(Debug)]
-enum OverflowDirection {
- RightAndDown,
- LeftAndDown,
- LeftAndUp,
- RightAndUp,
-}
-
-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,
- position,
- property,
- result: None,
- }
- }
-}
-
-impl FragmentBorderBoxIterator for PositionRetrievingFragmentBorderBoxIterator {
- fn process(&mut self, fragment: &Fragment, _: i32, border_box: &Rect<Au>) {
- let border_padding = fragment
- .border_padding
- .to_physical(fragment.style.writing_mode);
- self.result = Some(match self.property {
- PositionProperty::Left => self.position.x,
- PositionProperty::Top => self.position.y,
- PositionProperty::Width => border_box.size.width - border_padding.horizontal(),
- PositionProperty::Height => border_box.size.height - border_padding.vertical(),
- // 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,
- side,
- margin_padding,
- result: None,
- 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(
- requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
-) -> Option<Rect<Au>> {
- // FIXME(pcwalton): This has not been updated to handle the stacking context relative
- // stuff. So the position is wrong in most cases.
- let mut iterator = UnioningFragmentBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.rect
-}
-
-pub fn process_content_boxes_request(
- requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
-) -> Vec<Rect<Au>> {
- // FIXME(pcwalton): This has not been updated to handle the stacking context relative
- // stuff. So the position is wrong in most cases.
- let mut iterator = CollectingFragmentBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.rects
-}
-
-struct FragmentClientRectQueryIterator {
- node_address: OpaqueNode,
- client_rect: Rect<i32>,
-}
-
-impl FragmentClientRectQueryIterator {
- fn new(node_address: OpaqueNode) -> FragmentClientRectQueryIterator {
- FragmentClientRectQueryIterator {
- node_address,
- client_rect: Rect::zero(),
- }
- }
-}
-
-struct UnioningFragmentScrollAreaIterator {
- node_address: OpaqueNode,
- union_rect: Rect<i32>,
- origin_rect: Rect<i32>,
- level: Option<i32>,
- is_child: bool,
- overflow_direction: OverflowDirection,
-}
-
-impl UnioningFragmentScrollAreaIterator {
- fn new(node_address: OpaqueNode) -> UnioningFragmentScrollAreaIterator {
- UnioningFragmentScrollAreaIterator {
- node_address,
- union_rect: Rect::zero(),
- origin_rect: Rect::zero(),
- level: None,
- is_child: false,
- // FIXME(#20867)
- overflow_direction: OverflowDirection::RightAndDown,
- }
- }
-}
-
-struct NodeOffsetBoxInfo {
- offset: Point2D<Au>,
- rectangle: Rect<Au>,
-}
-
-struct ParentBorderBoxInfo {
- node_address: OpaqueNode,
- origin: Point2D<Au>,
-}
-
-struct ParentOffsetBorderBoxIterator {
- node_address: OpaqueNode,
- has_processed_node: bool,
- node_offset_box: Option<NodeOffsetBoxInfo>,
- parent_nodes: Vec<Option<ParentBorderBoxInfo>>,
-}
-
-impl ParentOffsetBorderBoxIterator {
- fn new(node_address: OpaqueNode) -> ParentOffsetBorderBoxIterator {
- ParentOffsetBorderBoxIterator {
- node_address,
- has_processed_node: false,
- node_offset_box: None,
- parent_nodes: Vec::new(),
- }
- }
-}
-
-impl FragmentBorderBoxIterator for FragmentClientRectQueryIterator {
- 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();
- let (left_width, right_width) = (left_width.to_px(), right_width.to_px());
- let (top_width, bottom_width) = (top_width.to_px(), bottom_width.to_px());
- self.client_rect.origin.y = top_width;
- self.client_rect.origin.x = left_width;
- self.client_rect.size.width = border_box.size.width.to_px() - left_width - right_width;
- self.client_rect.size.height = border_box.size.height.to_px() - top_width - bottom_width;
- }
-
- fn should_process(&mut self, fragment: &Fragment) -> bool {
- fragment.node == self.node_address
- }
-}
-
-// https://drafts.csswg.org/cssom-view/#scrolling-area
-impl FragmentBorderBoxIterator for UnioningFragmentScrollAreaIterator {
- fn process(&mut self, fragment: &Fragment, level: i32, border_box: &Rect<Au>) {
- // In cases in which smaller child elements contain less padding than the parent
- // the a union of the two elements padding rectangles could result in an unwanted
- // increase in size. To work around this, we store the original elements padding
- // rectangle as `origin_rect` and the union of all child elements padding and
- // margin rectangles as `union_rect`.
- let style_structs::Border {
- border_top_width: top_border,
- border_right_width: right_border,
- border_bottom_width: bottom_border,
- border_left_width: left_border,
- ..
- } = *fragment.style.get_border();
- let (left_border, right_border) = (left_border.to_px(), right_border.to_px());
- let (top_border, bottom_border) = (top_border.to_px(), bottom_border.to_px());
- let right_padding = border_box.size.width.to_px() - right_border - left_border;
- let bottom_padding = border_box.size.height.to_px() - bottom_border - top_border;
- let top_padding = top_border;
- let left_padding = left_border;
-
- match self.level {
- Some(start_level) if level <= start_level => {
- self.is_child = false;
- },
- Some(_) => {
- let padding = Rect::new(
- Point2D::new(left_padding, top_padding),
- Size2D::new(right_padding, bottom_padding),
- );
- let top_margin = fragment.margin.top(fragment.style.writing_mode).to_px();
- let left_margin = fragment.margin.left(fragment.style.writing_mode).to_px();
- let bottom_margin = fragment.margin.bottom(fragment.style.writing_mode).to_px();
- let right_margin = fragment.margin.right(fragment.style.writing_mode).to_px();
- let margin = Rect::new(
- Point2D::new(left_margin, top_margin),
- Size2D::new(right_margin, bottom_margin),
- );
-
- // This is a workaround because euclid does not support unioning empty
- // rectangles.
- // TODO: The way that this iterator is calculating scroll area is very
- // suspect and the code below is a workaround until it can be written
- // in a better way.
- self.union_rect = Box2D::new(
- Point2D::new(
- min(
- padding.min_x(),
- min(margin.min_x(), self.union_rect.min_x()),
- ),
- min(
- padding.min_y(),
- min(margin.min_y(), self.union_rect.min_y()),
- ),
- ),
- Point2D::new(
- max(
- padding.max_x(),
- max(margin.max_x(), self.union_rect.max_x()),
- ),
- max(
- padding.max_y(),
- max(margin.max_y(), self.union_rect.max_y()),
- ),
- ),
- )
- .to_rect();
- },
- None => {
- self.level = Some(level);
- self.is_child = true;
- self.overflow_direction = overflow_direction(&fragment.style.writing_mode);
- self.origin_rect = Rect::new(
- Point2D::new(left_padding, top_padding),
- Size2D::new(right_padding, bottom_padding),
- );
- },
- };
- }
-
- fn should_process(&mut self, fragment: &Fragment) -> bool {
- fragment.contains_node(self.node_address) || self.is_child
- }
-}
-
-// 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 self.node_offset_box.is_none() {
- // We haven't found the node yet, so we're still looking
- // for its parent. Remove all nodes at this level or
- // higher, as they can't be parents of this node.
- self.parent_nodes.truncate(level as usize);
- assert_eq!(
- self.parent_nodes.len(),
- level as usize,
- "Skipped at least one level in the flow tree!"
- );
- }
-
- if !fragment.is_primary_fragment() {
- // This fragment doesn't correspond to anything worth
- // taking measurements from.
-
- if self.node_offset_box.is_none() {
- // If this is the only fragment in the flow, we need to
- // do this to avoid failing the above assertion.
- self.parent_nodes.push(None);
- }
-
- return;
- }
-
- if fragment.node == self.node_address {
- // Found the fragment in the flow tree that matches the
- // DOM node being looked for.
-
- assert!(
- self.node_offset_box.is_none(),
- "Node was being treated as inline, but it has an associated fragment!"
- );
-
- self.has_processed_node = true;
- self.node_offset_box = Some(NodeOffsetBoxInfo {
- offset: border_box.origin,
- rectangle: *border_box,
- });
-
- // offsetParent returns null if the node is fixed.
- if fragment.style.get_box().position == Position::Fixed {
- self.parent_nodes.clear();
- }
- } else if let Some(node) = fragment.inline_context.as_ref().and_then(|inline_context| {
- inline_context
- .nodes
- .iter()
- .find(|node| node.address == self.node_address)
- }) {
- // TODO: Handle cases where the `offsetParent` is an inline
- // element. This will likely be impossible until
- // https://github.com/servo/servo/issues/13982 is fixed.
-
- // Found a fragment in the flow tree whose inline context
- // contains the DOM node we're looking for, i.e. the node
- // is inline and contains this fragment.
- match self.node_offset_box {
- Some(NodeOffsetBoxInfo {
- ref mut rectangle, ..
- }) => {
- *rectangle = rectangle.union(border_box);
- },
- None => {
- // https://github.com/servo/servo/issues/13982 will
- // cause this assertion to fail sometimes, so it's
- // commented out for now.
- /*assert!(node.flags.contains(FIRST_FRAGMENT_OF_ELEMENT),
- "First fragment of inline node found wasn't its first fragment!");*/
-
- self.node_offset_box = Some(NodeOffsetBoxInfo {
- offset: border_box.origin,
- rectangle: *border_box,
- });
- },
- }
-
- if node
- .flags
- .contains(InlineFragmentNodeFlags::LAST_FRAGMENT_OF_ELEMENT)
- {
- self.has_processed_node = true;
- }
- } else if self.node_offset_box.is_none() {
- let is_body_element = fragment
- .flags
- .contains(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT);
- 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, Position::Static, &SpecificFragmentInfo::Table) |
- (false, Position::Static, &SpecificFragmentInfo::TableCell) |
- (false, Position::Sticky, _) |
- (false, Position::Absolute, _) |
- (false, Position::Relative, _) |
- (false, Position::Fixed, _) => true,
-
- // Otherwise, it's not a valid parent
- (false, Position::Static, _) => false,
- };
-
- let parent_info = if is_valid_parent {
- let border_width = fragment
- .border_width()
- .to_physical(fragment.style.writing_mode);
-
- Some(ParentBorderBoxInfo {
- node_address: fragment.node,
- origin: border_box.origin + Vector2D::new(border_width.left, border_width.top),
- })
- } else {
- None
- };
-
- self.parent_nodes.push(parent_info);
- }
- }
-
- fn should_process(&mut self, _: &Fragment) -> bool {
- !self.has_processed_node
- }
-}
-
-pub fn process_client_rect_query(
- requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
-) -> Rect<i32> {
- let mut iterator = FragmentClientRectQueryIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator.client_rect
-}
-
-/// <https://drafts.csswg.org/cssom-view/#scrolling-area>
-pub fn process_scrolling_area_request(
- requested_node: Option<OpaqueNode>,
- layout_root: &mut dyn Flow,
-) -> Rect<i32> {
- let requested_node = match requested_node {
- Some(node) => node,
- None => {
- let rect = layout_root.base().overflow.scroll;
- return Rect::new(
- Point2D::new(rect.origin.x.to_nearest_px(), rect.origin.y.to_nearest_px()),
- Size2D::new(rect.width().ceil_to_px(), rect.height().ceil_to_px()),
- );
- },
- };
-
- let mut iterator = UnioningFragmentScrollAreaIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- match iterator.overflow_direction {
- OverflowDirection::RightAndDown => {
- let right = max(
- iterator.union_rect.size.width,
- iterator.origin_rect.size.width,
- );
- let bottom = max(
- iterator.union_rect.size.height,
- iterator.origin_rect.size.height,
- );
- Rect::new(iterator.origin_rect.origin, Size2D::new(right, bottom))
- },
- OverflowDirection::LeftAndDown => {
- let bottom = max(
- iterator.union_rect.size.height,
- iterator.origin_rect.size.height,
- );
- let left = min(iterator.union_rect.origin.x, iterator.origin_rect.origin.x);
- Rect::new(
- Point2D::new(left, iterator.origin_rect.origin.y),
- Size2D::new(iterator.origin_rect.size.width, bottom),
- )
- },
- OverflowDirection::LeftAndUp => {
- let top = min(iterator.union_rect.origin.y, iterator.origin_rect.origin.y);
- let left = min(iterator.union_rect.origin.x, iterator.origin_rect.origin.x);
- Rect::new(Point2D::new(left, top), iterator.origin_rect.size)
- },
- OverflowDirection::RightAndUp => {
- let top = min(iterator.union_rect.origin.y, iterator.origin_rect.origin.y);
- let right = max(
- iterator.union_rect.size.width,
- iterator.origin_rect.size.width,
- );
- Rect::new(
- Point2D::new(iterator.origin_rect.origin.x, top),
- Size2D::new(right, iterator.origin_rect.size.height),
- )
- },
- }
-}
-
-fn create_font_declaration(
- value: &str,
- url_data: &ServoUrl,
- quirks_mode: QuirksMode,
-) -> Option<PropertyDeclarationBlock> {
- let mut declarations = SourcePropertyDeclaration::default();
- let result = parse_one_declaration_into(
- &mut declarations,
- PropertyId::NonCustom(ShorthandId::Font.into()),
- value,
- Origin::Author,
- &UrlExtraData(url_data.get_arc()),
- None,
- ParsingMode::DEFAULT,
- quirks_mode,
- CssRuleType::Style,
- );
- let declarations = match result {
- Ok(()) => {
- let mut block = PropertyDeclarationBlock::new();
- block.extend(declarations.drain(), Importance::Normal);
- block
- },
- Err(_) => return None,
- };
- // TODO: Force to set line-height property to 'normal' font property.
- Some(declarations)
-}
-
-fn resolve_for_declarations<'dom, E>(
- context: &SharedStyleContext,
- parent_style: Option<&ComputedValues>,
- declarations: PropertyDeclarationBlock,
- shared_lock: &SharedRwLock,
-) -> ServoArc<ComputedValues>
-where
- E: LayoutNode<'dom>,
-{
- let parent_style = match parent_style {
- Some(parent) => parent,
- None => context.stylist.device().default_computed_values(),
- };
- context
- .stylist
- .compute_for_declarations::<E::ConcreteElement>(
- &context.guards,
- parent_style,
- ServoArc::new(shared_lock.wrap(declarations)),
- )
-}
-
-pub fn process_resolved_font_style_request<'dom, E>(
- context: &SharedStyleContext,
- node: E,
- value: &str,
- url_data: ServoUrl,
- shared_lock: &SharedRwLock,
-) -> Option<ServoArc<Font>>
-where
- E: LayoutNode<'dom>,
-{
- use style::stylist::RuleInclusion;
- use style::traversal::resolve_style;
-
- // 1. Parse the given font property value
- let quirks_mode = context.quirks_mode();
- let declarations = create_font_declaration(value, &url_data, quirks_mode)?;
-
- // TODO: Reject 'inherit' and 'initial' values for the font property.
-
- // 2. Get resolved styles for the parent element
- let element = node.as_element().unwrap();
- let parent_style = if node.is_connected() {
- if element.has_data() {
- node.to_threadsafe().as_element().unwrap().resolved_style()
- } else {
- let mut tlc = ThreadLocalStyleContext::new();
- let mut context = StyleContext {
- shared: context,
- thread_local: &mut tlc,
- };
- let styles = resolve_style(&mut context, element, RuleInclusion::All, None, None);
- styles.primary().clone()
- }
- } else {
- let default_declarations =
- create_font_declaration("10px sans-serif", &url_data, quirks_mode).unwrap();
- resolve_for_declarations::<E>(context, None, default_declarations, shared_lock)
- };
-
- // 3. Resolve the parsed value with resolved styles of the parent element
- let computed_values =
- resolve_for_declarations::<E>(context, Some(&*parent_style), declarations, shared_lock);
-
- Some(computed_values.clone_font())
-}
-
-/// Return the resolved value of property for a given (pseudo)element.
-/// <https://drafts.csswg.org/cssom/#resolved-value>
-pub fn process_resolved_style_request<'dom>(
- context: &SharedStyleContext,
- node: impl LayoutNode<'dom>,
- pseudo: &Option<PseudoElement>,
- property: &PropertyId,
- layout_root: &mut dyn Flow,
-) -> String {
- use style::stylist::RuleInclusion;
- use style::traversal::resolve_style;
-
- let element = node.as_element().unwrap();
-
- // We call process_resolved_style_request after performing a whole-document
- // traversal, so in the common case, the element is styled.
- if element.has_data() {
- return process_resolved_style_request_internal(node, pseudo, property, layout_root);
- }
-
- // In a display: none subtree. No pseudo-element exists.
- if pseudo.is_some() {
- return String::new();
- }
-
- let mut tlc = ThreadLocalStyleContext::new();
- let mut context = StyleContext {
- shared: context,
- thread_local: &mut tlc,
- };
-
- let styles = resolve_style(
- &mut context,
- element,
- RuleInclusion::All,
- pseudo.as_ref(),
- None,
- );
- let style = styles.primary();
- let longhand_id = match *property {
- PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
- Ok(longhand_id) => longhand_id,
- Err(shorthand_id) => return shorthand_to_css_string(shorthand_id, style),
- },
- PropertyId::Custom(ref name) => {
- return style.computed_value_to_string(PropertyDeclarationId::Custom(name));
- },
- };
-
- // No need to care about used values here, since we're on a display: none
- // subtree, use the resolved value.
- style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id))
-}
-
-/// The primary resolution logic, which assumes that the element is styled.
-fn process_resolved_style_request_internal<'dom>(
- requested_node: impl LayoutNode<'dom>,
- pseudo: &Option<PseudoElement>,
- property: &PropertyId,
- layout_root: &mut dyn Flow,
-) -> String {
- let layout_el = requested_node.to_threadsafe().as_element().unwrap();
- let layout_el = match *pseudo {
- Some(PseudoElement::Before) => layout_el.get_before_pseudo(),
- Some(PseudoElement::After) => layout_el.get_after_pseudo(),
- Some(PseudoElement::DetailsSummary) |
- Some(PseudoElement::DetailsContent) |
- Some(PseudoElement::Selection) => None,
- // FIXME(emilio): What about the other pseudos? Probably they shouldn't
- // just return the element's style!
- _ => Some(layout_el),
- };
-
- let layout_el = match layout_el {
- 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 String::new();
- },
- Some(layout_el) => layout_el,
- };
-
- let style = &*layout_el.resolved_style();
- let longhand_id = match *property {
- PropertyId::NonCustom(id) => match id.longhand_or_shorthand() {
- Ok(longhand_id) => longhand_id,
- Err(shorthand_id) => return shorthand_to_css_string(shorthand_id, style),
- },
- PropertyId::Custom(ref name) => {
- return style.computed_value_to_string(PropertyDeclarationId::Custom(name));
- },
- };
-
- let positioned = matches!(
- style.get_box().position,
- Position::Relative | Position::Sticky | Position::Fixed | Position::Absolute
- );
-
- //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<'dom, N>(
- layout_el: <N::ConcreteThreadSafeLayoutNode as ThreadSafeLayoutNode<'dom>>::ConcreteThreadSafeLayoutElement,
- layout_root: &mut dyn Flow,
- requested_node: N,
- longhand_id: LonghandId,
- ) -> String
- where
- N: LayoutNode<'dom>,
- {
- let maybe_data = layout_el.as_node().borrow_layout_data();
- let position = maybe_data.map_or(Point2D::zero(), |data| {
- match data.flow_construction_result {
- ConstructionResult::Flow(ref flow_ref, _) => flow_ref
- .deref()
- .base()
- .stacking_relative_position
- .to_point(),
- // TODO(dzbarsky) search parents until we find node with a flow ref.
- // https://github.com/servo/servo/issues/8307
- _ => Point2D::zero(),
- }
- });
- let property = match longhand_id {
- LonghandId::Bottom => PositionProperty::Bottom,
- LonghandId::Top => PositionProperty::Top,
- LonghandId::Left => PositionProperty::Left,
- LonghandId::Right => PositionProperty::Right,
- LonghandId::Width => PositionProperty::Width,
- LonghandId::Height => PositionProperty::Height,
- _ => unreachable!(),
- };
- let mut iterator = PositionRetrievingFragmentBorderBoxIterator::new(
- requested_node.opaque(),
- property,
- position,
- );
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
- iterator
- .result
- .map(|r| r.to_css_string())
- .unwrap_or_default()
- }
-
- // TODO: we will return neither the computed nor used value for margin and padding.
- match longhand_id {
- LonghandId::MarginBottom |
- LonghandId::MarginTop |
- LonghandId::MarginLeft |
- LonghandId::MarginRight |
- LonghandId::PaddingBottom |
- LonghandId::PaddingTop |
- LonghandId::PaddingLeft |
- LonghandId::PaddingRight
- if applies && style.get_box().display != Display::None =>
- {
- let (margin_padding, side) = match longhand_id {
- LonghandId::MarginBottom => (MarginPadding::Margin, Side::Bottom),
- LonghandId::MarginTop => (MarginPadding::Margin, Side::Top),
- LonghandId::MarginLeft => (MarginPadding::Margin, Side::Left),
- LonghandId::MarginRight => (MarginPadding::Margin, Side::Right),
- LonghandId::PaddingBottom => (MarginPadding::Padding, Side::Bottom),
- LonghandId::PaddingTop => (MarginPadding::Padding, Side::Top),
- LonghandId::PaddingLeft => (MarginPadding::Padding, Side::Left),
- LonghandId::PaddingRight => (MarginPadding::Padding, Side::Right),
- _ => unreachable!(),
- };
- let mut iterator = MarginRetrievingFragmentBorderBoxIterator::new(
- requested_node.opaque(),
- 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())
- .unwrap_or_default()
- },
-
- LonghandId::Bottom | LonghandId::Top | LonghandId::Right | LonghandId::Left
- if applies && positioned && style.get_box().display != Display::None =>
- {
- used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id)
- },
- LonghandId::Width | LonghandId::Height
- if applies && style.get_box().display != Display::None =>
- {
- used_value_for_position_property(layout_el, layout_root, requested_node, longhand_id)
- },
- // FIXME: implement used value computation for line-height
- _ => style.computed_value_to_string(PropertyDeclarationId::Longhand(longhand_id)),
- }
-}
-
-fn shorthand_to_css_string(
- id: style::properties::ShorthandId,
- style: &style::properties::ComputedValues,
-) -> String {
- use style::values::resolved::Context;
- let mut block = PropertyDeclarationBlock::new();
- let mut dest = String::new();
- for longhand in id.longhands() {
- block.push(
- style.computed_or_resolved_declaration(longhand, Some(&Context { style })),
- Importance::Normal,
- );
- }
- match block.shorthand_to_css(id, &mut dest) {
- Ok(_) => dest.to_owned(),
- Err(_) => String::new(),
- }
-}
-
-pub fn process_offset_parent_query(
- requested_node: OpaqueNode,
- layout_root: &mut dyn Flow,
-) -> OffsetParentResponse {
- let mut iterator = ParentOffsetBorderBoxIterator::new(requested_node);
- sequential::iterate_through_flow_tree_fragment_border_boxes(layout_root, &mut iterator);
-
- let node_offset_box = iterator.node_offset_box;
- let parent_info = iterator.parent_nodes.into_iter().rev().flatten().next();
- match (node_offset_box, parent_info) {
- (Some(node_offset_box), Some(parent_info)) => {
- let origin = node_offset_box.offset - parent_info.origin.to_vector();
- let size = node_offset_box.rectangle.size;
- OffsetParentResponse {
- node_address: Some(parent_info.node_address.into()),
- rect: Rect::new(origin, size),
- }
- },
- _ => OffsetParentResponse::default(),
- }
-}
-
-enum InnerTextItem {
- Text(String),
- RequiredLineBreakCount(u32),
-}
-
-/// <https://html.spec.whatwg.org/multipage/#get-the-text-steps>
-pub fn get_the_text_steps<'dom>(
- node: impl LayoutNode<'dom>,
- indexable_text: &IndexableText,
-) -> String {
- // Step 1.
- let mut results = Vec::new();
- // Step 2.
- inner_text_collection_steps(node, indexable_text, &mut results);
- let mut max_req_line_break_count = 0;
- let mut inner_text = Vec::new();
- for item in results {
- match item {
- InnerTextItem::Text(s) => {
- if max_req_line_break_count > 0 {
- // Step 5.
- for _ in 0..max_req_line_break_count {
- inner_text.push("\u{000A}".to_owned());
- }
- max_req_line_break_count = 0;
- }
- // Step 3.
- if !s.is_empty() {
- inner_text.push(s.to_owned());
- }
- },
- InnerTextItem::RequiredLineBreakCount(count) => {
- // Step 4.
- if inner_text.is_empty() {
- // Remove required line break count at the start.
- continue;
- }
- // Store the count if it's the max of this run,
- // but it may be ignored if no text item is found afterwards,
- // which means that these are consecutive line breaks at the end.
- if count > max_req_line_break_count {
- max_req_line_break_count = count;
- }
- },
- }
- }
- inner_text.into_iter().collect()
-}
-
-// https://html.spec.whatwg.org/multipage/#inner-text-collection-steps
-#[allow(unsafe_code)]
-fn inner_text_collection_steps<'dom>(
- node: impl LayoutNode<'dom>,
- indexable_text: &IndexableText,
- results: &mut Vec<InnerTextItem>,
-) {
- let mut items = Vec::new();
- for child in node.traverse_preorder() {
- let node = match child.type_id() {
- LayoutNodeType::Text => child.parent_node().unwrap(),
- _ => child,
- };
-
- let element_data = match node.style_data() {
- Some(data) => &data.element_data,
- None => continue,
- };
-
- let style = match element_data.borrow().styles.get_primary() {
- None => continue,
- Some(style) => style.clone(),
- };
-
- // Step 2.
- if style.get_inherited_box().visibility != Visibility::Visible {
- continue;
- }
-
- // Step 3.
- let display = style.get_box().display;
- if !child.is_connected() || display == Display::None {
- continue;
- }
-
- match child.type_id() {
- LayoutNodeType::Text => {
- // Step 4.
- if let Some(text_content) = indexable_text.get(child.opaque()) {
- for content in text_content {
- items.push(InnerTextItem::Text(content.text_run.text.to_string()));
- }
- }
- },
- LayoutNodeType::Element(LayoutElementType::HTMLBRElement) => {
- // Step 5.
- items.push(InnerTextItem::Text(String::from(
- "\u{000A}", /* line feed */
- )));
- },
- LayoutNodeType::Element(LayoutElementType::HTMLParagraphElement) => {
- // Step 8.
- items.insert(0, InnerTextItem::RequiredLineBreakCount(2));
- items.push(InnerTextItem::RequiredLineBreakCount(2));
- },
- _ => {},
- }
-
- match display {
- Display::TableCell if !is_last_table_cell() => {
- // Step 6.
- items.push(InnerTextItem::Text(String::from("\u{0009}" /* tab */)));
- },
- Display::TableRow if !is_last_table_row() => {
- // Step 7.
- items.push(InnerTextItem::Text(String::from(
- "\u{000A}", /* line feed */
- )));
- },
- Display::Block | Display::Flex | Display::TableCaption | Display::Table => {
- // Step 9.
- items.insert(0, InnerTextItem::RequiredLineBreakCount(1));
- items.push(InnerTextItem::RequiredLineBreakCount(1));
- },
- _ => {},
- }
- }
-
- results.append(&mut items);
-}
-
-fn is_last_table_cell() -> bool {
- // FIXME(ferjm) Implement this.
- false
-}
-
-fn is_last_table_row() -> bool {
- // FIXME(ferjm) Implement this.
- false
-}