aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/layout_2020/dom_traversal.rs35
-rw-r--r--components/layout_2020/flow/construct.rs252
-rw-r--r--components/layout_2020/flow/float.rs26
-rw-r--r--components/layout_2020/flow/inline.rs289
-rw-r--r--components/layout_2020/flow/mod.rs21
-rw-r--r--components/layout_2020/flow/root.rs37
-rw-r--r--components/layout_2020/formatting_contexts.rs57
-rw-r--r--components/layout_2020/lib.rs2
-rw-r--r--components/layout_2020/positioned.rs104
-rw-r--r--components/layout_2020/replaced.rs50
-rw-r--r--components/layout_2020/sizing.rs152
-rw-r--r--components/layout_2020/style_ext.rs36
-rw-r--r--components/layout_2020/traversal.rs4
-rw-r--r--components/layout_thread_2020/lib.rs4
-rw-r--r--components/style/values/computed/length.rs16
-rw-r--r--components/style/values/computed/percentage.rs6
-rw-r--r--components/style/values/generics/length.rs2
-rw-r--r--tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini2
-rw-r--r--tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini2
19 files changed, 824 insertions, 273 deletions
diff --git a/components/layout_2020/dom_traversal.rs b/components/layout_2020/dom_traversal.rs
index 1a3ec7f1c98..056103d7205 100644
--- a/components/layout_2020/dom_traversal.rs
+++ b/components/layout_2020/dom_traversal.rs
@@ -2,6 +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::context::LayoutContext;
use crate::element_data::{LayoutBox, LayoutDataForElement};
use crate::geom::physical::Vec2;
use crate::replaced::ReplacedContent;
@@ -13,7 +14,6 @@ use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
use servo_arc::Arc as ServoArc;
use std::marker::PhantomData as marker;
use std::sync::Arc;
-use style::context::SharedStyleContext;
use style::dom::TNode;
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
@@ -66,7 +66,7 @@ where
fn traverse_children_of<'dom, Node>(
parent_element: Node,
- context: &SharedStyleContext,
+ context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom, Node>,
) where
Node: NodeExt<'dom>,
@@ -88,7 +88,7 @@ fn traverse_children_of<'dom, Node>(
fn traverse_element<'dom, Node>(
element: Node,
- context: &SharedStyleContext,
+ context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom, Node>,
) where
Node: NodeExt<'dom>,
@@ -121,7 +121,7 @@ fn traverse_element<'dom, Node>(
fn traverse_pseudo_element<'dom, Node>(
which: WhichPseudoElement,
element: Node,
- context: &SharedStyleContext,
+ context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom, Node>,
) where
Node: NodeExt<'dom>,
@@ -146,7 +146,7 @@ fn traverse_pseudo_element<'dom, Node>(
fn traverse_pseudo_element_contents<'dom, Node>(
pseudo_element_style: &ServoArc<ComputedValues>,
- context: &SharedStyleContext,
+ context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom, Node>,
items: Vec<PseudoElementContentItem>,
) where
@@ -159,9 +159,10 @@ fn traverse_pseudo_element_contents<'dom, Node>(
PseudoElementContentItem::Replaced(contents) => {
let item_style = anonymous_style.get_or_insert_with(|| {
context
+ .shared_context()
.stylist
.style_for_anonymous::<Node::ConcreteElement>(
- &context.guards,
+ &context.shared_context().guards,
&PseudoElement::ServoText,
&pseudo_element_style,
)
@@ -187,6 +188,16 @@ fn traverse_pseudo_element_contents<'dom, Node>(
}
}
+impl<Node> Contents<Node> {
+ /// Returns true iff the `try_from` impl below would return `Err(_)`
+ pub fn is_replaced(&self) -> bool {
+ match self {
+ Contents::OfElement(_) | Contents::OfPseudoElement(_) => false,
+ Contents::Replaced(_) => true,
+ }
+ }
+}
+
impl<Node> std::convert::TryFrom<Contents<Node>> for NonReplacedContents<Node> {
type Error = ReplacedContent;
@@ -215,7 +226,7 @@ where
pub(crate) fn traverse(
self,
inherited_style: &ServoArc<ComputedValues>,
- context: &SharedStyleContext,
+ context: &LayoutContext,
handler: &mut impl TraversalHandler<'dom, Node>,
) {
match self {
@@ -230,7 +241,7 @@ where
fn pseudo_element_style<'dom, Node>(
_which: WhichPseudoElement,
_element: Node,
- _context: &SharedStyleContext,
+ _context: &LayoutContext,
) -> Option<ServoArc<ComputedValues>>
where
Node: NodeExt<'dom>,
@@ -243,7 +254,7 @@ where
fn generate_pseudo_element_content<'dom, Node>(
_pseudo_element_style: &ComputedValues,
_element: Node,
- _context: &SharedStyleContext,
+ _context: &LayoutContext,
) -> Vec<PseudoElementContentItem>
where
Node: NodeExt<'dom>,
@@ -292,7 +303,7 @@ pub(crate) trait NodeExt<'dom>: 'dom + Copy + LayoutNode + Send + Sync {
fn first_child(self) -> Option<Self>;
fn next_sibling(self) -> Option<Self>;
fn parent_node(self) -> Option<Self>;
- fn style(self, context: &SharedStyleContext) -> ServoArc<ComputedValues>;
+ fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues>;
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement>;
fn element_box_slot(&self) -> BoxSlot<'dom>;
@@ -349,8 +360,8 @@ where
TNode::parent_node(&self)
}
- fn style(self, context: &SharedStyleContext) -> ServoArc<ComputedValues> {
- self.to_threadsafe().style(context)
+ fn style(self, context: &LayoutContext) -> ServoArc<ComputedValues> {
+ self.to_threadsafe().style(context.shared_context())
}
fn layout_data_mut(&self) -> AtomicRefMut<LayoutDataForElement> {
diff --git a/components/layout_2020/flow/construct.rs b/components/layout_2020/flow/construct.rs
index e7599bc843a..c02dcd08fd7 100644
--- a/components/layout_2020/flow/construct.rs
+++ b/components/layout_2020/flow/construct.rs
@@ -2,6 +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::context::LayoutContext;
use crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler};
use crate::element_data::LayoutBox;
use crate::flow::float::FloatBox;
@@ -9,26 +10,31 @@ use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, Te
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::positioned::AbsolutelyPositionedBox;
-use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside};
+use crate::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest};
+use crate::style_ext::{ComputedValuesExt, DisplayGeneratingBox, DisplayInside, DisplayOutside};
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
-use std::convert::TryInto;
-use style::context::SharedStyleContext;
+use std::convert::{TryFrom, TryInto};
use style::properties::ComputedValues;
use style::selector_parser::PseudoElement;
impl BlockFormattingContext {
pub fn construct<'dom>(
- context: &SharedStyleContext<'_>,
+ context: &LayoutContext,
style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
- ) -> Self {
- let (contents, contains_floats) = BlockContainer::construct(context, style, contents);
- Self {
+ content_sizes: ContentSizesRequest,
+ ) -> (Self, BoxContentSizes) {
+ let (contents, contains_floats, inline_content_sizes) =
+ BlockContainer::construct(context, style, contents, content_sizes);
+ // FIXME: add contribution to `inline_content_sizes` of floats in this formatting context
+ // https://dbaron.org/css/intrinsic/#intrinsic
+ let bfc = Self {
contents,
contains_floats: contains_floats == ContainsFloats::Yes,
- }
+ };
+ (bfc, inline_content_sizes)
}
}
@@ -71,7 +77,7 @@ enum IntermediateBlockContainer<Node> {
/// This builder starts from the first child of a given DOM node
/// and does a preorder traversal of all of its inclusive siblings.
struct BlockContainerBuilder<'dom, 'style, Node> {
- context: &'style SharedStyleContext<'style>,
+ context: &'style LayoutContext<'style>,
block_container_style: &'style Arc<ComputedValues>,
@@ -123,19 +129,20 @@ struct BlockContainerBuilder<'dom, 'style, Node> {
}
impl BlockContainer {
- pub fn construct<'dom, 'style>(
- context: &SharedStyleContext<'style>,
+ pub fn construct<'dom>(
+ context: &LayoutContext,
block_container_style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
- ) -> (BlockContainer, ContainsFloats) {
+ content_sizes: ContentSizesRequest,
+ ) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
let mut builder = BlockContainerBuilder {
context,
block_container_style,
- block_level_boxes: Default::default(),
- ongoing_inline_formatting_context: Default::default(),
- ongoing_inline_boxes_stack: Default::default(),
- anonymous_style: Default::default(),
- contains_floats: Default::default(),
+ block_level_boxes: Vec::new(),
+ ongoing_inline_formatting_context: InlineFormattingContext::default(),
+ ongoing_inline_boxes_stack: Vec::new(),
+ anonymous_style: None,
+ contains_floats: ContainsFloats::No,
};
contents.traverse(block_container_style, context, &mut builder);
@@ -148,32 +155,65 @@ impl BlockContainer {
.is_empty()
{
if builder.block_level_boxes.is_empty() {
+ let content_sizes = content_sizes.compute(|| {
+ builder
+ .ongoing_inline_formatting_context
+ .inline_content_sizes(context)
+ });
let container = BlockContainer::InlineFormattingContext(
builder.ongoing_inline_formatting_context,
);
- return (container, builder.contains_floats);
+ return (container, builder.contains_floats, content_sizes);
}
builder.end_ongoing_inline_formatting_context();
}
- let mut contains_floats = builder.contains_floats;
- let container = BlockContainer::BlockLevelBoxes(
- builder
- .block_level_boxes
- .into_par_iter()
- .mapfold_reduce_into(
- &mut contains_floats,
- |contains_floats, (intermediate, box_slot): (IntermediateBlockLevelBox<_>, BoxSlot<'_>)| {
- let (block_level_box, box_contains_floats) = intermediate.finish(context);
- *contains_floats |= box_contains_floats;
- box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
- block_level_box
- },
- |left, right| *left |= right,
- )
- .collect(),
+ type Intermediate<Node> = IntermediateBlockLevelBox<Node>;
+ struct Target {
+ contains_floats: ContainsFloats,
+ outer_content_sizes_of_children: ContentSizes,
+ }
+ impl Default for Target {
+ fn default() -> Self {
+ Self {
+ contains_floats: ContainsFloats::No,
+ outer_content_sizes_of_children: ContentSizes::zero(),
+ }
+ }
+ }
+ let mut target = Target {
+ contains_floats: builder.contains_floats,
+ outer_content_sizes_of_children: ContentSizes::zero(),
+ };
+ let iter = builder.block_level_boxes.into_par_iter();
+ let iter = iter.mapfold_reduce_into(
+ &mut target,
+ |target, (intermediate, box_slot): (Intermediate<_>, BoxSlot<'_>)| {
+ let (block_level_box, box_contains_floats) = intermediate.finish(
+ context,
+ content_sizes
+ .if_requests_inline(|| &mut target.outer_content_sizes_of_children),
+ );
+ target.contains_floats |= box_contains_floats;
+ box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
+ block_level_box
+ },
+ |left, right| {
+ left.contains_floats |= right.contains_floats;
+ if content_sizes.requests_inline() {
+ left.outer_content_sizes_of_children
+ .max_assign(&right.outer_content_sizes_of_children)
+ }
+ },
);
- (container, contains_floats)
+ let container = BlockContainer::BlockLevelBoxes(iter.collect());
+
+ let Target {
+ contains_floats,
+ outer_content_sizes_of_children,
+ } = target;
+ let content_sizes = content_sizes.compute(|| outer_content_sizes_of_children);
+ (container, contains_floats, content_sizes)
}
}
@@ -324,38 +364,38 @@ where
display_inside: DisplayInside,
contents: Contents<Node>,
) -> Arc<InlineLevelBox> {
- let box_ = match contents.try_into() {
- Err(replaced) => Arc::new(InlineLevelBox::Atomic(
+ let box_ = if display_inside == DisplayInside::Flow && !contents.is_replaced() {
+ // We found un inline box.
+ // Whatever happened before, all we need to do before recurring
+ // is to remember this ongoing inline level box.
+ self.ongoing_inline_boxes_stack.push(InlineBox {
+ style: style.clone(),
+ first_fragment: true,
+ last_fragment: false,
+ children: vec![],
+ });
+
+ // `unwrap` doesn’t panic here because `is_replaced` returned `false`.
+ NonReplacedContents::try_from(contents)
+ .unwrap()
+ .traverse(&style, self.context, self);
+
+ let mut inline_box = self
+ .ongoing_inline_boxes_stack
+ .pop()
+ .expect("no ongoing inline level box found");
+ inline_box.last_fragment = true;
+ Arc::new(InlineLevelBox::InlineBox(inline_box))
+ } else {
+ Arc::new(InlineLevelBox::Atomic(
IndependentFormattingContext::construct(
self.context,
style.clone(),
display_inside,
- <Contents<Node>>::Replaced(replaced),
+ contents,
+ ContentSizesRequest::inline_if(style.inline_size_is_auto()),
),
- )),
- Ok(non_replaced) => match display_inside {
- DisplayInside::Flow |
- // TODO: Properly implement display: inline-block.
- DisplayInside::FlowRoot => {
- // Whatever happened before, we just found an inline level element, so
- // all we need to do is to remember this ongoing inline level box.
- self.ongoing_inline_boxes_stack.push(InlineBox {
- style: style.clone(),
- first_fragment: true,
- last_fragment: false,
- children: vec![],
- });
-
- NonReplacedContents::traverse(non_replaced, &style, self.context, self);
-
- let mut inline_box = self
- .ongoing_inline_boxes_stack
- .pop()
- .expect("no ongoing inline level box found");
- inline_box.last_fragment = true;
- Arc::new(InlineLevelBox::InlineBox(inline_box))
- },
- },
+ ))
};
self.current_inline_level_boxes().push(box_.clone());
box_
@@ -451,14 +491,7 @@ where
self.block_level_boxes.push((box_, box_slot));
} else {
let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(
- AbsolutelyPositionedBox {
- contents: IndependentFormattingContext::construct(
- self.context,
- style,
- display_inside,
- contents,
- ),
- },
+ AbsolutelyPositionedBox::construct(self.context, style, display_inside, contents),
));
self.current_inline_level_boxes().push(box_.clone());
box_slot.set(LayoutBox::InlineLevel(box_))
@@ -482,14 +515,12 @@ where
};
self.block_level_boxes.push((box_, box_slot));
} else {
- let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox {
- contents: IndependentFormattingContext::construct(
- self.context,
- style,
- display_inside,
- contents,
- ),
- }));
+ let box_ = Arc::new(InlineLevelBox::OutOfFlowFloatBox(FloatBox::construct(
+ self.context,
+ style,
+ display_inside,
+ contents,
+ )));
self.current_inline_level_boxes().push(box_.clone());
box_slot.set(LayoutBox::InlineLevel(box_))
}
@@ -509,9 +540,10 @@ where
let block_container_style = self.block_container_style;
let anonymous_style = self.anonymous_style.get_or_insert_with(|| {
context
+ .shared_context()
.stylist
.style_for_anonymous::<Node::ConcreteElement>(
- &context.guards,
+ &context.shared_context().guards,
&PseudoElement::ServoText,
&block_container_style,
)
@@ -546,13 +578,24 @@ impl<'dom, Node> IntermediateBlockLevelBox<Node>
where
Node: NodeExt<'dom>,
{
- fn finish<'style>(
+ fn finish(
self,
- context: &SharedStyleContext<'style>,
+ context: &LayoutContext,
+ max_assign_in_flow_outer_content_sizes_to: Option<&mut ContentSizes>,
) -> (Arc<BlockLevelBox>, ContainsFloats) {
match self {
IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => {
- let (contents, contains_floats) = contents.finish(context, &style);
+ let (contents, contains_floats, box_content_sizes) = contents.finish(
+ context,
+ &style,
+ ContentSizesRequest::inline_if(
+ max_assign_in_flow_outer_content_sizes_to.is_some() &&
+ style.inline_size_is_auto(),
+ ),
+ );
+ if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
+ to.max_assign(&box_content_sizes.outer_inline(&style))
+ }
let block_level_box =
Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style });
(block_level_box, contains_floats)
@@ -562,12 +605,20 @@ where
display_inside,
contents,
} => {
+ let content_sizes = ContentSizesRequest::inline_if(
+ max_assign_in_flow_outer_content_sizes_to.is_some() &&
+ style.inline_size_is_auto(),
+ );
let contents = IndependentFormattingContext::construct(
context,
style,
display_inside,
contents,
+ content_sizes,
);
+ if let Some(to) = max_assign_in_flow_outer_content_sizes_to {
+ to.max_assign(&contents.content_sizes.outer_inline(&contents.style))
+ }
(
Arc::new(BlockLevelBox::Independent(contents)),
ContainsFloats::No,
@@ -579,14 +630,7 @@ where
contents,
} => {
let block_level_box = Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
- AbsolutelyPositionedBox {
- contents: IndependentFormattingContext::construct(
- context,
- style,
- display_inside,
- contents,
- ),
- },
+ AbsolutelyPositionedBox::construct(context, style, display_inside, contents),
));
(block_level_box, ContainsFloats::No)
},
@@ -595,14 +639,9 @@ where
display_inside,
contents,
} => {
- let contents = IndependentFormattingContext::construct(
- context,
- style,
- display_inside,
- contents,
- );
- let block_level_box =
- Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox { contents }));
+ let block_level_box = Arc::new(BlockLevelBox::OutOfFlowFloatBox(
+ FloatBox::construct(context, style, display_inside, contents),
+ ));
(block_level_box, ContainsFloats::Yes)
},
}
@@ -613,22 +652,25 @@ impl<'dom, Node> IntermediateBlockContainer<Node>
where
Node: NodeExt<'dom>,
{
- fn finish<'style>(
+ fn finish(
self,
- context: &SharedStyleContext<'style>,
+ context: &LayoutContext,
style: &Arc<ComputedValues>,
- ) -> (BlockContainer, ContainsFloats) {
+ content_sizes: ContentSizesRequest,
+ ) -> (BlockContainer, ContainsFloats, BoxContentSizes) {
match self {
IntermediateBlockContainer::Deferred { contents } => {
- BlockContainer::construct(context, style, contents)
+ BlockContainer::construct(context, style, contents, content_sizes)
},
IntermediateBlockContainer::InlineFormattingContext(ifc) => {
+ let content_sizes = content_sizes.compute(|| ifc.inline_content_sizes(context));
// If that inline formatting context contained any float, those
// were already taken into account during the first phase of
// box construction.
(
BlockContainer::InlineFormattingContext(ifc),
ContainsFloats::No,
+ content_sizes,
)
},
}
@@ -648,9 +690,3 @@ impl std::ops::BitOrAssign for ContainsFloats {
}
}
}
-
-impl Default for ContainsFloats {
- fn default() -> Self {
- ContainsFloats::No
- }
-}
diff --git a/components/layout_2020/flow/float.rs b/components/layout_2020/flow/float.rs
index f9be366bc97..2acc2095004 100644
--- a/components/layout_2020/flow/float.rs
+++ b/components/layout_2020/flow/float.rs
@@ -2,7 +2,13 @@
* 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::context::LayoutContext;
+use crate::dom_traversal::{Contents, NodeExt};
use crate::formatting_contexts::IndependentFormattingContext;
+use crate::sizing::ContentSizesRequest;
+use crate::style_ext::{ComputedValuesExt, DisplayInside};
+use servo_arc::Arc;
+use style::properties::ComputedValues;
#[derive(Debug)]
pub(crate) struct FloatBox {
@@ -19,3 +25,23 @@ impl FloatContext {
FloatContext {}
}
}
+
+impl FloatBox {
+ pub fn construct<'dom>(
+ context: &LayoutContext,
+ style: Arc<ComputedValues>,
+ display_inside: DisplayInside,
+ contents: Contents<impl NodeExt<'dom>>,
+ ) -> Self {
+ let content_sizes = ContentSizesRequest::inline_if(style.inline_size_is_auto());
+ Self {
+ contents: IndependentFormattingContext::construct(
+ context,
+ style,
+ display_inside,
+ contents,
+ content_sizes,
+ ),
+ }
+ }
+}
diff --git a/components/layout_2020/flow/inline.rs b/components/layout_2020/flow/inline.rs
index 33d1d8a0ed9..4c6e364a8fa 100644
--- a/components/layout_2020/flow/inline.rs
+++ b/components/layout_2020/flow/inline.rs
@@ -10,12 +10,16 @@ use crate::fragments::CollapsedBlockMargins;
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
+use crate::sizing::ContentSizes;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
use crate::{relative_adjustement, ContainingBlock};
+use app_units::Au;
+use gfx::text::text_run::GlyphRun;
use servo_arc::Arc;
use style::properties::ComputedValues;
-use style::values::computed::Length;
+use style::values::computed::{Length, LengthPercentage, Percentage};
use style::Zero;
+use webrender_api::FontInstanceKey;
#[derive(Debug, Default)]
pub(crate) struct InlineFormattingContext {
@@ -63,8 +67,9 @@ struct PartialInlineBoxFragment<'box_tree> {
parent_nesting_level: InlineNestingLevelState<'box_tree>,
}
-struct InlineFormattingContextState<'box_tree, 'cb> {
- containing_block: &'cb ContainingBlock,
+struct InlineFormattingContextState<'box_tree, 'a> {
+ absolutely_positioned_fragments: &'a mut Vec<AbsolutelyPositionedFragment<'box_tree>>,
+ containing_block: &'a ContainingBlock,
line_boxes: LinesBoxes,
inline_position: Length,
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
@@ -77,6 +82,114 @@ struct LinesBoxes {
}
impl InlineFormattingContext {
+ // This works on an already-constructed `InlineFormattingContext`,
+ // Which would have to change if/when
+ // `BlockContainer::construct` parallelize their construction.
+ pub(super) fn inline_content_sizes(&self, layout_context: &LayoutContext) -> ContentSizes {
+ struct Computation {
+ paragraph: ContentSizes,
+ current_line: ContentSizes,
+ current_line_percentages: Percentage,
+ }
+ impl Computation {
+ fn traverse(
+ &mut self,
+ layout_context: &LayoutContext,
+ inline_level_boxes: &[Arc<InlineLevelBox>],
+ ) {
+ for inline_level_box in inline_level_boxes {
+ match &**inline_level_box {
+ InlineLevelBox::InlineBox(inline_box) => {
+ let padding = inline_box.style.padding();
+ let border = inline_box.style.border_width();
+ let margin = inline_box.style.margin();
+ macro_rules! add {
+ ($condition: ident, $side: ident) => {
+ if inline_box.$condition {
+ self.add_lengthpercentage(padding.$side);
+ self.add_length(border.$side);
+ if let Some(lp) = margin.$side.non_auto() {
+ self.add_lengthpercentage(lp)
+ }
+ }
+ };
+ }
+
+ add!(first_fragment, inline_start);
+ self.traverse(layout_context, &inline_box.children);
+ add!(last_fragment, inline_end);
+ },
+ InlineLevelBox::TextRun(text_run) => {
+ let BreakAndShapeResult {
+ runs,
+ break_at_start,
+ ..
+ } = text_run.break_and_shape(layout_context);
+ if break_at_start {
+ self.line_break_opportunity()
+ }
+ for run in &runs {
+ let advance = Length::from(run.glyph_store.total_advance());
+ if run.glyph_store.is_whitespace() {
+ self.line_break_opportunity()
+ } else {
+ self.current_line.min_content += advance
+ }
+ self.current_line.max_content += advance
+ }
+ },
+ InlineLevelBox::Atomic(atomic) => {
+ let (outer, pc) = atomic
+ .content_sizes
+ .outer_inline_and_percentages(&atomic.style);
+ self.current_line.min_content += outer.min_content;
+ self.current_line.max_content += outer.max_content;
+ self.current_line_percentages += pc;
+ },
+ InlineLevelBox::OutOfFlowFloatBox(_) |
+ InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
+ }
+ }
+ }
+
+ fn add_lengthpercentage(&mut self, lp: LengthPercentage) {
+ self.add_length(lp.length_component());
+ self.current_line_percentages += lp.percentage_component();
+ }
+
+ fn add_length(&mut self, l: Length) {
+ self.current_line.min_content += l;
+ self.current_line.max_content += l;
+ }
+
+ fn line_break_opportunity(&mut self) {
+ self.paragraph
+ .min_content
+ .max_assign(take(&mut self.current_line.min_content));
+ }
+
+ fn forced_line_break(&mut self) {
+ self.line_break_opportunity();
+ self.current_line
+ .adjust_for_pbm_percentages(take(&mut self.current_line_percentages));
+ self.paragraph
+ .max_content
+ .max_assign(take(&mut self.current_line.max_content));
+ }
+ }
+ fn take<T: Zero>(x: &mut T) -> T {
+ std::mem::replace(x, T::zero())
+ }
+ let mut computation = Computation {
+ paragraph: ContentSizes::zero(),
+ current_line: ContentSizes::zero(),
+ current_line_percentages: Percentage::zero(),
+ };
+ computation.traverse(layout_context, &self.inline_level_boxes);
+ computation.forced_line_break();
+ computation.paragraph
+ }
+
pub(super) fn layout<'a>(
&'a self,
layout_context: &LayoutContext,
@@ -85,6 +198,7 @@ impl InlineFormattingContext {
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
) -> FlowLayout {
let mut ifc = InlineFormattingContextState {
+ absolutely_positioned_fragments,
containing_block,
partial_inline_boxes_stack: Vec::new(),
line_boxes: LinesBoxes {
@@ -107,10 +221,7 @@ impl InlineFormattingContext {
ifc.partial_inline_boxes_stack.push(partial)
},
InlineLevelBox::TextRun(run) => run.layout(layout_context, &mut ifc),
- InlineLevelBox::Atomic(_independent) => {
- // TODO
- continue;
- },
+ InlineLevelBox::Atomic(a) => layout_atomic(layout_context, &mut ifc, a),
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
let initial_start_corner =
match Display::from(box_.contents.style.get_box().original_display) {
@@ -131,12 +242,11 @@ impl InlineFormattingContext {
panic!("display:none does not generate an abspos box")
},
};
- absolutely_positioned_fragments
+ ifc.absolutely_positioned_fragments
.push(box_.layout(initial_start_corner, tree_rank));
},
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
// TODO
- continue;
},
}
} else
@@ -282,12 +392,114 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}
}
+fn layout_atomic<'box_tree>(
+ layout_context: &LayoutContext,
+ ifc: &mut InlineFormattingContextState<'box_tree, '_>,
+ atomic: &'box_tree IndependentFormattingContext,
+) {
+ let cbis = ifc.containing_block.inline_size;
+ let padding = atomic.style.padding().percentages_relative_to(cbis);
+ let border = atomic.style.border_width();
+ let margin = atomic
+ .style
+ .margin()
+ .percentages_relative_to(cbis)
+ .auto_is(Length::zero);
+ let pbm = &(&padding + &border) + &margin;
+ ifc.inline_position += pbm.inline_start;
+ let mut start_corner = Vec2 {
+ block: pbm.block_start,
+ inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
+ };
+ start_corner += &relative_adjustement(
+ &atomic.style,
+ ifc.containing_block.inline_size,
+ ifc.containing_block.block_size,
+ );
+
+ let fragment = match atomic.as_replaced() {
+ Ok(replaced) => {
+ // FIXME: implement https://drafts.csswg.org/css2/visudet.html#inline-replaced-width
+ // and https://drafts.csswg.org/css2/visudet.html#inline-replaced-height
+ let size = Vec2::zero();
+ let fragments = replaced.make_fragments(&atomic.style, size.clone());
+ let content_rect = Rect { start_corner, size };
+ BoxFragment {
+ style: atomic.style.clone(),
+ children: fragments,
+ content_rect,
+ padding,
+ border,
+ margin,
+ block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
+ }
+ },
+ Err(non_replaced) => {
+ let box_size = atomic.style.box_size();
+ let inline_size = box_size.inline.percentage_relative_to(cbis).auto_is(|| {
+ let available_size = cbis - pbm.inline_sum();
+ atomic.content_sizes.shrink_to_fit(available_size)
+ });
+ let block_size = box_size
+ .block
+ .maybe_percentage_relative_to(ifc.containing_block.block_size.non_auto());
+ let containing_block_for_children = ContainingBlock {
+ inline_size,
+ block_size,
+ mode: atomic.style.writing_mode(),
+ };
+ assert_eq!(
+ ifc.containing_block.mode, containing_block_for_children.mode,
+ "Mixed writing modes are not supported yet"
+ );
+ // FIXME is this correct?
+ let dummy_tree_rank = 0;
+ // FIXME: Do we need to call `adjust_static_positions` somewhere near here?
+ let independent_layout = non_replaced.layout(
+ layout_context,
+ &containing_block_for_children,
+ dummy_tree_rank,
+ ifc.absolutely_positioned_fragments,
+ );
+ let block_size = block_size.auto_is(|| independent_layout.content_block_size);
+ let content_rect = Rect {
+ start_corner,
+ size: Vec2 {
+ block: block_size,
+ inline: inline_size,
+ },
+ };
+ BoxFragment {
+ style: atomic.style.clone(),
+ children: independent_layout.fragments,
+ content_rect,
+ padding,
+ border,
+ margin,
+ block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
+ }
+ },
+ };
+
+ ifc.inline_position += pbm.inline_end;
+ ifc.current_nesting_level
+ .fragments_so_far
+ .push(Fragment::Box(fragment));
+}
+
+struct BreakAndShapeResult {
+ font_ascent: Au,
+ font_line_gap: Au,
+ font_key: FontInstanceKey,
+ runs: Vec<GlyphRun>,
+ break_at_start: bool,
+}
+
impl TextRun {
- fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
+ fn break_and_shape(&self, layout_context: &LayoutContext) -> BreakAndShapeResult {
use gfx::font::ShapingFlags;
use style::computed_values::text_rendering::T as TextRendering;
use style::computed_values::word_break::T as WordBreak;
- use style::values::generics::text::LineHeight;
let font_style = self.parent_style.clone_font();
let inherited_text_style = self.parent_style.get_inherited_text();
@@ -316,30 +528,41 @@ impl TextRun {
flags,
};
- let (font_ascent, font_line_gap, font_key, runs) =
- crate::context::with_thread_local_font_context(layout_context, |font_context| {
- let font_group = font_context.font_group(font_style);
- let font = font_group
- .borrow_mut()
- .first(font_context)
- .expect("could not find font");
- let mut font = font.borrow_mut();
-
- let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
- &mut font,
- &self.text,
- &shaping_options,
- &mut None,
- );
+ crate::context::with_thread_local_font_context(layout_context, |font_context| {
+ let font_group = font_context.font_group(font_style);
+ let font = font_group
+ .borrow_mut()
+ .first(font_context)
+ .expect("could not find font");
+ let mut font = font.borrow_mut();
- (
- font.metrics.ascent,
- font.metrics.line_gap,
- font.font_key,
- runs,
- )
- });
+ let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
+ &mut font,
+ &self.text,
+ &shaping_options,
+ &mut None,
+ );
+
+ BreakAndShapeResult {
+ font_ascent: font.metrics.ascent,
+ font_line_gap: font.metrics.line_gap,
+ font_key: font.font_key,
+ runs,
+ break_at_start,
+ }
+ })
+ }
+
+ fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
+ use style::values::generics::text::LineHeight;
+ let BreakAndShapeResult {
+ font_ascent,
+ font_line_gap,
+ font_key,
+ runs,
+ break_at_start: _,
+ } = self.break_and_shape(layout_context);
let font_size = self.parent_style.get_font().font_size.size.0;
let mut runs = runs.iter();
loop {
diff --git a/components/layout_2020/flow/mod.rs b/components/layout_2020/flow/mod.rs
index d7b9854668a..cbbefdabb1b 100644
--- a/components/layout_2020/flow/mod.rs
+++ b/components/layout_2020/flow/mod.rs
@@ -656,17 +656,11 @@ fn layout_in_flow_replaced_block_level<'a>(
block_start: computed_margin.block_start.auto_is(Length::zero),
block_end: computed_margin.block_end.auto_is(Length::zero),
};
- let containing_block_for_children = ContainingBlock {
- inline_size,
- block_size: LengthOrAuto::LengthPercentage(block_size),
- mode,
+ let size = Vec2 {
+ block: block_size,
+ inline: inline_size,
};
- // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
- assert_eq!(
- containing_block.mode, containing_block_for_children.mode,
- "Mixed writing modes are not supported yet"
- );
- let independent_layout = replaced.layout(style, &containing_block_for_children);
+ let fragments = replaced.make_fragments(style, size.clone());
let relative_adjustement = relative_adjustement(
style,
inline_size,
@@ -677,14 +671,11 @@ fn layout_in_flow_replaced_block_level<'a>(
block: pb.block_start + relative_adjustement.block,
inline: pb.inline_start + relative_adjustement.inline + margin.inline_start,
},
- size: Vec2 {
- block: block_size,
- inline: inline_size,
- },
+ size,
};
BoxFragment {
style: style.clone(),
- children: independent_layout.fragments,
+ children: fragments,
content_rect,
padding,
border,
diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs
index 7c062f12f47..6396b4dd658 100644
--- a/components/layout_2020/flow/root.rs
+++ b/components/layout_2020/flow/root.rs
@@ -14,12 +14,12 @@ use crate::geom;
use crate::geom::flow_relative::Vec2;
use crate::positioned::AbsolutelyPositionedBox;
use crate::replaced::ReplacedContent;
+use crate::sizing::ContentSizesRequest;
use crate::style_ext::{Direction, Display, DisplayGeneratingBox, DisplayInside, WritingMode};
use crate::{ContainingBlock, DefiniteContainingBlock};
use rayon::iter::{IntoParallelRefIterator, ParallelExtend, ParallelIterator};
use script_layout_interface::wrapper_traits::LayoutNode;
use servo_arc::Arc;
-use style::context::SharedStyleContext;
use style::values::computed::{Length, LengthOrAuto};
use style::Zero;
use style_traits::CSSPixel;
@@ -28,7 +28,7 @@ pub struct BoxTreeRoot(BlockFormattingContext);
pub struct FragmentTreeRoot(Vec<Fragment>);
impl BoxTreeRoot {
- pub fn construct<'dom, Node>(context: &SharedStyleContext<'_>, root_element: Node) -> Self
+ pub fn construct<'dom, Node>(context: &LayoutContext, root_element: Node) -> Self
where
Node: 'dom + Copy + LayoutNode + Send + Sync,
{
@@ -41,7 +41,7 @@ impl BoxTreeRoot {
}
fn construct_for_root_element<'dom>(
- context: &SharedStyleContext<'_>,
+ context: &LayoutContext,
root_element: impl NodeExt<'dom>,
) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) {
let style = root_element.style(context);
@@ -60,32 +60,33 @@ fn construct_for_root_element<'dom>(
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
};
- let position = box_style.position;
- let float = box_style.float;
- let contents = IndependentFormattingContext::construct(
- context,
- style,
- display_inside,
- replaced.map_or(Contents::OfElement(root_element), Contents::Replaced),
- );
- if position.is_absolutely_positioned() {
+ let contents = replaced.map_or(Contents::OfElement(root_element), Contents::Replaced);
+ if box_style.position.is_absolutely_positioned() {
(
ContainsFloats::No,
vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
- AbsolutelyPositionedBox { contents },
+ AbsolutelyPositionedBox::construct(context, style, display_inside, contents),
))],
)
- } else if float.is_floating() {
+ } else if box_style.float.is_floating() {
(
ContainsFloats::Yes,
- vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox {
- contents,
- }))],
+ vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(
+ FloatBox::construct(context, style, display_inside, contents),
+ ))],
)
} else {
(
ContainsFloats::No,
- vec![Arc::new(BlockLevelBox::Independent(contents))],
+ vec![Arc::new(BlockLevelBox::Independent(
+ IndependentFormattingContext::construct(
+ context,
+ style,
+ display_inside,
+ contents,
+ ContentSizesRequest::None,
+ ),
+ ))],
)
}
}
diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs
index 0517f599852..d7477182437 100644
--- a/components/layout_2020/formatting_contexts.rs
+++ b/components/layout_2020/formatting_contexts.rs
@@ -8,11 +8,11 @@ use crate::flow::BlockFormattingContext;
use crate::fragments::Fragment;
use crate::positioned::AbsolutelyPositionedFragment;
use crate::replaced::ReplacedContent;
+use crate::sizing::{BoxContentSizes, ContentSizesRequest};
use crate::style_ext::DisplayInside;
use crate::ContainingBlock;
use servo_arc::Arc;
use std::convert::TryInto;
-use style::context::SharedStyleContext;
use style::properties::ComputedValues;
use style::values::computed::Length;
@@ -20,6 +20,10 @@ use style::values::computed::Length;
#[derive(Debug)]
pub(crate) struct IndependentFormattingContext {
pub style: Arc<ComputedValues>,
+
+ /// If it was requested during construction
+ pub content_sizes: BoxContentSizes,
+
contents: IndependentFormattingContextContents,
}
@@ -46,22 +50,39 @@ enum NonReplacedIFCKind<'a> {
}
impl IndependentFormattingContext {
- pub fn construct<'dom, 'style>(
- context: &SharedStyleContext<'style>,
+ pub fn construct<'dom>(
+ context: &LayoutContext,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents<impl NodeExt<'dom>>,
+ content_sizes: ContentSizesRequest,
) -> Self {
use self::IndependentFormattingContextContents as Contents;
- let contents = match contents.try_into() {
+ let (contents, content_sizes) = match contents.try_into() {
Ok(non_replaced) => match display_inside {
- DisplayInside::Flow | DisplayInside::FlowRoot => Contents::Flow(
- BlockFormattingContext::construct(context, &style, non_replaced),
- ),
+ DisplayInside::Flow | DisplayInside::FlowRoot => {
+ let (bfc, box_content_sizes) = BlockFormattingContext::construct(
+ context,
+ &style,
+ non_replaced,
+ content_sizes,
+ );
+ (Contents::Flow(bfc), box_content_sizes)
+ },
+ },
+ Err(replaced) => {
+ // The `content_sizes` field is not used by layout code:
+ (
+ Contents::Replaced(replaced),
+ BoxContentSizes::NoneWereRequested,
+ )
},
- Err(replaced) => Contents::Replaced(replaced),
};
- Self { style, contents }
+ Self {
+ style,
+ contents,
+ content_sizes,
+ }
}
pub fn as_replaced(&self) -> Result<&ReplacedContent, NonReplacedIFC> {
@@ -73,24 +94,6 @@ impl IndependentFormattingContext {
Contents::Flow(f) => Err(NR(Kind::Flow(f))),
}
}
-
- pub fn layout<'a>(
- &'a self,
- layout_context: &LayoutContext,
- containing_block: &ContainingBlock,
- tree_rank: usize,
- absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
- ) -> IndependentLayout {
- match self.as_replaced() {
- Ok(replaced) => replaced.layout(&self.style, containing_block),
- Err(ifc) => ifc.layout(
- layout_context,
- containing_block,
- tree_rank,
- absolutely_positioned_fragments,
- ),
- }
- }
}
impl<'a> NonReplacedIFC<'a> {
diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs
index 80254421e66..dd589e1819b 100644
--- a/components/layout_2020/lib.rs
+++ b/components/layout_2020/lib.rs
@@ -4,6 +4,7 @@
#![deny(unsafe_code)]
#![feature(exact_size_is_empty)]
+#![feature(matches_macro)]
pub mod context;
pub mod data;
@@ -18,6 +19,7 @@ mod opaque_node;
mod positioned;
pub mod query;
mod replaced;
+mod sizing;
mod style_ext;
pub mod traversal;
pub mod wrapper;
diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs
index 3577a86642b..06d88709165 100644
--- a/components/layout_2020/positioned.rs
+++ b/components/layout_2020/positioned.rs
@@ -3,12 +3,16 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::context::LayoutContext;
+use crate::dom_traversal::{Contents, NodeExt};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
-use crate::style_ext::{ComputedValuesExt, Direction, WritingMode};
+use crate::sizing::ContentSizesRequest;
+use crate::style_ext::{ComputedValuesExt, Direction, DisplayInside, WritingMode};
use crate::{ContainingBlock, DefiniteContainingBlock};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
+use servo_arc::Arc;
+use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero;
@@ -42,6 +46,32 @@ pub(crate) enum AbsoluteBoxOffsets<NonStatic> {
}
impl AbsolutelyPositionedBox {
+ pub fn construct<'dom>(
+ context: &LayoutContext,
+ style: Arc<ComputedValues>,
+ display_inside: DisplayInside,
+ contents: Contents<impl NodeExt<'dom>>,
+ ) -> Self {
+ // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
+ let content_sizes = ContentSizesRequest::inline_if(
+ // If inline-size is non-auto, that value is used without shrink-to-fit
+ style.inline_size_is_auto() &&
+ // If it is, then the only case where shrink-to-fit is *not* used is
+ // if both offsets are non-auto, leaving inline-size as the only variable
+ // in the constraint equation.
+ !style.inline_box_offsets_are_both_non_auto(),
+ );
+ Self {
+ contents: IndependentFormattingContext::construct(
+ context,
+ style,
+ display_inside,
+ contents,
+ content_sizes,
+ ),
+ }
+ }
+
pub(crate) fn layout<'a>(
&'a self,
initial_start_corner: Vec2<Length>,
@@ -256,28 +286,60 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
};
- // FIXME(nox): shrink-to-fit.
- available_size
+ if self
+ .absolutely_positioned_box
+ .contents
+ .as_replaced()
+ .is_ok()
+ {
+ // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
+ available_size
+ } else {
+ self.absolutely_positioned_box
+ .contents
+ .content_sizes
+ .shrink_to_fit(available_size)
+ }
});
- let containing_block_for_children = ContainingBlock {
- inline_size,
- block_size,
- mode: style.writing_mode(),
+ let mut absolutely_positioned_fragments = Vec::new();
+ let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() {
+ Ok(replaced) => {
+ // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
+ // and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
+ let block_size = block_size.auto_is(Length::zero);
+ let fragments = replaced.make_fragments(
+ &self.absolutely_positioned_box.contents.style,
+ Vec2 {
+ inline: inline_size,
+ block: block_size,
+ },
+ );
+ crate::formatting_contexts::IndependentLayout {
+ fragments,
+ content_block_size: block_size,
+ }
+ },
+ Err(non_replaced) => {
+ let containing_block_for_children = ContainingBlock {
+ inline_size,
+ block_size,
+ mode: style.writing_mode(),
+ };
+ // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
+ assert_eq!(
+ containing_block.mode, containing_block_for_children.mode,
+ "Mixed writing modes are not supported yet"
+ );
+ let dummy_tree_rank = 0;
+ non_replaced.layout(
+ layout_context,
+ &containing_block_for_children,
+ dummy_tree_rank,
+ &mut absolutely_positioned_fragments,
+ )
+ },
};
- // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
- assert_eq!(
- containing_block.mode, containing_block_for_children.mode,
- "Mixed writing modes are not supported yet"
- );
- let dummy_tree_rank = 0;
- let mut absolutely_positioned_fragments = vec![];
- let mut independent_layout = self.absolutely_positioned_box.contents.layout(
- layout_context,
- &containing_block_for_children,
- dummy_tree_rank,
- &mut absolutely_positioned_fragments,
- );
let inline_start = match inline_anchor {
Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
@@ -307,7 +369,7 @@ impl<'a> AbsolutelyPositionedFragment<'a> {
&mut independent_layout.fragments,
&content_rect.size,
&padding,
- containing_block_for_children.mode,
+ style.writing_mode(),
);
Fragment::Box(BoxFragment {
diff --git a/components/layout_2020/replaced.rs b/components/layout_2020/replaced.rs
index ecb8d2433e3..8cb77d267bd 100644
--- a/components/layout_2020/replaced.rs
+++ b/components/layout_2020/replaced.rs
@@ -3,10 +3,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom_traversal::NodeExt;
-use crate::formatting_contexts::IndependentLayout;
use crate::fragments::{Fragment, ImageFragment};
use crate::geom::{flow_relative, physical};
-use crate::ContainingBlock;
use net_traits::image::base::Image;
use servo_arc::Arc as ServoArc;
use std::sync::Arc;
@@ -35,39 +33,27 @@ impl ReplacedContent {
None
}
- pub fn layout<'a>(
+ pub fn make_fragments<'a>(
&'a self,
style: &ServoArc<ComputedValues>,
- containing_block: &ContainingBlock,
- ) -> IndependentLayout {
- let (fragments, content_block_size) = match self.kind {
- ReplacedContentKind::Image(ref image) => {
- // FIXME(nox): We should not assume block size is known.
- let block_size = containing_block.block_size.non_auto().unwrap();
- let fragments = image
- .as_ref()
- .and_then(|image| image.id)
- .map(|image_key| {
- Fragment::Image(ImageFragment {
- style: style.clone(),
- content_rect: flow_relative::Rect {
- start_corner: flow_relative::Vec2::zero(),
- size: flow_relative::Vec2 {
- inline: containing_block.inline_size,
- block: block_size,
- },
- },
- image_key,
- })
+ size: flow_relative::Vec2<Length>,
+ ) -> Vec<Fragment> {
+ match &self.kind {
+ ReplacedContentKind::Image(image) => image
+ .as_ref()
+ .and_then(|image| image.id)
+ .map(|image_key| {
+ Fragment::Image(ImageFragment {
+ style: style.clone(),
+ content_rect: flow_relative::Rect {
+ start_corner: flow_relative::Vec2::zero(),
+ size,
+ },
+ image_key,
})
- .into_iter()
- .collect::<Vec<_>>();
- (fragments, block_size)
- },
- };
- IndependentLayout {
- fragments,
- content_block_size,
+ })
+ .into_iter()
+ .collect(),
}
}
}
diff --git a/components/layout_2020/sizing.rs b/components/layout_2020/sizing.rs
new file mode 100644
index 00000000000..75c13b1dcb5
--- /dev/null
+++ b/components/layout_2020/sizing.rs
@@ -0,0 +1,152 @@
+/* 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/. */
+
+//! https://drafts.csswg.org/css-sizing/
+
+use crate::style_ext::ComputedValuesExt;
+use style::properties::ComputedValues;
+use style::values::computed::{Length, LengthPercentage, Percentage};
+use style::Zero;
+
+/// Which min/max-content values should be computed during box construction
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum ContentSizesRequest {
+ Inline,
+ None,
+}
+
+impl ContentSizesRequest {
+ pub fn inline_if(condition: bool) -> Self {
+ if condition {
+ Self::Inline
+ } else {
+ Self::None
+ }
+ }
+
+ pub fn requests_inline(self) -> bool {
+ match self {
+ Self::Inline => true,
+ Self::None => false,
+ }
+ }
+
+ pub fn if_requests_inline<T>(self, f: impl FnOnce() -> T) -> Option<T> {
+ match self {
+ Self::Inline => Some(f()),
+ Self::None => None,
+ }
+ }
+
+ pub fn compute(self, compute_inline: impl FnOnce() -> ContentSizes) -> BoxContentSizes {
+ match self {
+ Self::Inline => BoxContentSizes::Inline(compute_inline()),
+ Self::None => BoxContentSizes::NoneWereRequested,
+ }
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct ContentSizes {
+ pub min_content: Length,
+ pub max_content: Length,
+}
+
+/// https://drafts.csswg.org/css-sizing/#intrinsic-sizes
+impl ContentSizes {
+ pub fn zero() -> Self {
+ Self {
+ min_content: Length::zero(),
+ max_content: Length::zero(),
+ }
+ }
+
+ pub fn max_assign(&mut self, other: &Self) {
+ self.min_content.max_assign(other.min_content);
+ self.max_content.max_assign(other.max_content);
+ }
+
+ /// Relevant to outer intrinsic inline sizes, for percentages from padding and margin.
+ pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) {
+ // " Note that this may yield an infinite result, but undefined results
+ // (zero divided by zero) must be treated as zero. "
+ if self.max_content.px() == 0. {
+ // Avoid a potential `NaN`.
+ // Zero is already the result we want regardless of `denominator`.
+ } else {
+ let denominator = (1. - percentages.0).max(0.);
+ self.max_content = Length::new(self.max_content.px() / denominator);
+ }
+ }
+}
+
+/// Optional min/max-content for storage in the box tree
+#[derive(Debug)]
+pub(crate) enum BoxContentSizes {
+ NoneWereRequested, // … during box construction
+ Inline(ContentSizes),
+}
+
+impl BoxContentSizes {
+ fn expect_inline(&self) -> &ContentSizes {
+ match self {
+ Self::NoneWereRequested => panic!("Accessing content size that was not requested"),
+ Self::Inline(s) => s,
+ }
+ }
+
+ /// https://dbaron.org/css/intrinsic/#outer-intrinsic
+ pub fn outer_inline(&self, style: &ComputedValues) -> ContentSizes {
+ let (mut outer, percentages) = self.outer_inline_and_percentages(style);
+ outer.adjust_for_pbm_percentages(percentages);
+ outer
+ }
+
+ pub(crate) fn outer_inline_and_percentages(
+ &self,
+ style: &ComputedValues,
+ ) -> (ContentSizes, Percentage) {
+ // FIXME: account for 'min-width', 'max-width', 'box-sizing'
+
+ let inline_size = style.box_size().inline;
+ // Percentages for 'width' are treated as 'auto'
+ let inline_size = inline_size.map(|lp| lp.as_length());
+ // The (inner) min/max-content are only used for 'auto'
+ let mut outer = match inline_size.non_auto().flatten() {
+ None => self.expect_inline().clone(),
+ Some(length) => ContentSizes {
+ min_content: length,
+ max_content: length,
+ },
+ };
+
+ let mut pbm_lengths = Length::zero();
+ let mut pbm_percentages = Percentage::zero();
+ let padding = style.padding();
+ let border = style.border_width();
+ let margin = style.margin();
+ pbm_lengths += border.inline_sum();
+ let mut add = |x: LengthPercentage| {
+ pbm_lengths += x.length_component();
+ pbm_percentages += x.percentage_component();
+ };
+ add(padding.inline_start);
+ add(padding.inline_end);
+ margin.inline_start.non_auto().map(&mut add);
+ margin.inline_end.non_auto().map(&mut add);
+
+ outer.min_content += pbm_lengths;
+ outer.max_content += pbm_lengths;
+
+ (outer, pbm_percentages)
+ }
+
+ /// https://drafts.csswg.org/css2/visudet.html#shrink-to-fit-float
+ pub(crate) fn shrink_to_fit(&self, available_size: Length) -> Length {
+ let inline = self.expect_inline();
+ available_size
+ .max(inline.min_content)
+ .min(inline.max_content)
+ }
+}
diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs
index 8c145f23c6f..5748eac1b02 100644
--- a/components/layout_2020/style_ext.rs
+++ b/components/layout_2020/style_ext.rs
@@ -45,6 +45,9 @@ pub(crate) enum DisplayInside {
pub(crate) trait ComputedValuesExt {
fn writing_mode(&self) -> (WritingMode, Direction);
+ fn writing_mode_is_horizontal(&self) -> bool;
+ fn inline_size_is_auto(&self) -> bool;
+ fn inline_box_offsets_are_both_non_auto(&self) -> bool;
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto>;
fn box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
fn min_box_size(&self) -> flow_relative::Vec2<LengthPercentageOrAuto>;
@@ -62,6 +65,39 @@ impl ComputedValuesExt for ComputedValues {
(writing_mode, direction)
}
+ fn writing_mode_is_horizontal(&self) -> bool {
+ match self.get_inherited_box().writing_mode {
+ WritingMode::HorizontalTb => true,
+ WritingMode::VerticalLr | WritingMode::VerticalRl => false,
+ }
+ }
+
+ fn inline_size_is_auto(&self) -> bool {
+ let position = self.get_position();
+ let size = if self.writing_mode_is_horizontal() {
+ position.width
+ } else {
+ position.height
+ };
+ matches!(size, Size::Auto)
+ }
+
+ fn inline_box_offsets_are_both_non_auto(&self) -> bool {
+ let position = self.get_position();
+ let offsets = if self.writing_mode_is_horizontal() {
+ (position.left, position.right)
+ } else {
+ (position.top, position.bottom)
+ };
+ matches!(
+ offsets,
+ (
+ LengthPercentageOrAuto::LengthPercentage(_),
+ LengthPercentageOrAuto::LengthPercentage(_),
+ )
+ )
+ }
+
#[inline]
fn box_offsets(&self) -> flow_relative::Sides<LengthPercentageOrAuto> {
let position = self.get_position();
diff --git a/components/layout_2020/traversal.rs b/components/layout_2020/traversal.rs
index bdc2c095026..97a9874a20b 100644
--- a/components/layout_2020/traversal.rs
+++ b/components/layout_2020/traversal.rs
@@ -20,6 +20,10 @@ impl<'a> RecalcStyle<'a> {
RecalcStyle { context: context }
}
+ pub fn context(&self) -> &LayoutContext<'a> {
+ &self.context
+ }
+
pub fn destroy(self) -> LayoutContext<'a> {
self.context
}
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 77e89950d57..45202d51ad9 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -1081,9 +1081,9 @@ impl LayoutThread {
let box_tree = if token.should_traverse() {
driver::traverse_dom(&traversal, token, Some(rayon_pool));
- let shared = DomTraversal::<ServoLayoutElement>::shared_context(&traversal);
let root_node = document.root_element().unwrap().as_node();
- let box_tree = rayon_pool.install(|| BoxTreeRoot::construct(shared, root_node));
+ let box_tree =
+ rayon_pool.install(|| BoxTreeRoot::construct(traversal.context(), root_node));
Some(box_tree)
} else {
None
diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs
index 53cf7cbd696..06631a354df 100644
--- a/components/style/values/computed/length.rs
+++ b/components/style/values/computed/length.rs
@@ -170,6 +170,12 @@ impl LengthPercentage {
self.length
}
+ /// Returns the percentage component of this `calc()`
+ #[inline]
+ pub fn percentage_component(&self) -> Percentage {
+ Percentage(self.clamping_mode.clamp(self.percentage.0))
+ }
+
/// Return the percentage value as CSSFloat.
#[inline]
pub fn percentage(&self) -> CSSFloat {
@@ -186,6 +192,16 @@ impl LengthPercentage {
}
}
+ /// Returns the length component if this could be represented as a
+ /// non-calc length.
+ pub fn as_length(&self) -> Option<Length> {
+ if !self.has_percentage {
+ Some(self.length_component())
+ } else {
+ None
+ }
+ }
+
/// Returns the percentage component if this could be represented as a
/// non-calc percentage.
pub fn as_percentage(&self) -> Option<Percentage> {
diff --git a/components/style/values/computed/percentage.rs b/components/style/values/computed/percentage.rs
index b5a734367b2..7430d82d471 100644
--- a/components/style/values/computed/percentage.rs
+++ b/components/style/values/computed/percentage.rs
@@ -64,6 +64,12 @@ impl Zero for Percentage {
}
}
+impl std::ops::AddAssign for Percentage {
+ fn add_assign(&mut self, other: Self) {
+ self.0 += other.0
+ }
+}
+
impl ToCss for Percentage {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs
index d9cc2396f38..4183f40a942 100644
--- a/components/style/values/generics/length.rs
+++ b/components/style/values/generics/length.rs
@@ -92,7 +92,7 @@ where
}
/// Maps the length of this value.
- pub fn map(&self, f: impl FnOnce(LengthPercentage) -> LengthPercentage) -> Self {
+ pub fn map<T>(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto<T> {
match self {
LengthPercentageOrAuto::LengthPercentage(l) => {
LengthPercentageOrAuto::LengthPercentage(f(l.clone()))
diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini
deleted file mode 100644
index dd630be0f54..00000000000
--- a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-008.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[containing-block-008.xht]
- expected: FAIL
diff --git a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini b/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini
deleted file mode 100644
index af40eef0a5d..00000000000
--- a/tests/wpt/metadata-layout-2020/css/CSS2/box-display/containing-block-010.xht.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[containing-block-010.xht]
- expected: FAIL