mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #25033 - servo:intrinsic, r=nox
Add support for inline-block and for computing min/max-content
This commit is contained in:
commit
e70397d90a
19 changed files with 823 additions and 272 deletions
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
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,
|
||||
);
|
||||
let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
||||
&mut font,
|
||||
&self.text,
|
||||
&shaping_options,
|
||||
&mut None,
|
||||
);
|
||||
|
||||
(
|
||||
font.metrics.ascent,
|
||||
font.metrics.line_gap,
|
||||
font.font_key,
|
||||
runs,
|
||||
)
|
||||
});
|
||||
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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
))],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue