Import victor's layout system 🍷

This commit is contained in:
Anthony Ramine 2019-09-09 17:21:26 +02:00
parent 86904757e6
commit 4444c5a2ad
25 changed files with 1270 additions and 712 deletions

View file

@ -1,10 +1,27 @@
use super::*;
/* 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 crate::dom_traversal::{BoxSlot, Contents, NodeExt, NonReplacedContents, TraversalHandler};
use crate::element_data::LayoutBox;
use crate::flow::float::FloatBox;
use crate::flow::inline::{InlineBox, InlineFormattingContext, InlineLevelBox, TextRun};
use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox};
use crate::positioned::AbsolutelyPositionedBox;
use crate::style_ext::{DisplayGeneratingBox, DisplayInside, DisplayOutside};
use crate::IndependentFormattingContext;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use std::convert::TryInto;
use style::context::SharedStyleContext;
use style::properties::ComputedValues;
impl BlockFormattingContext {
pub fn construct<'a>(
context: &'a Context<'a>,
style: &'a Arc<ComputedValues>,
contents: NonReplacedContents,
pub fn construct<'dom>(
context: &SharedStyleContext<'_>,
style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
) -> Self {
let (contents, contains_floats) = BlockContainer::construct(context, style, contents);
Self {
@ -14,25 +31,25 @@ impl BlockFormattingContext {
}
}
enum IntermediateBlockLevelBox {
enum IntermediateBlockLevelBox<Node> {
SameFormattingContextBlock {
style: Arc<ComputedValues>,
contents: IntermediateBlockContainer,
contents: IntermediateBlockContainer<Node>,
},
Independent {
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
contents: Contents<Node>,
},
OutOfFlowAbsolutelyPositionedBox {
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
contents: Contents<Node>,
},
OutOfFlowFloatBox {
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
contents: Contents<Node>,
},
}
@ -43,18 +60,19 @@ enum IntermediateBlockLevelBox {
/// of a given element.
///
/// Deferring allows using rayons `into_par_iter`.
enum IntermediateBlockContainer {
enum IntermediateBlockContainer<Node> {
InlineFormattingContext(InlineFormattingContext),
Deferred { contents: NonReplacedContents },
Deferred { contents: NonReplacedContents<Node> },
}
/// A builder for a block container.
///
/// 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<'a> {
context: &'a Context<'a>,
block_container_style: &'a Arc<ComputedValues>,
struct BlockContainerBuilder<'dom, 'style, Node> {
context: &'style SharedStyleContext<'style>,
block_container_style: &'style Arc<ComputedValues>,
/// The list of block-level boxes of the final block container.
///
@ -69,7 +87,7 @@ struct BlockContainerBuilder<'a> {
/// doesn't have a next sibling, we either reached the end of the container
/// root or there are ongoing inline-level boxes
/// (see `handle_block_level_element`).
block_level_boxes: Vec<(IntermediateBlockLevelBox, BoxSlot<'a>)>,
block_level_boxes: Vec<(IntermediateBlockLevelBox<Node>, BoxSlot<'dom>)>,
/// The ongoing inline formatting context of the builder.
///
@ -104,10 +122,10 @@ struct BlockContainerBuilder<'a> {
}
impl BlockContainer {
pub fn construct<'a>(
context: &'a Context<'a>,
block_container_style: &'a Arc<ComputedValues>,
contents: NonReplacedContents,
pub fn construct<'dom, 'style>(
context: &SharedStyleContext<'style>,
block_container_style: &Arc<ComputedValues>,
contents: NonReplacedContents<impl NodeExt<'dom>>,
) -> (BlockContainer, ContainsFloats) {
let mut builder = BlockContainerBuilder {
context,
@ -134,7 +152,8 @@ impl BlockContainer {
);
return (container, builder.contains_floats);
}
builder.end_ongoing_inline_formatting_context();
// FIXME
// builder.end_ongoing_inline_formatting_context();
}
let mut contains_floats = builder.contains_floats;
@ -144,10 +163,11 @@ impl BlockContainer {
.into_par_iter()
.mapfold_reduce_into(
&mut contains_floats,
|contains_floats, (intermediate, box_slot)| {
|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()));
// FIXME
// box_slot.set(LayoutBox::BlockLevel(block_level_box.clone()));
block_level_box
},
|left, right| *left |= right,
@ -158,13 +178,16 @@ impl BlockContainer {
}
}
impl<'a> TraversalHandler<'a> for BlockContainerBuilder<'a> {
impl<'dom, Node> TraversalHandler<Node> for BlockContainerBuilder<'dom, '_, Node>
where
Node: NodeExt<'dom>,
{
fn handle_element(
&mut self,
style: &Arc<ComputedValues>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'a>,
contents: Contents<Node>,
box_slot: BoxSlot,
) {
match display {
DisplayGeneratingBox::OutsideInside { outside, inside } => match outside {
@ -172,27 +195,29 @@ impl<'a> TraversalHandler<'a> for BlockContainerBuilder<'a> {
self.handle_inline_level_element(style, inside, contents),
)),
DisplayOutside::Block => {
// FIXME
// Floats and abspos cause blockification, so they only happen in this case.
// https://drafts.csswg.org/css2/visuren.html#dis-pos-flo
if style.box_.position.is_absolutely_positioned() {
self.handle_absolutely_positioned_element(
style.clone(),
inside,
contents,
box_slot,
)
} else if style.box_.float.is_floating() {
self.handle_float_element(style.clone(), inside, contents, box_slot)
} else {
self.handle_block_level_element(style.clone(), inside, contents, box_slot)
}
}
// if style.box_.position.is_absolutely_positioned() {
// self.handle_absolutely_positioned_element(
// style.clone(),
// inside,
// contents,
// box_slot,
// )
// } else if style.box_.float.is_floating() {
// self.handle_float_element(style.clone(), inside, contents, box_slot)
// } else {
// self.handle_block_level_element(style.clone(), inside, contents, box_slot)
// }
},
DisplayOutside::None => panic!(":("),
},
}
}
fn handle_text(&mut self, input: &str, parent_style: &Arc<ComputedValues>) {
let (leading_whitespace, mut input) = self.handle_leading_whitespace(input);
fn handle_text(&mut self, input: String, parent_style: &Arc<ComputedValues>) {
let (leading_whitespace, mut input) = self.handle_leading_whitespace(&input);
if leading_whitespace || !input.is_empty() {
// This text node should be pushed either to the next ongoing
// inline level box with the parent style of that inline level box
@ -256,7 +281,10 @@ impl<'a> TraversalHandler<'a> for BlockContainerBuilder<'a> {
}
}
impl<'a> BlockContainerBuilder<'a> {
impl<'dom, Node> BlockContainerBuilder<'dom, '_, Node>
where
Node: NodeExt<'dom>,
{
/// Returns:
///
/// * Whether this text run has preserved (non-collapsible) leading whitespace
@ -273,19 +301,19 @@ impl<'a> BlockContainerBuilder<'a> {
match inline_level_boxes.next().map(|b| &**b) {
Some(InlineLevelBox::TextRun(r)) => break !r.text.ends_with(' '),
Some(InlineLevelBox::Atomic { .. }) => break false,
Some(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_))
| Some(InlineLevelBox::OutOfFlowFloatBox(_)) => {}
Some(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_)) |
Some(InlineLevelBox::OutOfFlowFloatBox(_)) => {},
Some(InlineLevelBox::InlineBox(b)) => {
stack.push(inline_level_boxes);
inline_level_boxes = b.children.iter().rev()
}
},
None => {
if let Some(iter) = stack.pop() {
inline_level_boxes = iter
} else {
break false; // Paragraph start
}
}
},
}
};
let text = text.trim_start_matches(|c: char| c.is_ascii_whitespace());
@ -296,7 +324,7 @@ impl<'a> BlockContainerBuilder<'a> {
&mut self,
style: &Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
contents: Contents<Node>,
) -> Arc<InlineLevelBox> {
let box_ = match contents.try_into() {
Err(replaced) => Arc::new(InlineLevelBox::Atomic {
@ -322,97 +350,99 @@ impl<'a> BlockContainerBuilder<'a> {
.expect("no ongoing inline level box found");
inline_box.last_fragment = true;
Arc::new(InlineLevelBox::InlineBox(inline_box))
}
},
DisplayInside::FlowRoot => {
// a.k.a. `inline-block`
unimplemented!()
}
},
DisplayInside::None | DisplayInside::Contents => panic!(":("),
},
};
self.current_inline_level_boxes().push(box_.clone());
box_
}
fn handle_block_level_element(
&mut self,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'a>,
) {
// We just found a block level element, all ongoing inline level boxes
// need to be split around it. We iterate on the fragmented inline
// level box stack to take their contents and set their first_fragment
// field to false, for the fragmented inline level boxes that will
// come after the block level element.
let mut fragmented_inline_boxes =
self.ongoing_inline_boxes_stack
.iter_mut()
.rev()
.map(|ongoing| {
let fragmented = InlineBox {
style: ongoing.style.clone(),
first_fragment: ongoing.first_fragment,
// The fragmented boxes before the block level element
// are obviously not the last fragment.
last_fragment: false,
children: take(&mut ongoing.children),
};
ongoing.first_fragment = false;
fragmented
});
// FIXME
// fn handle_block_level_element(
// &mut self,
// style: Arc<ComputedValues>,
// display_inside: DisplayInside,
// contents: Contents,
// box_slot: BoxSlot<'a>,
// ) {
// // We just found a block level element, all ongoing inline level boxes
// // need to be split around it. We iterate on the fragmented inline
// // level box stack to take their contents and set their first_fragment
// // field to false, for the fragmented inline level boxes that will
// // come after the block level element.
// let mut fragmented_inline_boxes =
// self.ongoing_inline_boxes_stack
// .iter_mut()
// .rev()
// .map(|ongoing| {
// let fragmented = InlineBox {
// style: ongoing.style.clone(),
// first_fragment: ongoing.first_fragment,
// // The fragmented boxes before the block level element
// // are obviously not the last fragment.
// last_fragment: false,
// children: take(&mut ongoing.children),
// };
// ongoing.first_fragment = false;
// fragmented
// });
if let Some(last) = fragmented_inline_boxes.next() {
// There were indeed some ongoing inline level boxes before
// the block, we accumulate them as a single inline level box
// to be pushed to the ongoing inline formatting context.
let mut fragmented_inline = InlineLevelBox::InlineBox(last);
for mut fragmented_parent_inline_box in fragmented_inline_boxes {
fragmented_parent_inline_box
.children
.push(Arc::new(fragmented_inline));
fragmented_inline = InlineLevelBox::InlineBox(fragmented_parent_inline_box);
}
// if let Some(last) = fragmented_inline_boxes.next() {
// // There were indeed some ongoing inline level boxes before
// // the block, we accumulate them as a single inline level box
// // to be pushed to the ongoing inline formatting context.
// let mut fragmented_inline = InlineLevelBox::InlineBox(last);
// for mut fragmented_parent_inline_box in fragmented_inline_boxes {
// fragmented_parent_inline_box
// .children
// .push(Arc::new(fragmented_inline));
// fragmented_inline = InlineLevelBox::InlineBox(fragmented_parent_inline_box);
// }
self.ongoing_inline_formatting_context
.inline_level_boxes
.push(Arc::new(fragmented_inline));
}
// self.ongoing_inline_formatting_context
// .inline_level_boxes
// .push(Arc::new(fragmented_inline));
// }
// We found a block level element, so the ongoing inline formatting
// context needs to be ended.
self.end_ongoing_inline_formatting_context();
// // We found a block level element, so the ongoing inline formatting
// // context needs to be ended.
// self.end_ongoing_inline_formatting_context();
let intermediate_box = match contents.try_into() {
Ok(contents) => match display_inside {
DisplayInside::Flow => IntermediateBlockLevelBox::SameFormattingContextBlock {
style,
contents: IntermediateBlockContainer::Deferred { contents },
},
_ => IntermediateBlockLevelBox::Independent {
style,
display_inside,
contents: contents.into(),
},
},
Err(contents) => {
let contents = Contents::Replaced(contents);
IntermediateBlockLevelBox::Independent {
style,
display_inside,
contents,
}
}
};
self.block_level_boxes.push((intermediate_box, box_slot))
}
// let intermediate_box = match contents.try_into() {
// Ok(contents) => match display_inside {
// DisplayInside::Flow => IntermediateBlockLevelBox::SameFormattingContextBlock {
// style,
// contents: IntermediateBlockContainer::Deferred { contents },
// },
// _ => IntermediateBlockLevelBox::Independent {
// style,
// display_inside,
// contents: contents.into(),
// },
// },
// Err(contents) => {
// let contents = Contents::Replaced(contents);
// IntermediateBlockLevelBox::Independent {
// style,
// display_inside,
// contents,
// }
// }
// };
// self.block_level_boxes.push((intermediate_box, box_slot))
// }
fn handle_absolutely_positioned_element(
&mut self,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'a>,
contents: Contents<Node>,
box_slot: BoxSlot<'dom>,
) {
if !self.has_ongoing_inline_formatting_context() {
let box_ = IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox {
@ -420,12 +450,12 @@ impl<'a> BlockContainerBuilder<'a> {
contents,
display_inside,
};
self.block_level_boxes.push((box_, box_slot))
self.block_level_boxes.push((box_, box_slot));
} else {
let box_ = Arc::new(InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(
AbsolutelyPositionedBox {
contents: IndependentFormattingContext::construct(
self.context,
unimplemented!(),
&style,
display_inside,
contents,
@ -442,8 +472,8 @@ impl<'a> BlockContainerBuilder<'a> {
&mut self,
style: Arc<ComputedValues>,
display_inside: DisplayInside,
contents: Contents,
box_slot: BoxSlot<'a>,
contents: Contents<Node>,
box_slot: BoxSlot<'dom>,
) {
self.contains_floats = ContainsFloats::Yes;
@ -485,20 +515,21 @@ impl<'a> BlockContainerBuilder<'a> {
}
let block_container_style = self.block_container_style;
let anonymous_style = self.anonymous_style.get_or_insert_with(|| {
// If parent_style is None, the parent is the document node,
// in which case anonymous inline boxes should inherit their
// styles from initial values.
ComputedValues::anonymous_inheriting_from(Some(block_container_style))
});
// FIXME
// let anonymous_style = self.anonymous_style.get_or_insert_with(|| {
// // If parent_style is None, the parent is the document node,
// // in which case anonymous inline boxes should inherit their
// // styles from initial values.
// ComputedValues::anonymous_inheriting_from(Some(block_container_style))
// });
let box_ = IntermediateBlockLevelBox::SameFormattingContextBlock {
style: anonymous_style.clone(),
contents: IntermediateBlockContainer::InlineFormattingContext(take(
&mut self.ongoing_inline_formatting_context,
)),
};
self.block_level_boxes.push((box_, BoxSlot::dummy()))
// let box_ = IntermediateBlockLevelBox::SameFormattingContextBlock {
// style: anonymous_style.clone(),
// contents: IntermediateBlockContainer::InlineFormattingContext(take(
// &mut self.ongoing_inline_formatting_context,
// )),
// };
// self.block_level_boxes.push((box_, BoxSlot::dummy()))
}
fn current_inline_level_boxes(&mut self) -> &mut Vec<Arc<InlineLevelBox>> {
@ -512,20 +543,26 @@ impl<'a> BlockContainerBuilder<'a> {
!self
.ongoing_inline_formatting_context
.inline_level_boxes
.is_empty()
|| !self.ongoing_inline_boxes_stack.is_empty()
.is_empty() ||
!self.ongoing_inline_boxes_stack.is_empty()
}
}
impl IntermediateBlockLevelBox {
fn finish(self, context: &Context) -> (Arc<BlockLevelBox>, ContainsFloats) {
impl<'dom, Node> IntermediateBlockLevelBox<Node>
where
Node: NodeExt<'dom>,
{
fn finish<'style>(
self,
context: &SharedStyleContext<'style>,
) -> (Arc<BlockLevelBox>, ContainsFloats) {
match self {
IntermediateBlockLevelBox::SameFormattingContextBlock { style, contents } => {
let (contents, contains_floats) = contents.finish(context, &style);
let block_level_box =
Arc::new(BlockLevelBox::SameFormattingContextBlock { contents, style });
(block_level_box, contains_floats)
}
},
IntermediateBlockLevelBox::Independent {
style,
display_inside,
@ -541,7 +578,7 @@ impl IntermediateBlockLevelBox {
Arc::new(BlockLevelBox::Independent { style, contents }),
ContainsFloats::No,
)
}
},
IntermediateBlockLevelBox::OutOfFlowAbsolutelyPositionedBox {
style,
display_inside,
@ -559,7 +596,7 @@ impl IntermediateBlockLevelBox {
},
));
(block_level_box, ContainsFloats::No)
}
},
IntermediateBlockLevelBox::OutOfFlowFloatBox {
style,
display_inside,
@ -576,21 +613,24 @@ impl IntermediateBlockLevelBox {
style,
}));
(block_level_box, ContainsFloats::Yes)
}
},
}
}
}
impl IntermediateBlockContainer {
fn finish(
impl<'dom, Node> IntermediateBlockContainer<Node>
where
Node: NodeExt<'dom>,
{
fn finish<'style>(
self,
context: &Context,
context: &SharedStyleContext<'style>,
style: &Arc<ComputedValues>,
) -> (BlockContainer, ContainsFloats) {
match self {
IntermediateBlockContainer::Deferred { contents } => {
BlockContainer::construct(context, style, contents)
}
},
IntermediateBlockContainer::InlineFormattingContext(ifc) => {
// If that inline formatting context contained any float, those
// were already taken into account during the first phase of
@ -599,13 +639,13 @@ impl IntermediateBlockContainer {
BlockContainer::InlineFormattingContext(ifc),
ContainsFloats::No,
)
}
},
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::layout) enum ContainsFloats {
pub(crate) enum ContainsFloats {
No,
Yes,
}

View file

@ -1,13 +1,19 @@
use super::*;
/* 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 crate::IndependentFormattingContext;
use servo_arc::Arc;
use style::properties::ComputedValues;
#[derive(Debug)]
pub(in crate::layout) struct FloatBox {
pub(crate) struct FloatBox {
pub style: Arc<ComputedValues>,
pub contents: IndependentFormattingContext,
}
/// Data kept during layout about the floats in a given block formatting context.
pub(in crate::layout) struct FloatContext {
pub(crate) struct FloatContext {
// TODO
}

View file

@ -1,14 +1,27 @@
use super::*;
use crate::fonts::BITSTREAM_VERA_SANS;
use crate::text::ShapedSegment;
/* 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 crate::flow::float::FloatBox;
use crate::flow::FlowChildren;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
use crate::replaced::ReplacedContent;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
use crate::{relative_adjustement, take, ContainingBlock};
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::Length;
use style::Zero;
#[derive(Debug, Default)]
pub(in crate::layout) struct InlineFormattingContext {
pub(crate) struct InlineFormattingContext {
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
}
#[derive(Debug)]
pub(in crate::layout) enum InlineLevelBox {
pub(crate) enum InlineLevelBox {
InlineBox(InlineBox),
TextRun(TextRun),
OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox),
@ -21,7 +34,7 @@ pub(in crate::layout) enum InlineLevelBox {
}
#[derive(Debug)]
pub(in crate::layout) struct InlineBox {
pub(crate) struct InlineBox {
pub style: Arc<ComputedValues>,
pub first_fragment: bool,
pub last_fragment: bool,
@ -30,7 +43,7 @@ pub(in crate::layout) struct InlineBox {
/// https://www.w3.org/TR/css-display-3/#css-text-run
#[derive(Debug)]
pub(in crate::layout) struct TextRun {
pub(crate) struct TextRun {
pub parent_style: Arc<ComputedValues>,
pub text: String,
}
@ -93,36 +106,40 @@ impl InlineFormattingContext {
InlineLevelBox::InlineBox(inline) => {
let partial = inline.start_layout(&mut ifc);
ifc.partial_inline_boxes_stack.push(partial)
}
},
InlineLevelBox::TextRun(run) => run.layout(&mut ifc),
InlineLevelBox::Atomic { style: _, contents } => {
// FIXME
match *contents {}
}
},
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
let initial_start_corner = match box_.style.specified_display {
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside {
outside,
inside: _,
}) => Vec2 {
inline: match outside {
DisplayOutside::Inline => ifc.inline_position,
DisplayOutside::Block => Length::zero(),
let initial_start_corner =
match Display::from(box_.style.get_box().original_display) {
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside {
outside,
inside: _,
}) => Vec2 {
inline: match outside {
DisplayOutside::Inline => ifc.inline_position,
DisplayOutside::Block => Length::zero(),
DisplayOutside::None => unreachable!(":("),
},
block: ifc.line_boxes.next_line_block_position,
},
block: ifc.line_boxes.next_line_block_position,
},
Display::Contents => {
panic!("display:contents does not generate an abspos box")
}
Display::None => panic!("display:none does not generate an abspos box"),
};
Display::Contents => {
panic!("display:contents does not generate an abspos box")
},
Display::None => {
panic!("display:none does not generate an abspos box")
},
};
absolutely_positioned_fragments
.push(box_.layout(initial_start_corner, tree_rank));
}
},
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
// TODO
continue;
}
},
}
} else
// Reached the end of ifc.remaining_boxes
@ -180,7 +197,7 @@ impl InlineBox {
let style = self.style.clone();
let cbis = ifc.containing_block.inline_size;
let mut padding = style.padding().percentages_relative_to(cbis);
let mut border = style.border_width().percentages_relative_to(cbis);
let mut border = style.border_width();
let mut margin = style
.margin()
.percentages_relative_to(cbis)
@ -245,9 +262,9 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
};
let last_fragment = self.last_box_tree_fragment && !at_line_break;
if last_fragment {
*inline_position += fragment.padding.inline_end
+ fragment.border.inline_end
+ fragment.margin.inline_end;
*inline_position += fragment.padding.inline_end +
fragment.border.inline_end +
fragment.margin.inline_end;
} else {
fragment.padding.inline_end = Length::zero();
fragment.border.inline_end = Length::zero();
@ -256,10 +273,10 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
self.parent_nesting_level
.max_block_size_of_fragments_so_far
.max_assign(
fragment.content_rect.size.block
+ fragment.padding.block_sum()
+ fragment.border.block_sum()
+ fragment.margin.block_sum(),
fragment.content_rect.size.block +
fragment.padding.block_sum() +
fragment.border.block_sum() +
fragment.margin.block_sum(),
);
self.parent_nesting_level
.fragments_so_far
@ -268,78 +285,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}
impl TextRun {
fn layout(&self, ifc: &mut InlineFormattingContextState) {
let available = ifc.containing_block.inline_size - ifc.inline_position;
let mut chars = self.text.chars();
loop {
let mut shaped = ShapedSegment::new_with_naive_shaping(BITSTREAM_VERA_SANS.clone());
let mut last_break_opportunity = None;
loop {
let next = chars.next();
if matches!(next, Some(' ') | None) {
let inline_size = self.parent_style.font.font_size * shaped.advance_width;
if inline_size > available {
if let Some((state, iter)) = last_break_opportunity.take() {
shaped.restore(&state);
chars = iter;
}
break;
}
}
if let Some(ch) = next {
if ch == ' ' {
last_break_opportunity = Some((shaped.save(), chars.clone()))
}
shaped.append_char(ch).unwrap()
} else {
break;
}
}
let inline_size = self.parent_style.font.font_size * shaped.advance_width;
// https://www.w3.org/TR/CSS2/visudet.html#propdef-line-height
// 'normal':
// “set the used value to a "reasonable" value based on the font of the element.”
let line_height = self.parent_style.font.font_size.0 * 1.2;
let content_rect = Rect {
start_corner: Vec2 {
block: Length::zero(),
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
},
size: Vec2 {
block: line_height,
inline: inline_size,
},
};
ifc.inline_position += inline_size;
ifc.current_nesting_level
.max_block_size_of_fragments_so_far
.max_assign(line_height);
ifc.current_nesting_level
.fragments_so_far
.push(Fragment::Text(TextFragment {
parent_style: self.parent_style.clone(),
content_rect,
text: shaped,
}));
if chars.as_str().is_empty() {
break;
} else {
// New line
ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
partial.finish_layout(nesting_level, &mut ifc.inline_position, true);
partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero();
partial.margin.inline_start = Length::zero();
partial.parent_nesting_level.inline_start = Length::zero();
nesting_level = &mut partial.parent_nesting_level;
}
ifc.line_boxes
.finish_line(nesting_level, ifc.containing_block);
ifc.inline_position = Length::zero();
}
}
fn layout(&self, _ifc: &mut InlineFormattingContextState) {
// TODO
}
}

View file

@ -1,32 +1,46 @@
/* 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/. */
//! Flow layout, also known as block-and-inline layout.
use super::*;
use rayon::prelude::*;
use crate::flow::float::{FloatBox, FloatContext};
use crate::flow::inline::InlineFormattingContext;
use crate::fragments::{
AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
};
use crate::geom::flow_relative::{Rect, Vec2};
use crate::positioned::{
adjust_static_positions, AbsolutelyPositionedBox, AbsolutelyPositionedFragment,
};
use crate::style_ext::{ComputedValuesExt, Position};
use crate::{relative_adjustement, ContainingBlock, IndependentFormattingContext};
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use rayon_croissant::ParallelIteratorExt;
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto};
use style::Zero;
mod construct;
mod float;
mod inline;
pub mod inline;
mod root;
pub(super) use construct::*;
pub(super) use float::*;
pub(super) use inline::*;
#[derive(Debug)]
pub(super) struct BlockFormattingContext {
pub(crate) struct BlockFormattingContext {
pub contents: BlockContainer,
pub contains_floats: bool,
}
#[derive(Debug)]
pub(super) enum BlockContainer {
pub(crate) enum BlockContainer {
BlockLevelBoxes(Vec<Arc<BlockLevelBox>>),
InlineFormattingContext(InlineFormattingContext),
}
#[derive(Debug)]
pub(super) enum BlockLevelBox {
pub(crate) enum BlockLevelBox {
SameFormattingContextBlock {
style: Arc<ComputedValues>,
contents: BlockContainer,
@ -98,7 +112,7 @@ impl BlockContainer {
),
BlockContainer::InlineFormattingContext(ifc) => {
ifc.layout(containing_block, tree_rank, absolutely_positioned_fragments)
}
},
}
}
}
@ -115,9 +129,9 @@ fn layout_block_level_children<'a>(
match fragment {
Fragment::Box(fragment) => {
let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
let fragment_block_size = fragment.padding.block_sum()
+ fragment.border.block_sum()
+ fragment.content_rect.size.block;
let fragment_block_size = fragment.padding.block_sum() +
fragment.border.block_sum() +
fragment.content_rect.size.block;
if placement_state.next_in_flow_margin_collapses_with_parent_start_margin {
assert_eq!(placement_state.current_margin.solve(), Length::zero());
@ -136,8 +150,8 @@ fn layout_block_level_children<'a>(
.current_margin
.adjoin_assign(&fragment_block_margins.start);
}
fragment.content_rect.start_corner.block += placement_state.current_margin.solve()
+ placement_state.current_block_direction_position;
fragment.content_rect.start_corner.block += placement_state.current_margin.solve() +
placement_state.current_block_direction_position;
if fragment_block_margins.collapsed_through {
placement_state
.current_margin
@ -147,7 +161,7 @@ fn layout_block_level_children<'a>(
placement_state.current_block_direction_position +=
placement_state.current_margin.solve() + fragment_block_size;
placement_state.current_margin = fragment_block_margins.end;
}
},
Fragment::Anonymous(fragment) => {
// FIXME(nox): Margin collapsing for hypothetical boxes of
// abspos elements is probably wrong.
@ -155,7 +169,7 @@ fn layout_block_level_children<'a>(
assert_eq!(fragment.rect.size.block, Length::zero());
fragment.rect.start_corner.block +=
placement_state.current_block_direction_position;
}
},
_ => unreachable!(),
}
}
@ -261,12 +275,12 @@ impl BlockLevelBox {
)
},
))
}
},
BlockLevelBox::Independent { style, contents } => match contents.as_replaced() {
Ok(replaced) => {
// FIXME
match *replaced {}
}
},
Err(contents) => Fragment::Box(layout_in_flow_non_replaced_block_level(
containing_block,
absolutely_positioned_fragments,
@ -280,11 +294,11 @@ impl BlockLevelBox {
BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
absolutely_positioned_fragments.push(box_.layout(Vec2::zero(), tree_rank));
Fragment::Anonymous(AnonymousFragment::no_op(containing_block.mode))
}
},
BlockLevelBox::OutOfFlowFloatBox(_box_) => {
// TODO
Fragment::Anonymous(AnonymousFragment::no_op(containing_block.mode))
}
},
}
}
}
@ -310,43 +324,40 @@ fn layout_in_flow_non_replaced_block_level<'a>(
) -> BoxFragment {
let cbis = containing_block.inline_size;
let padding = style.padding().percentages_relative_to(cbis);
let border = style.border_width().percentages_relative_to(cbis);
let border = style.border_width();
let mut computed_margin = style.margin().percentages_relative_to(cbis);
let pb = &padding + &border;
let box_size = style.box_size();
let inline_size = box_size.inline.percentage_relative_to(cbis);
if let LengthOrAuto::Length(is) = inline_size {
if let LengthOrAuto::LengthPercentage(is) = inline_size {
let inline_margins = cbis - is - pb.inline_sum();
use LengthOrAuto::*;
match (
&mut computed_margin.inline_start,
&mut computed_margin.inline_end,
) {
(s @ &mut Auto, e @ &mut Auto) => {
*s = Length(inline_margins / 2.);
*e = Length(inline_margins / 2.);
}
(s @ &mut Auto, _) => {
*s = Length(inline_margins);
}
(_, e @ &mut Auto) => {
*e = Length(inline_margins);
}
(s @ &mut LengthOrAuto::Auto, e @ &mut LengthOrAuto::Auto) => {
*s = LengthOrAuto::LengthPercentage(inline_margins / 2.);
*e = LengthOrAuto::LengthPercentage(inline_margins / 2.);
},
(s @ &mut LengthOrAuto::Auto, _) => {
*s = LengthOrAuto::LengthPercentage(inline_margins);
},
(_, e @ &mut LengthOrAuto::Auto) => {
*e = LengthOrAuto::LengthPercentage(inline_margins);
},
(_, e @ _) => {
// Either the inline-end margin is auto,
// or were over-constrained and we do as if it were.
*e = Length(inline_margins);
}
*e = LengthOrAuto::LengthPercentage(inline_margins);
},
}
}
let margin = computed_margin.auto_is(Length::zero);
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
let inline_size = inline_size.auto_is(|| cbis - pb.inline_sum() - margin.inline_sum());
let block_size = match box_size.block {
LengthOrPercentageOrAuto::Length(l) => LengthOrAuto::Length(l),
LengthOrPercentageOrAuto::Percentage(p) => containing_block.block_size.map(|cbbs| cbbs * p),
LengthOrPercentageOrAuto::Auto => LengthOrAuto::Auto,
};
let block_size = box_size
.block
.maybe_percentage_relative_to(containing_block.block_size.non_auto());
let containing_block_for_children = ContainingBlock {
inline_size,
block_size,
@ -358,11 +369,11 @@ fn layout_in_flow_non_replaced_block_level<'a>(
"Mixed writing modes are not supported yet"
);
let this_start_margin_can_collapse_with_children = CollapsibleWithParentStartMargin(
block_level_kind == BlockLevelKind::SameFormattingContextBlock
&& pb.block_start == Length::zero(),
block_level_kind == BlockLevelKind::SameFormattingContextBlock &&
pb.block_start == Length::zero(),
);
let this_end_margin_can_collapse_with_children = (block_level_kind, pb.block_end, block_size)
== (
let this_end_margin_can_collapse_with_children = (block_level_kind, pb.block_end, block_size) ==
(
BlockLevelKind::SameFormattingContextBlock,
Length::zero(),
LengthOrAuto::Auto,
@ -370,7 +381,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
let mut nested_abspos = vec![];
let mut flow_children = layout_contents(
&containing_block_for_children,
if style.box_.position.is_relatively_positioned() {
if style.get_box().position == Position::Relative {
&mut nested_abspos
} else {
absolutely_positioned_fragments
@ -401,9 +412,9 @@ fn layout_in_flow_non_replaced_block_level<'a>(
flow_children.block_size += flow_children.collapsible_margins_in_children.end.solve();
}
block_margins_collapsed_with_children.collapsed_through =
this_start_margin_can_collapse_with_children.0
&& this_end_margin_can_collapse_with_children
&& flow_children
this_start_margin_can_collapse_with_children.0 &&
this_end_margin_can_collapse_with_children &&
flow_children
.collapsible_margins_in_children
.collapsed_through;
let relative_adjustement = relative_adjustement(style, inline_size, block_size);
@ -418,7 +429,7 @@ fn layout_in_flow_non_replaced_block_level<'a>(
inline: inline_size,
},
};
if style.box_.position.is_relatively_positioned() {
if style.get_box().position == Position::Relative {
AbsolutelyPositionedFragment::in_positioned_containing_block(
&nested_abspos,
&mut flow_children.fragments,

View file

@ -1,122 +1,129 @@
/* 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::*;
impl crate::dom::Document {
pub(crate) fn layout(
&self,
viewport: crate::primitives::Size<crate::primitives::CssPx>,
) -> Vec<Fragment> {
BoxTreeRoot::construct(self).layout(viewport)
}
}
// FIXME
// impl crate::dom::Document {
// pub(crate) fn layout(
// &self,
// viewport: crate::geom::Size<crate::geom::CssPx>,
// ) -> Vec<Fragment> {
// BoxTreeRoot::construct(self).layout(viewport)
// }
// }
struct BoxTreeRoot(BlockFormattingContext);
impl BoxTreeRoot {
pub fn construct(document: &dom::Document) -> Self {
let author_styles = &document.parse_stylesheets();
let context = Context {
document,
author_styles,
};
let root_element = document.root_element();
let style = style_for_element(context.author_styles, context.document, root_element, None);
let (contains_floats, boxes) = construct_for_root_element(&context, root_element, style);
Self(BlockFormattingContext {
contains_floats: contains_floats == ContainsFloats::Yes,
contents: BlockContainer::BlockLevelBoxes(boxes),
})
}
}
// FIXME
// impl BoxTreeRoot {
// pub fn construct(document: &dom::Document) -> Self {
// let author_styles = &document.parse_stylesheets();
// let context = Context {
// document,
// author_styles,
// };
// let root_element = document.root_element();
// let style = style_for_element(context.author_styles, context.document, root_element, None);
// let (contains_floats, boxes) = construct_for_root_element(&context, root_element, style);
// Self(BlockFormattingContext {
// contains_floats: contains_floats == ContainsFloats::Yes,
// contents: BlockContainer::BlockLevelBoxes(boxes),
// })
// }
// }
fn construct_for_root_element(
context: &Context,
root_element: dom::NodeId,
style: Arc<ComputedValues>,
) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) {
let replaced = ReplacedContent::for_element(root_element, context);
// fn construct_for_root_element(
// context: &Context,
// root_element: dom::NodeId,
// style: Arc<ComputedValues>,
// ) -> (ContainsFloats, Vec<Arc<BlockLevelBox>>) {
// let replaced = ReplacedContent::for_element(root_element, context);
let display_inside = match style.box_.display {
Display::None => return (ContainsFloats::No, Vec::new()),
Display::Contents if replaced.is_some() => {
// 'display: contents' computes to 'none' for replaced elements
return (ContainsFloats::No, Vec::new());
}
// https://drafts.csswg.org/css-display-3/#transformations
Display::Contents => DisplayInside::Flow,
// The root element is blockified, ignore DisplayOutside
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
};
// let display_inside = match style.box_.display {
// Display::None => return (ContainsFloats::No, Vec::new()),
// Display::Contents if replaced.is_some() => {
// // 'display: contents' computes to 'none' for replaced elements
// return (ContainsFloats::No, Vec::new());
// }
// // https://drafts.csswg.org/css-display-3/#transformations
// Display::Contents => DisplayInside::Flow,
// // The root element is blockified, ignore DisplayOutside
// Display::GeneratingBox(DisplayGeneratingBox::OutsideInside { inside, .. }) => inside,
// };
if let Some(replaced) = replaced {
let _box = match replaced {};
#[allow(unreachable_code)]
{
return (ContainsFloats::No, vec![Arc::new(_box)]);
}
}
// if let Some(replaced) = replaced {
// let _box = match replaced {};
// #[allow(unreachable_code)]
// {
// return (ContainsFloats::No, vec![Arc::new(_box)]);
// }
// }
let contents = IndependentFormattingContext::construct(
context,
&style,
display_inside,
Contents::OfElement(root_element),
);
if style.box_.position.is_absolutely_positioned() {
(
ContainsFloats::No,
vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
AbsolutelyPositionedBox { style, contents },
))],
)
} else if style.box_.float.is_floating() {
(
ContainsFloats::Yes,
vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox {
contents,
style,
}))],
)
} else {
(
ContainsFloats::No,
vec![Arc::new(BlockLevelBox::Independent { style, contents })],
)
}
}
// let contents = IndependentFormattingContext::construct(
// context,
// &style,
// display_inside,
// Contents::OfElement(root_element),
// );
// if style.box_.position.is_absolutely_positioned() {
// (
// ContainsFloats::No,
// vec![Arc::new(BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(
// AbsolutelyPositionedBox { style, contents },
// ))],
// )
// } else if style.box_.float.is_floating() {
// (
// ContainsFloats::Yes,
// vec![Arc::new(BlockLevelBox::OutOfFlowFloatBox(FloatBox {
// contents,
// style,
// }))],
// )
// } else {
// (
// ContainsFloats::No,
// vec![Arc::new(BlockLevelBox::Independent { style, contents })],
// )
// }
// }
impl BoxTreeRoot {
fn layout(&self, viewport: crate::primitives::Size<crate::primitives::CssPx>) -> Vec<Fragment> {
let initial_containing_block_size = Vec2 {
inline: Length { px: viewport.width },
block: Length {
px: viewport.height,
},
};
// impl BoxTreeRoot {
// fn layout(&self, viewport: crate::geom::Size<crate::geom::CssPx>) -> Vec<Fragment> {
// let initial_containing_block_size = Vec2 {
// inline: Length { px: viewport.width },
// block: Length {
// px: viewport.height,
// },
// };
let initial_containing_block = ContainingBlock {
inline_size: initial_containing_block_size.inline,
block_size: LengthOrAuto::Length(initial_containing_block_size.block),
// FIXME: use the documents mode:
// https://drafts.csswg.org/css-writing-modes/#principal-flow
mode: (WritingMode::HorizontalTb, Direction::Ltr),
};
let dummy_tree_rank = 0;
let mut absolutely_positioned_fragments = vec![];
let mut flow_children = self.0.layout(
&initial_containing_block,
dummy_tree_rank,
&mut absolutely_positioned_fragments,
);
// let initial_containing_block = ContainingBlock {
// inline_size: initial_containing_block_size.inline,
// block_size: LengthOrAuto::Length(initial_containing_block_size.block),
// // FIXME: use the documents mode:
// // https://drafts.csswg.org/css-writing-modes/#principal-flow
// mode: (WritingMode::HorizontalTb, Direction::Ltr),
// };
// let dummy_tree_rank = 0;
// let mut absolutely_positioned_fragments = vec![];
// let mut fragments = self.0.layout(
// &initial_containing_block,
// &mut absolutely_positioned_fragments,
// dummy_tree_rank,
// &mut PlacementState::root(),
// );
let initial_containing_block = DefiniteContainingBlock {
size: initial_containing_block_size,
mode: initial_containing_block.mode,
};
flow_children.fragments.par_extend(
absolutely_positioned_fragments
.par_iter()
.map(|a| a.layout(&initial_containing_block)),
);
flow_children.fragments
}
}
// let initial_containing_block = DefiniteContainingBlock {
// size: initial_containing_block_size,
// mode: initial_containing_block.mode,
// };
// fragments.par_extend(
// absolutely_positioned_fragments
// .par_iter()
// .map(|a| a.layout(&initial_containing_block)),
// );
// fragments
// }
// }