diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index b1201e26add..2103d62bb09 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -5,12 +5,10 @@ use crate::context::LayoutContext; use crate::display_list::conversions::ToWebRender; use crate::display_list::stacking_context::StackingContextSection; -use crate::fragment_tree::Tag; -use crate::fragments::{BoxFragment, Fragment, TextFragment}; +use crate::fragment_tree::{BoxFragment, Fragment, FragmentTree, Tag, TextFragment}; use crate::geom::{PhysicalPoint, PhysicalRect}; use crate::replaced::IntrinsicSizes; use crate::style_ext::ComputedValuesExt; -use crate::FragmentTree; use embedder_traits::Cursor; use euclid::{Point2D, SideOffsets2D, Size2D}; use fnv::FnvHashMap; diff --git a/components/layout_2020/display_list/stacking_context.rs b/components/layout_2020/display_list/stacking_context.rs index 77421ea59a9..63fefd0c4f4 100644 --- a/components/layout_2020/display_list/stacking_context.rs +++ b/components/layout_2020/display_list/stacking_context.rs @@ -6,11 +6,11 @@ use super::DisplayList; use crate::cell::ArcRefCell; use crate::display_list::conversions::ToWebRender; use crate::display_list::DisplayListBuilder; -use crate::fragment_tree::ContainingBlockManager; -use crate::fragments::{AnonymousFragment, BoxFragment, Fragment}; +use crate::fragment_tree::{ + AnonymousFragment, BoxFragment, ContainingBlockManager, Fragment, FragmentTree, +}; use crate::geom::PhysicalRect; use crate::style_ext::ComputedValuesExt; -use crate::FragmentTree; use euclid::default::Rect; use script_traits::compositor::{ScrollTreeNodeId, ScrollableNodeInfo}; use servo_arc::Arc as ServoArc; diff --git a/components/layout_2020/flexbox/layout.rs b/components/layout_2020/flexbox/layout.rs index 4a15af23005..e4e59379d1e 100644 --- a/components/layout_2020/flexbox/layout.rs +++ b/components/layout_2020/flexbox/layout.rs @@ -8,7 +8,7 @@ use super::geom::{ use super::{FlexContainer, FlexLevelBox}; use crate::context::LayoutContext; use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout}; -use crate::fragments::{BoxFragment, CollapsedBlockMargins, Fragment}; +use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::LengthOrAuto; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs index 582ca9e1242..784c5d62722 100644 --- a/components/layout_2020/flow/float.rs +++ b/components/layout_2020/flow/float.rs @@ -10,7 +10,7 @@ use crate::context::LayoutContext; use crate::dom::NodeExt; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragments::{ +use crate::fragment_tree::{ BoxFragment, CollapsedBlockMargins, CollapsedMargin, FloatFragment, Fragment, }; use crate::geom::flow_relative::{Rect, Vec2}; diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs index 962a90e9213..7d447c492db 100644 --- a/components/layout_2020/flow/inline.rs +++ b/components/layout_2020/flow/inline.rs @@ -7,9 +7,9 @@ use crate::context::LayoutContext; use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::flow::FlowLayout; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::BaseFragmentInfo; -use crate::fragments::{ - AnonymousFragment, BoxFragment, CollapsedBlockMargins, FontMetrics, Fragment, TextFragment, +use crate::fragment_tree::{ + AnonymousFragment, BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, FontMetrics, Fragment, + TextFragment, }; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{ diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs index cb88ecebe6a..fcd2c2b1837 100644 --- a/components/layout_2020/flow/mod.rs +++ b/components/layout_2020/flow/mod.rs @@ -13,8 +13,9 @@ use crate::flow::inline::InlineFormattingContext; use crate::formatting_contexts::{ IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext, }; -use crate::fragment_tree::BaseFragmentInfo; -use crate::fragments::{BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment}; +use crate::fragment_tree::{ + BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment, +}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::replaced::ReplacedContent; @@ -34,7 +35,7 @@ pub mod float; pub mod inline; mod root; -pub use root::{BoxTree, FragmentTree}; +pub use root::{BoxTree, CanvasBackground}; #[derive(Debug, Serialize)] pub(crate) struct BlockFormattingContext { diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs index 39f82e85fa0..e30577c3c09 100644 --- a/components/layout_2020/flow/root.rs +++ b/components/layout_2020/flow/root.rs @@ -4,7 +4,6 @@ use crate::cell::ArcRefCell; use crate::context::LayoutContext; -use crate::display_list::StackingContext; use crate::dom::{LayoutBox, NodeExt}; use crate::dom_traversal::{iter_child_nodes, Contents, NodeAndStyleInfo}; use crate::flexbox::FlexLevelBox; @@ -13,8 +12,7 @@ use crate::flow::float::FloatBox; use crate::flow::inline::InlineLevelBox; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::{ContainingBlockManager, Tag}; -use crate::fragments::Fragment; +use crate::fragment_tree::FragmentTree; use crate::geom::flow_relative::Vec2; use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSize}; use crate::positioned::AbsolutelyPositionedBox; @@ -23,15 +21,10 @@ use crate::replaced::ReplacedContent; use crate::style_ext::ComputedValuesExt; use crate::style_ext::{Display, DisplayGeneratingBox, DisplayInside}; use crate::DefiniteContainingBlock; -use app_units::Au; use atomic_refcell::AtomicRef; -use euclid::default::{Point2D, Rect, Size2D}; -use fxhash::FxHashSet; -use gfx_traits::print_tree::PrintTree; use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::{LayoutElementType, LayoutNodeType}; use servo_arc::Arc; -use style::animation::AnimationSetKey; use style::dom::OpaqueNode; use style::properties::ComputedValues; use style::values::computed::Length; @@ -47,30 +40,6 @@ pub struct BoxTree { canvas_background: CanvasBackground, } -#[derive(Serialize)] -pub struct FragmentTree { - /// Fragments at the top-level of the tree. - /// - /// If the root element has `display: none`, there are zero fragments. - /// Otherwise, there is at least one: - /// - /// * The first fragment is generated by the root element. - /// * There may be additional fragments generated by positioned boxes - /// that have the initial containing block. - pub(crate) root_fragments: Vec>, - - /// The scrollable overflow rectangle for the entire tree - /// https://drafts.csswg.org/css-overflow/#scrollable - pub(crate) scrollable_overflow: PhysicalRect, - - /// The containing block used in the layout of this fragment tree. - pub(crate) initial_containing_block: PhysicalRect, - - /// https://drafts.csswg.org/css-backgrounds/#special-backgrounds - #[serde(skip)] - pub(crate) canvas_background: CanvasBackground, -} - impl BoxTree { pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self where @@ -380,173 +349,9 @@ impl BoxTree { } } -impl FragmentTree { - pub(crate) fn build_display_list( - &self, - builder: &mut crate::display_list::DisplayListBuilder, - root_stacking_context: &StackingContext, - ) { - // Paint the canvas’ background (if any) before/under everything else - root_stacking_context.build_canvas_background_display_list( - builder, - self, - &self.initial_containing_block, - ); - root_stacking_context.build_display_list(builder); - } - - pub fn print(&self) { - let mut print_tree = PrintTree::new("Fragment Tree".to_string()); - for fragment in &self.root_fragments { - fragment.borrow().print(&mut print_tree); - } - } - - pub fn scrollable_overflow(&self) -> webrender_api::units::LayoutSize { - webrender_api::units::LayoutSize::from_untyped(Size2D::new( - self.scrollable_overflow.size.width.px(), - self.scrollable_overflow.size.height.px(), - )) - } - - pub(crate) fn find( - &self, - mut process_func: impl FnMut(&Fragment, usize, &PhysicalRect) -> Option, - ) -> Option { - let info = ContainingBlockManager { - for_non_absolute_descendants: &self.initial_containing_block, - for_absolute_descendants: None, - for_absolute_and_fixed_descendants: &self.initial_containing_block, - }; - self.root_fragments - .iter() - .find_map(|child| child.borrow().find(&info, 0, &mut process_func)) - } - - pub fn remove_nodes_in_fragment_tree_from_set(&self, set: &mut FxHashSet) { - self.find(|fragment, _, _| { - let tag = fragment.tag()?; - set.remove(&AnimationSetKey::new(tag.node, tag.pseudo)); - None::<()> - }); - } - - pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect { - let mut bounding_box = PhysicalRect::zero(); - let tag_to_find = Tag::new(requested_node); - self.find(|fragment, _, containing_block| { - if fragment.tag() != Some(tag_to_find) { - return None::<()>; - } - - let fragment_relative_rect = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => fragment - .border_rect() - .to_physical(fragment.style.writing_mode, &containing_block), - Fragment::Text(fragment) => fragment - .rect - .to_physical(fragment.parent_style.writing_mode, &containing_block), - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Image(_) | - Fragment::IFrame(_) | - Fragment::Anonymous(_) => return None, - }; - - bounding_box = fragment_relative_rect - .translate(containing_block.origin.to_vector()) - .union(&bounding_box); - None::<()> - }); - - Rect::new( - Point2D::new( - Au::from_f32_px(bounding_box.origin.x.px()), - Au::from_f32_px(bounding_box.origin.y.px()), - ), - Size2D::new( - Au::from_f32_px(bounding_box.size.width.px()), - Au::from_f32_px(bounding_box.size.height.px()), - ), - ) - } - - pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect { - let tag_to_find = Tag::new(requested_node); - self.find(|fragment, _, containing_block| { - if fragment.tag() != Some(tag_to_find) { - return None; - } - - let (style, padding_rect) = match fragment { - Fragment::Box(fragment) => (&fragment.style, fragment.padding_rect()), - _ => return None, - }; - - // https://drafts.csswg.org/cssom-view/#dom-element-clienttop - // " If the element has no associated CSS layout box or if the - // CSS layout box is inline, return zero." For this check we - // also explicitly ignore the list item portion of the display - // style. - let display = &style.get_box().display; - if display.inside() == style::values::specified::box_::DisplayInside::Flow && - display.outside() == style::values::specified::box_::DisplayOutside::Inline - { - return Some(Rect::zero()); - } - - let padding_rect = padding_rect.to_physical(style.writing_mode, &containing_block); - let border = style.get_border(); - Some(Rect::new( - Point2D::new( - border.border_left_width.px() as i32, - border.border_top_width.px() as i32, - ), - Size2D::new( - padding_rect.size.width.px() as i32, - padding_rect.size.height.px() as i32, - ), - )) - }) - .unwrap_or_else(Rect::zero) - } - - pub fn get_scroll_area_for_node(&self, requested_node: OpaqueNode) -> Rect { - let mut scroll_area: PhysicalRect = PhysicalRect::zero(); - let tag_to_find = Tag::new(requested_node); - self.find(|fragment, _, containing_block| { - if fragment.tag() != Some(tag_to_find) { - return None::<()>; - } - - scroll_area = match fragment { - Fragment::Box(fragment) | Fragment::Float(fragment) => fragment - .scrollable_overflow(&containing_block) - .translate(containing_block.origin.to_vector()), - Fragment::Text(_) | - Fragment::AbsoluteOrFixedPositioned(_) | - Fragment::Image(_) | - Fragment::IFrame(_) | - Fragment::Anonymous(_) => return None, - }; - None::<()> - }); - - Rect::new( - Point2D::new( - scroll_area.origin.x.px() as i32, - scroll_area.origin.y.px() as i32, - ), - Size2D::new( - scroll_area.size.width.px() as i32, - scroll_area.size.height.px() as i32, - ), - ) - } -} - /// https://drafts.csswg.org/css-backgrounds/#root-background #[derive(Clone, Serialize)] -pub(crate) struct CanvasBackground { +pub struct CanvasBackground { /// DOM node for the root element pub root_element: OpaqueNode, diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs index d913ad7241a..494d7822fd9 100644 --- a/components/layout_2020/formatting_contexts.rs +++ b/components/layout_2020/formatting_contexts.rs @@ -7,8 +7,7 @@ use crate::dom::NodeExt; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::flexbox::FlexContainer; use crate::flow::BlockFormattingContext; -use crate::fragment_tree::BaseFragmentInfo; -use crate::fragments::Fragment; +use crate::fragment_tree::{BaseFragmentInfo, Fragment}; use crate::positioned::PositioningContext; use crate::replaced::ReplacedContent; use crate::sizing::{self, ContentSizes}; diff --git a/components/layout_2020/fragment_tree/base.rs b/components/layout_2020/fragment_tree/base_fragment.rs similarity index 100% rename from components/layout_2020/fragment_tree/base.rs rename to components/layout_2020/fragment_tree/base_fragment.rs diff --git a/components/layout_2020/fragment_tree/box_fragment.rs b/components/layout_2020/fragment_tree/box_fragment.rs new file mode 100644 index 00000000000..6f697ef23fd --- /dev/null +++ b/components/layout_2020/fragment_tree/box_fragment.rs @@ -0,0 +1,280 @@ +/* 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/. */ + +use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment}; +use crate::cell::ArcRefCell; +use crate::geom::flow_relative::{Rect, Sides}; +use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize}; +use gfx_traits::print_tree::PrintTree; +use servo_arc::Arc as ServoArc; +use style::computed_values::overflow_x::T as ComputedOverflow; +use style::computed_values::position::T as ComputedPosition; +use style::properties::ComputedValues; +use style::values::computed::{CSSPixelLength, Length, LengthPercentage, LengthPercentageOrAuto}; +use style::Zero; + +#[derive(Serialize)] +pub(crate) struct BoxFragment { + pub base: BaseFragment, + + #[serde(skip_serializing)] + pub style: ServoArc, + pub children: Vec>, + + /// From the containing block’s start corner…? + /// This might be broken when the containing block is in a different writing mode: + /// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows + pub content_rect: Rect, + + pub padding: Sides, + pub border: Sides, + pub margin: Sides, + + pub clearance: Length, + + pub block_margins_collapsed_with_children: CollapsedBlockMargins, + + /// The scrollable overflow of this box fragment. + pub scrollable_overflow_from_children: PhysicalRect, + + /// Whether or not this box was overconstrained in the given dimension. + overconstrained: PhysicalSize, +} + +impl BoxFragment { + pub fn new( + base_fragment_info: BaseFragmentInfo, + style: ServoArc, + children: Vec, + content_rect: Rect, + padding: Sides, + border: Sides, + margin: Sides, + clearance: Length, + block_margins_collapsed_with_children: CollapsedBlockMargins, + ) -> BoxFragment { + let position = style.get_box().position; + let insets = style.get_position(); + let width_overconstrained = position == ComputedPosition::Relative && + !insets.left.is_auto() && + !insets.right.is_auto(); + let height_overconstrained = position == ComputedPosition::Relative && + !insets.left.is_auto() && + !insets.bottom.is_auto(); + + Self::new_with_overconstrained( + base_fragment_info, + style, + children, + content_rect, + padding, + border, + margin, + clearance, + block_margins_collapsed_with_children, + PhysicalSize::new(width_overconstrained, height_overconstrained), + ) + } + + pub fn new_with_overconstrained( + base_fragment_info: BaseFragmentInfo, + style: ServoArc, + children: Vec, + content_rect: Rect, + padding: Sides, + border: Sides, + margin: Sides, + clearance: Length, + block_margins_collapsed_with_children: CollapsedBlockMargins, + overconstrained: PhysicalSize, + ) -> BoxFragment { + // FIXME(mrobinson, bug 25564): We should be using the containing block + // here to properly convert scrollable overflow to physical geometry. + let containing_block = PhysicalRect::zero(); + let scrollable_overflow_from_children = + children.iter().fold(PhysicalRect::zero(), |acc, child| { + acc.union(&child.scrollable_overflow(&containing_block)) + }); + + BoxFragment { + base: base_fragment_info.into(), + style, + children: children + .into_iter() + .map(|fragment| ArcRefCell::new(fragment)) + .collect(), + content_rect, + padding, + border, + margin, + clearance, + block_margins_collapsed_with_children, + scrollable_overflow_from_children, + overconstrained, + } + } + + pub fn scrollable_overflow( + &self, + containing_block: &PhysicalRect, + ) -> PhysicalRect { + let physical_padding_rect = self + .padding_rect() + .to_physical(self.style.writing_mode, containing_block); + + let content_origin = self + .content_rect + .start_corner + .to_physical(self.style.writing_mode); + physical_padding_rect.union( + &self + .scrollable_overflow_from_children + .translate(content_origin.to_vector()), + ) + } + + pub fn padding_rect(&self) -> Rect { + self.content_rect.inflate(&self.padding) + } + + pub fn border_rect(&self) -> Rect { + self.padding_rect().inflate(&self.border) + } + + pub fn print(&self, tree: &mut PrintTree) { + tree.new_level(format!( + "Box\ + \nbase={:?}\ + \ncontent={:?}\ + \npadding rect={:?}\ + \nborder rect={:?}\ + \nclearance={:?}\ + \nscrollable_overflow={:?}\ + \noverflow={:?} / {:?}", + self.base, + self.content_rect, + self.padding_rect(), + self.border_rect(), + self.clearance, + self.scrollable_overflow(&PhysicalRect::zero()), + self.style.get_box().overflow_x, + self.style.get_box().overflow_y, + )); + + for child in &self.children { + child.borrow().print(tree); + } + tree.end_level(); + } + + pub fn scrollable_overflow_for_parent( + &self, + containing_block: &PhysicalRect, + ) -> PhysicalRect { + let mut overflow = self + .border_rect() + .to_physical(self.style.writing_mode, containing_block); + + if self.style.get_box().overflow_y != ComputedOverflow::Visible && + self.style.get_box().overflow_x != ComputedOverflow::Visible + { + return overflow; + } + + // https://www.w3.org/TR/css-overflow-3/#scrollable + // Only include the scrollable overflow of a child box if it has overflow: visible. + let scrollable_overflow = self.scrollable_overflow(&containing_block); + let bottom_right = PhysicalPoint::new( + overflow.max_x().max(scrollable_overflow.max_x()), + overflow.max_y().max(scrollable_overflow.max_y()), + ); + + if self.style.get_box().overflow_y == ComputedOverflow::Visible { + overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y); + overflow.size.height = bottom_right.y - overflow.origin.y; + } + + if self.style.get_box().overflow_x == ComputedOverflow::Visible { + overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x); + overflow.size.width = bottom_right.x - overflow.origin.x; + } + + overflow + } + + pub(crate) fn calculate_resolved_insets_if_positioned( + &self, + containing_block: &PhysicalRect, + ) -> PhysicalSides { + let position = self.style.get_box().position; + debug_assert_ne!( + position, + ComputedPosition::Static, + "Should not call this method on statically positioned box." + ); + + let (cb_width, cb_height) = (containing_block.width(), containing_block.height()); + let content_rect = self + .content_rect + .to_physical(self.style.writing_mode, &containing_block); + + // "A resolved value special case property like top defined in another + // specification If the property applies to a positioned element and the + // resolved value of the display property is not none or contents, and + // the property is not over-constrained, then the resolved value is the + // used value. Otherwise the resolved value is the computed value." + // https://drafts.csswg.org/cssom/#resolved-values + let insets = self.style.get_position(); + if position == ComputedPosition::Relative { + let get_resolved_axis = + |start: &LengthPercentageOrAuto, + end: &LengthPercentageOrAuto, + container_length: CSSPixelLength| { + let start = start.map(|v| v.percentage_relative_to(container_length)); + let end = end.map(|v| v.percentage_relative_to(container_length)); + match (start.non_auto(), end.non_auto()) { + (None, None) => (Length::zero(), Length::zero()), + (None, Some(end)) => (-end, end), + (Some(start), None) => (start, -start), + // This is the overconstrained case, for which the resolved insets will + // simply be the computed insets. + (Some(start), Some(end)) => (start, end), + } + }; + let (left, right) = get_resolved_axis(&insets.left, &insets.right, cb_width); + let (top, bottom) = get_resolved_axis(&insets.top, &insets.bottom, cb_height); + return PhysicalSides::new(top, right, bottom, left); + } + + debug_assert!( + position == ComputedPosition::Fixed || position == ComputedPosition::Absolute, + "Got unknown position." + ); + + let resolve = |value: &LengthPercentageOrAuto, container_length| { + value + .auto_is(LengthPercentage::zero) + .percentage_relative_to(container_length) + }; + + let (top, bottom) = if self.overconstrained.height { + ( + resolve(&insets.top, cb_height), + resolve(&insets.bottom, cb_height), + ) + } else { + (content_rect.origin.y, cb_height - content_rect.max_y()) + }; + let (left, right) = if self.overconstrained.width { + ( + resolve(&insets.left, cb_width), + resolve(&insets.right, cb_width), + ) + } else { + (content_rect.origin.x, cb_width - content_rect.max_x()) + }; + + PhysicalSides::new(top, right, bottom, left) + } +} diff --git a/components/layout_2020/fragment_tree/containing_block.rs b/components/layout_2020/fragment_tree/containing_block.rs index febe52dfd6d..82a205fd2d5 100644 --- a/components/layout_2020/fragment_tree/containing_block.rs +++ b/components/layout_2020/fragment_tree/containing_block.rs @@ -2,7 +2,7 @@ * 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/. */ -use crate::fragments::Fragment; +use crate::fragment_tree::Fragment; use style::computed_values::position::T as ComputedPosition; /// A data structure used to track the containing block when recursing diff --git a/components/layout_2020/fragments.rs b/components/layout_2020/fragment_tree/fragment.rs similarity index 55% rename from components/layout_2020/fragments.rs rename to components/layout_2020/fragment_tree/fragment.rs index f4b25ca2b46..782676be8bc 100644 --- a/components/layout_2020/fragments.rs +++ b/components/layout_2020/fragment_tree/fragment.rs @@ -2,11 +2,10 @@ * 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/. */ +use super::{BaseFragment, BoxFragment, ContainingBlockManager, HoistedSharedFragment, Tag}; use crate::cell::ArcRefCell; -use crate::fragment_tree::{BaseFragment, BaseFragmentInfo, ContainingBlockManager, Tag}; use crate::geom::flow_relative::{Rect, Sides}; -use crate::geom::{PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize}; -use crate::positioned::HoistedSharedFragment; +use crate::geom::PhysicalRect; use crate::style_ext::ComputedValuesExt; use gfx::font::FontMetrics as GfxFontMetrics; use gfx::text::glyph::GlyphStore; @@ -14,11 +13,9 @@ use gfx_traits::print_tree::PrintTree; use msg::constellation_msg::{BrowsingContextId, PipelineId}; use servo_arc::Arc as ServoArc; use std::sync::Arc; -use style::computed_values::overflow_x::T as ComputedOverflow; -use style::computed_values::position::T as ComputedPosition; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; -use style::values::computed::{CSSPixelLength, Length, LengthPercentage, LengthPercentageOrAuto}; +use style::values::computed::Length; use style::values::specified::text::TextDecorationLine; use style::Zero; use webrender_api::{FontInstanceKey, ImageKey}; @@ -46,34 +43,6 @@ pub(crate) enum Fragment { IFrame(IFrameFragment), } -#[derive(Serialize)] -pub(crate) struct BoxFragment { - pub base: BaseFragment, - - #[serde(skip_serializing)] - pub style: ServoArc, - pub children: Vec>, - - /// From the containing block’s start corner…? - /// This might be broken when the containing block is in a different writing mode: - /// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows - pub content_rect: Rect, - - pub padding: Sides, - pub border: Sides, - pub margin: Sides, - - pub clearance: Length, - - pub block_margins_collapsed_with_children: CollapsedBlockMargins, - - /// The scrollable overflow of this box fragment. - pub scrollable_overflow_from_children: PhysicalRect, - - /// Whether or not this box was overconstrained in the given dimension. - overconstrained: PhysicalSize, -} - #[derive(Serialize)] pub(crate) struct FloatFragment { pub box_fragment: BoxFragment, @@ -327,243 +296,6 @@ impl AnonymousFragment { } } -impl BoxFragment { - pub fn new( - base_fragment_info: BaseFragmentInfo, - style: ServoArc, - children: Vec, - content_rect: Rect, - padding: Sides, - border: Sides, - margin: Sides, - clearance: Length, - block_margins_collapsed_with_children: CollapsedBlockMargins, - ) -> BoxFragment { - let position = style.get_box().position; - let insets = style.get_position(); - let width_overconstrained = position == ComputedPosition::Relative && - !insets.left.is_auto() && - !insets.right.is_auto(); - let height_overconstrained = position == ComputedPosition::Relative && - !insets.left.is_auto() && - !insets.bottom.is_auto(); - - Self::new_with_overconstrained( - base_fragment_info, - style, - children, - content_rect, - padding, - border, - margin, - clearance, - block_margins_collapsed_with_children, - PhysicalSize::new(width_overconstrained, height_overconstrained), - ) - } - - pub fn new_with_overconstrained( - base_fragment_info: BaseFragmentInfo, - style: ServoArc, - children: Vec, - content_rect: Rect, - padding: Sides, - border: Sides, - margin: Sides, - clearance: Length, - block_margins_collapsed_with_children: CollapsedBlockMargins, - overconstrained: PhysicalSize, - ) -> BoxFragment { - // FIXME(mrobinson, bug 25564): We should be using the containing block - // here to properly convert scrollable overflow to physical geometry. - let containing_block = PhysicalRect::zero(); - let scrollable_overflow_from_children = - children.iter().fold(PhysicalRect::zero(), |acc, child| { - acc.union(&child.scrollable_overflow(&containing_block)) - }); - - BoxFragment { - base: base_fragment_info.into(), - style, - children: children - .into_iter() - .map(|fragment| ArcRefCell::new(fragment)) - .collect(), - content_rect, - padding, - border, - margin, - clearance, - block_margins_collapsed_with_children, - scrollable_overflow_from_children, - overconstrained, - } - } - - pub fn scrollable_overflow( - &self, - containing_block: &PhysicalRect, - ) -> PhysicalRect { - let physical_padding_rect = self - .padding_rect() - .to_physical(self.style.writing_mode, containing_block); - - let content_origin = self - .content_rect - .start_corner - .to_physical(self.style.writing_mode); - physical_padding_rect.union( - &self - .scrollable_overflow_from_children - .translate(content_origin.to_vector()), - ) - } - - pub fn padding_rect(&self) -> Rect { - self.content_rect.inflate(&self.padding) - } - - pub fn border_rect(&self) -> Rect { - self.padding_rect().inflate(&self.border) - } - - pub fn print(&self, tree: &mut PrintTree) { - tree.new_level(format!( - "Box\ - \nbase={:?}\ - \ncontent={:?}\ - \npadding rect={:?}\ - \nborder rect={:?}\ - \nclearance={:?}\ - \nscrollable_overflow={:?}\ - \noverflow={:?} / {:?}", - self.base, - self.content_rect, - self.padding_rect(), - self.border_rect(), - self.clearance, - self.scrollable_overflow(&PhysicalRect::zero()), - self.style.get_box().overflow_x, - self.style.get_box().overflow_y, - )); - - for child in &self.children { - child.borrow().print(tree); - } - tree.end_level(); - } - - pub fn scrollable_overflow_for_parent( - &self, - containing_block: &PhysicalRect, - ) -> PhysicalRect { - let mut overflow = self - .border_rect() - .to_physical(self.style.writing_mode, containing_block); - - if self.style.get_box().overflow_y != ComputedOverflow::Visible && - self.style.get_box().overflow_x != ComputedOverflow::Visible - { - return overflow; - } - - // https://www.w3.org/TR/css-overflow-3/#scrollable - // Only include the scrollable overflow of a child box if it has overflow: visible. - let scrollable_overflow = self.scrollable_overflow(&containing_block); - let bottom_right = PhysicalPoint::new( - overflow.max_x().max(scrollable_overflow.max_x()), - overflow.max_y().max(scrollable_overflow.max_y()), - ); - - if self.style.get_box().overflow_y == ComputedOverflow::Visible { - overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y); - overflow.size.height = bottom_right.y - overflow.origin.y; - } - - if self.style.get_box().overflow_x == ComputedOverflow::Visible { - overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x); - overflow.size.width = bottom_right.x - overflow.origin.x; - } - - overflow - } - - pub(crate) fn calculate_resolved_insets_if_positioned( - &self, - containing_block: &PhysicalRect, - ) -> PhysicalSides { - let position = self.style.get_box().position; - debug_assert_ne!( - position, - ComputedPosition::Static, - "Should not call this method on statically positioned box." - ); - - let (cb_width, cb_height) = (containing_block.width(), containing_block.height()); - let content_rect = self - .content_rect - .to_physical(self.style.writing_mode, &containing_block); - - // "A resolved value special case property like top defined in another - // specification If the property applies to a positioned element and the - // resolved value of the display property is not none or contents, and - // the property is not over-constrained, then the resolved value is the - // used value. Otherwise the resolved value is the computed value." - // https://drafts.csswg.org/cssom/#resolved-values - let insets = self.style.get_position(); - if position == ComputedPosition::Relative { - let get_resolved_axis = - |start: &LengthPercentageOrAuto, - end: &LengthPercentageOrAuto, - container_length: CSSPixelLength| { - let start = start.map(|v| v.percentage_relative_to(container_length)); - let end = end.map(|v| v.percentage_relative_to(container_length)); - match (start.non_auto(), end.non_auto()) { - (None, None) => (Length::zero(), Length::zero()), - (None, Some(end)) => (-end, end), - (Some(start), None) => (start, -start), - // This is the overconstrained case, for which the resolved insets will - // simply be the computed insets. - (Some(start), Some(end)) => (start, end), - } - }; - let (left, right) = get_resolved_axis(&insets.left, &insets.right, cb_width); - let (top, bottom) = get_resolved_axis(&insets.top, &insets.bottom, cb_height); - return PhysicalSides::new(top, right, bottom, left); - } - - debug_assert!( - position == ComputedPosition::Fixed || position == ComputedPosition::Absolute, - "Got unknown position." - ); - - let resolve = |value: &LengthPercentageOrAuto, container_length| { - value - .auto_is(LengthPercentage::zero) - .percentage_relative_to(container_length) - }; - - let (top, bottom) = if self.overconstrained.height { - ( - resolve(&insets.top, cb_height), - resolve(&insets.bottom, cb_height), - ) - } else { - (content_rect.origin.y, cb_height - content_rect.max_y()) - }; - let (left, right) = if self.overconstrained.width { - ( - resolve(&insets.left, cb_width), - resolve(&insets.right, cb_width), - ) - } else { - (content_rect.origin.x, cb_width - content_rect.max_x()) - }; - - PhysicalSides::new(top, right, bottom, left) - } -} - impl TextFragment { pub fn print(&self, tree: &mut PrintTree) { tree.add_item(format!( diff --git a/components/layout_2020/fragment_tree/fragment_tree.rs b/components/layout_2020/fragment_tree/fragment_tree.rs new file mode 100644 index 00000000000..6234d9e3390 --- /dev/null +++ b/components/layout_2020/fragment_tree/fragment_tree.rs @@ -0,0 +1,204 @@ +/* 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/. */ + +use super::{ContainingBlockManager, Fragment, Tag}; +use crate::cell::ArcRefCell; +use crate::display_list::StackingContext; +use crate::flow::CanvasBackground; +use crate::geom::PhysicalRect; +use app_units::Au; +use euclid::default::{Point2D, Rect, Size2D}; +use fxhash::FxHashSet; +use gfx_traits::print_tree::PrintTree; +use style::animation::AnimationSetKey; +use style::dom::OpaqueNode; +use style::values::computed::Length; + +#[derive(Serialize)] +pub struct FragmentTree { + /// Fragments at the top-level of the tree. + /// + /// If the root element has `display: none`, there are zero fragments. + /// Otherwise, there is at least one: + /// + /// * The first fragment is generated by the root element. + /// * There may be additional fragments generated by positioned boxes + /// that have the initial containing block. + pub(crate) root_fragments: Vec>, + + /// The scrollable overflow rectangle for the entire tree + /// https://drafts.csswg.org/css-overflow/#scrollable + pub(crate) scrollable_overflow: PhysicalRect, + + /// The containing block used in the layout of this fragment tree. + pub(crate) initial_containing_block: PhysicalRect, + + /// https://drafts.csswg.org/css-backgrounds/#special-backgrounds + #[serde(skip)] + pub(crate) canvas_background: CanvasBackground, +} + +impl FragmentTree { + pub(crate) fn build_display_list( + &self, + builder: &mut crate::display_list::DisplayListBuilder, + root_stacking_context: &StackingContext, + ) { + // Paint the canvas’ background (if any) before/under everything else + root_stacking_context.build_canvas_background_display_list( + builder, + self, + &self.initial_containing_block, + ); + root_stacking_context.build_display_list(builder); + } + + pub fn print(&self) { + let mut print_tree = PrintTree::new("Fragment Tree".to_string()); + for fragment in &self.root_fragments { + fragment.borrow().print(&mut print_tree); + } + } + + pub fn scrollable_overflow(&self) -> webrender_api::units::LayoutSize { + webrender_api::units::LayoutSize::from_untyped(Size2D::new( + self.scrollable_overflow.size.width.px(), + self.scrollable_overflow.size.height.px(), + )) + } + + pub(crate) fn find( + &self, + mut process_func: impl FnMut(&Fragment, usize, &PhysicalRect) -> Option, + ) -> Option { + let info = ContainingBlockManager { + for_non_absolute_descendants: &self.initial_containing_block, + for_absolute_descendants: None, + for_absolute_and_fixed_descendants: &self.initial_containing_block, + }; + self.root_fragments + .iter() + .find_map(|child| child.borrow().find(&info, 0, &mut process_func)) + } + + pub fn remove_nodes_in_fragment_tree_from_set(&self, set: &mut FxHashSet) { + self.find(|fragment, _, _| { + let tag = fragment.tag()?; + set.remove(&AnimationSetKey::new(tag.node, tag.pseudo)); + None::<()> + }); + } + + pub fn get_content_box_for_node(&self, requested_node: OpaqueNode) -> Rect { + let mut bounding_box = PhysicalRect::zero(); + let tag_to_find = Tag::new(requested_node); + self.find(|fragment, _, containing_block| { + if fragment.tag() != Some(tag_to_find) { + return None::<()>; + } + + let fragment_relative_rect = match fragment { + Fragment::Box(fragment) | Fragment::Float(fragment) => fragment + .border_rect() + .to_physical(fragment.style.writing_mode, &containing_block), + Fragment::Text(fragment) => fragment + .rect + .to_physical(fragment.parent_style.writing_mode, &containing_block), + Fragment::AbsoluteOrFixedPositioned(_) | + Fragment::Image(_) | + Fragment::IFrame(_) | + Fragment::Anonymous(_) => return None, + }; + + bounding_box = fragment_relative_rect + .translate(containing_block.origin.to_vector()) + .union(&bounding_box); + None::<()> + }); + + Rect::new( + Point2D::new( + Au::from_f32_px(bounding_box.origin.x.px()), + Au::from_f32_px(bounding_box.origin.y.px()), + ), + Size2D::new( + Au::from_f32_px(bounding_box.size.width.px()), + Au::from_f32_px(bounding_box.size.height.px()), + ), + ) + } + + pub fn get_border_dimensions_for_node(&self, requested_node: OpaqueNode) -> Rect { + let tag_to_find = Tag::new(requested_node); + self.find(|fragment, _, containing_block| { + if fragment.tag() != Some(tag_to_find) { + return None; + } + + let (style, padding_rect) = match fragment { + Fragment::Box(fragment) => (&fragment.style, fragment.padding_rect()), + _ => return None, + }; + + // https://drafts.csswg.org/cssom-view/#dom-element-clienttop + // " If the element has no associated CSS layout box or if the + // CSS layout box is inline, return zero." For this check we + // also explicitly ignore the list item portion of the display + // style. + let display = &style.get_box().display; + if display.inside() == style::values::specified::box_::DisplayInside::Flow && + display.outside() == style::values::specified::box_::DisplayOutside::Inline + { + return Some(Rect::zero()); + } + + let padding_rect = padding_rect.to_physical(style.writing_mode, &containing_block); + let border = style.get_border(); + Some(Rect::new( + Point2D::new( + border.border_left_width.px() as i32, + border.border_top_width.px() as i32, + ), + Size2D::new( + padding_rect.size.width.px() as i32, + padding_rect.size.height.px() as i32, + ), + )) + }) + .unwrap_or_else(Rect::zero) + } + + pub fn get_scroll_area_for_node(&self, requested_node: OpaqueNode) -> Rect { + let mut scroll_area: PhysicalRect = PhysicalRect::zero(); + let tag_to_find = Tag::new(requested_node); + self.find(|fragment, _, containing_block| { + if fragment.tag() != Some(tag_to_find) { + return None::<()>; + } + + scroll_area = match fragment { + Fragment::Box(fragment) | Fragment::Float(fragment) => fragment + .scrollable_overflow(&containing_block) + .translate(containing_block.origin.to_vector()), + Fragment::Text(_) | + Fragment::AbsoluteOrFixedPositioned(_) | + Fragment::Image(_) | + Fragment::IFrame(_) | + Fragment::Anonymous(_) => return None, + }; + None::<()> + }); + + Rect::new( + Point2D::new( + scroll_area.origin.x.px() as i32, + scroll_area.origin.y.px() as i32, + ), + Size2D::new( + scroll_area.size.width.px() as i32, + scroll_area.size.height.px() as i32, + ), + ) + } +} diff --git a/components/layout_2020/fragment_tree/hoisted_shared_fragment.rs b/components/layout_2020/fragment_tree/hoisted_shared_fragment.rs new file mode 100644 index 00000000000..a9ccbd64e71 --- /dev/null +++ b/components/layout_2020/fragment_tree/hoisted_shared_fragment.rs @@ -0,0 +1,68 @@ +/* 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/. */ + +use super::Fragment; +use crate::{cell::ArcRefCell, geom::flow_relative::Vec2}; +use style::values::computed::{Length, LengthPercentage}; + +/// A reference to a Fragment which is shared between `HoistedAbsolutelyPositionedBox` +/// and its placeholder `AbsoluteOrFixedPositionedFragment` in the original tree position. +/// This will be used later in order to paint this hoisted box in tree order. +#[derive(Serialize)] +pub(crate) struct HoistedSharedFragment { + pub fragment: Option>, + pub box_offsets: Vec2, +} + +impl HoistedSharedFragment { + pub(crate) fn new(box_offsets: Vec2) -> Self { + HoistedSharedFragment { + fragment: None, + box_offsets, + } + } +} + +impl HoistedSharedFragment { + /// In some cases `inset: auto`-positioned elements do not know their precise + /// position until after they're hoisted. This lets us adjust auto values + /// after the fact. + pub(crate) fn adjust_offsets(&mut self, offsets: Vec2) { + self.box_offsets.inline.adjust_offset(offsets.inline); + self.box_offsets.block.adjust_offset(offsets.block); + } +} + +#[derive(Clone, Debug, Serialize)] +pub(crate) enum AbsoluteBoxOffsets { + StaticStart { + start: Length, + }, + Start { + start: LengthPercentage, + }, + End { + end: LengthPercentage, + }, + Both { + start: LengthPercentage, + end: LengthPercentage, + }, +} + +impl AbsoluteBoxOffsets { + pub(crate) fn both_specified(&self) -> bool { + match self { + AbsoluteBoxOffsets::Both { .. } => return true, + _ => return false, + } + } + + pub(crate) fn adjust_offset(&mut self, new_offset: Length) { + match *self { + AbsoluteBoxOffsets::StaticStart { ref mut start } => *start = new_offset, + _ => (), + } + } +} diff --git a/components/layout_2020/fragment_tree/mod.rs b/components/layout_2020/fragment_tree/mod.rs index 96f07217ac4..03e112d7adf 100644 --- a/components/layout_2020/fragment_tree/mod.rs +++ b/components/layout_2020/fragment_tree/mod.rs @@ -2,8 +2,16 @@ * 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/. */ -mod base; +mod base_fragment; +mod box_fragment; mod containing_block; +mod fragment; +mod fragment_tree; +mod hoisted_shared_fragment; -pub(crate) use base::*; +pub(crate) use base_fragment::*; +pub(crate) use box_fragment::*; pub(crate) use containing_block::*; +pub(crate) use fragment::*; +pub use fragment_tree::*; +pub(crate) use hoisted_shared_fragment::*; diff --git a/components/layout_2020/layout_debug.rs b/components/layout_2020/layout_debug.rs index 21636f32a89..549075c7595 100644 --- a/components/layout_2020/layout_debug.rs +++ b/components/layout_2020/layout_debug.rs @@ -5,7 +5,8 @@ //! Supports writing a trace file created during each layout scope //! that can be viewed by an external tool to make layout debugging easier. -use crate::flow::{BoxTree, FragmentTree}; +use crate::flow::BoxTree; +use crate::fragment_tree::FragmentTree; #[cfg(not(debug_assertions))] use serde::ser::{Serialize, Serializer}; use serde_json::{to_string, to_value, Value}; diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs index ce737c89258..91f4bf5ffc9 100644 --- a/components/layout_2020/lib.rs +++ b/components/layout_2020/lib.rs @@ -18,7 +18,6 @@ mod flexbox; pub mod flow; mod formatting_contexts; mod fragment_tree; -mod fragments; pub mod geom; #[macro_use] pub mod layout_debug; @@ -30,7 +29,8 @@ mod sizing; mod style_ext; pub mod traversal; -pub use flow::{BoxTree, FragmentTree}; +pub use flow::BoxTree; +pub use fragment_tree::FragmentTree; use crate::geom::flow_relative::Vec2; use style::properties::ComputedValues; diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs index 1b1c931477a..282046456e2 100644 --- a/components/layout_2020/positioned.rs +++ b/components/layout_2020/positioned.rs @@ -7,7 +7,9 @@ use crate::context::LayoutContext; use crate::dom::NodeExt; use crate::dom_traversal::{Contents, NodeAndStyleInfo}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragments::{BoxFragment, CollapsedBlockMargins, Fragment}; +use crate::fragment_tree::{ + AbsoluteBoxOffsets, BoxFragment, CollapsedBlockMargins, Fragment, HoistedSharedFragment, +}; use crate::geom::flow_relative::{Rect, Sides, Vec2}; use crate::geom::{LengthOrAuto, LengthPercentageOrAuto}; use crate::style_ext::{ComputedValuesExt, DisplayInside}; @@ -16,7 +18,7 @@ use rayon::iter::{IntoParallelRefMutIterator, ParallelExtend}; use rayon_croissant::ParallelIteratorExt; use style::computed_values::position::T as Position; use style::properties::ComputedValues; -use style::values::computed::{CSSPixelLength, Length, LengthPercentage}; +use style::values::computed::{CSSPixelLength, Length}; use style::values::specified::text::TextDecorationLine; use style::Zero; @@ -48,67 +50,6 @@ pub(crate) struct HoistedAbsolutelyPositionedBox { pub fragment: ArcRefCell, } -/// A reference to a Fragment which is shared between `HoistedAbsolutelyPositionedBox` -/// and its placeholder `AbsoluteOrFixedPositionedFragment` in the original tree position. -/// This will be used later in order to paint this hoisted box in tree order. -#[derive(Serialize)] -pub(crate) struct HoistedSharedFragment { - pub(crate) fragment: Option>, - pub(crate) box_offsets: Vec2, -} - -impl HoistedSharedFragment { - pub(crate) fn new(box_offsets: Vec2) -> Self { - HoistedSharedFragment { - fragment: None, - box_offsets, - } - } -} - -impl HoistedSharedFragment { - /// In some cases `inset: auto`-positioned elements do not know their precise - /// position until after they're hoisted. This lets us adjust auto values - /// after the fact. - pub(crate) fn adjust_offsets(&mut self, offsets: Vec2) { - self.box_offsets.inline.adjust_offset(offsets.inline); - self.box_offsets.block.adjust_offset(offsets.block); - } -} - -#[derive(Clone, Debug, Serialize)] -pub(crate) enum AbsoluteBoxOffsets { - StaticStart { - start: Length, - }, - Start { - start: LengthPercentage, - }, - End { - end: LengthPercentage, - }, - Both { - start: LengthPercentage, - end: LengthPercentage, - }, -} - -impl AbsoluteBoxOffsets { - fn both_specified(&self) -> bool { - match self { - AbsoluteBoxOffsets::Both { .. } => return true, - _ => return false, - } - } - - fn adjust_offset(&mut self, new_offset: Length) { - match *self { - AbsoluteBoxOffsets::StaticStart { ref mut start } => *start = new_offset, - _ => (), - } - } -} - impl AbsolutelyPositionedBox { pub fn construct<'dom>( context: &LayoutContext, diff --git a/components/layout_2020/query.rs b/components/layout_2020/query.rs index 7d19963b951..ed5e7f1b54c 100644 --- a/components/layout_2020/query.rs +++ b/components/layout_2020/query.rs @@ -4,9 +4,7 @@ //! Utilities for querying the layout, as needed by the layout thread. use crate::context::LayoutContext; -use crate::flow::FragmentTree; -use crate::fragment_tree::{FragmentFlags, Tag}; -use crate::fragments::Fragment; +use crate::fragment_tree::{Fragment, FragmentFlags, FragmentTree, Tag}; use app_units::Au; use euclid::default::{Point2D, Rect}; use euclid::Size2D; diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs index 48d06802edf..d350086c63a 100644 --- a/components/layout_2020/replaced.rs +++ b/components/layout_2020/replaced.rs @@ -4,8 +4,7 @@ use crate::context::LayoutContext; use crate::dom::NodeExt; -use crate::fragment_tree::BaseFragmentInfo; -use crate::fragments::{Fragment, IFrameFragment, ImageFragment}; +use crate::fragment_tree::{BaseFragmentInfo, Fragment, IFrameFragment, ImageFragment}; use crate::geom::flow_relative::{Rect, Vec2}; use crate::geom::PhysicalSize; use crate::sizing::ContentSizes;