mirror of
https://github.com/servo/servo.git
synced 2025-07-25 16:20:36 +01:00
We want to mutate them when lazily computing their content sizes, but they are behind an Arc for the hoisting infra, so it also needs its own layer of inner mutability.
252 lines
8.9 KiB
Rust
252 lines
8.9 KiB
Rust
/* 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::cell::ArcRefCell;
|
|
use crate::context::LayoutContext;
|
|
use crate::dom_traversal::{
|
|
BoxSlot, Contents, NodeAndStyleInfo, NodeExt, NonReplacedContents, TraversalHandler,
|
|
};
|
|
use crate::element_data::LayoutBox;
|
|
use crate::formatting_contexts::{IndependentFormattingContext, IndependentLayout};
|
|
use crate::fragments::Tag;
|
|
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
|
use crate::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest};
|
|
use crate::style_ext::DisplayGeneratingBox;
|
|
use crate::ContainingBlock;
|
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
|
use std::borrow::Cow;
|
|
use style::values::computed::Length;
|
|
use style::values::specified::text::TextDecorationLine;
|
|
use style::Zero;
|
|
|
|
// FIXME: `min-width: auto` is not zero: https://drafts.csswg.org/css-flexbox/#min-size-auto
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) struct FlexContainer {
|
|
children: Vec<ArcRefCell<FlexLevelBox>>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub(crate) enum FlexLevelBox {
|
|
FlexItem(IndependentFormattingContext),
|
|
OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
|
|
}
|
|
|
|
impl FlexContainer {
|
|
pub fn construct<'dom>(
|
|
context: &LayoutContext,
|
|
info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
|
|
contents: NonReplacedContents,
|
|
content_sizes: ContentSizesRequest,
|
|
propagated_text_decoration_line: TextDecorationLine,
|
|
) -> (Self, BoxContentSizes) {
|
|
let text_decoration_line =
|
|
propagated_text_decoration_line | info.style.clone_text_decoration_line();
|
|
let mut builder = FlexContainerBuilder {
|
|
context,
|
|
info,
|
|
text_decoration_line,
|
|
contiguous_text_runs: Vec::new(),
|
|
jobs: Vec::new(),
|
|
has_text_runs: false,
|
|
};
|
|
contents.traverse(context, info, &mut builder);
|
|
let content_sizes = content_sizes.compute(|| {
|
|
// FIXME
|
|
ContentSizes::zero()
|
|
});
|
|
(builder.finish(), content_sizes)
|
|
}
|
|
}
|
|
|
|
/// https://drafts.csswg.org/css-flexbox/#flex-items
|
|
struct FlexContainerBuilder<'a, 'dom, Node> {
|
|
context: &'a LayoutContext<'a>,
|
|
info: &'a NodeAndStyleInfo<Node>,
|
|
text_decoration_line: TextDecorationLine,
|
|
contiguous_text_runs: Vec<TextRun<'dom, Node>>,
|
|
/// To be run in parallel with rayon in `finish`
|
|
jobs: Vec<FlexLevelJob<'dom, Node>>,
|
|
has_text_runs: bool,
|
|
}
|
|
|
|
enum FlexLevelJob<'dom, Node> {
|
|
/// Or pseudo-element
|
|
Element {
|
|
info: NodeAndStyleInfo<Node>,
|
|
display: DisplayGeneratingBox,
|
|
contents: Contents,
|
|
box_slot: BoxSlot<'dom>,
|
|
},
|
|
TextRuns(Vec<TextRun<'dom, Node>>),
|
|
}
|
|
|
|
struct TextRun<'dom, Node> {
|
|
info: NodeAndStyleInfo<Node>,
|
|
text: Cow<'dom, str>,
|
|
}
|
|
|
|
impl<'a, 'dom, Node: 'dom> TraversalHandler<'dom, Node> for FlexContainerBuilder<'a, 'dom, Node>
|
|
where
|
|
Node: NodeExt<'dom>,
|
|
{
|
|
fn handle_text(&mut self, info: &NodeAndStyleInfo<Node>, text: Cow<'dom, str>) {
|
|
self.contiguous_text_runs.push(TextRun {
|
|
info: info.clone(),
|
|
text,
|
|
})
|
|
}
|
|
|
|
/// Or pseudo-element
|
|
fn handle_element(
|
|
&mut self,
|
|
info: &NodeAndStyleInfo<Node>,
|
|
display: DisplayGeneratingBox,
|
|
contents: Contents,
|
|
box_slot: BoxSlot<'dom>,
|
|
) {
|
|
// FIXME: are text runs considered "contiguous" if they are only separated
|
|
// by an out-of-flow abspos element?
|
|
// (That is, are they wrapped in the same anonymous flex item, or each its own?)
|
|
self.wrap_any_text_in_anonymous_block_container();
|
|
|
|
self.jobs.push(FlexLevelJob::Element {
|
|
info: info.clone(),
|
|
display,
|
|
contents,
|
|
box_slot,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// https://drafts.csswg.org/css-text/#white-space
|
|
fn is_only_document_white_space<Node>(run: &TextRun<'_, Node>) -> bool {
|
|
// FIXME: is this the right definition? See
|
|
// https://github.com/w3c/csswg-drafts/issues/5146
|
|
// https://github.com/w3c/csswg-drafts/issues/5147
|
|
run.text
|
|
.bytes()
|
|
.all(|byte| matches!(byte, b' ' | b'\n' | b'\t'))
|
|
}
|
|
|
|
impl<'a, 'dom, Node: 'dom> FlexContainerBuilder<'a, 'dom, Node>
|
|
where
|
|
Node: NodeExt<'dom>,
|
|
{
|
|
fn wrap_any_text_in_anonymous_block_container(&mut self) {
|
|
let runs = std::mem::take(&mut self.contiguous_text_runs);
|
|
if runs.iter().all(is_only_document_white_space) {
|
|
// There is no text run, or they all only contain document white space characters
|
|
} else {
|
|
self.jobs.push(FlexLevelJob::TextRuns(runs));
|
|
self.has_text_runs = true;
|
|
}
|
|
}
|
|
|
|
fn finish(mut self) -> FlexContainer {
|
|
self.wrap_any_text_in_anonymous_block_container();
|
|
|
|
let anonymous_style = if self.has_text_runs {
|
|
Some(
|
|
self.context
|
|
.shared_context()
|
|
.stylist
|
|
.style_for_anonymous::<Node::ConcreteElement>(
|
|
&self.context.shared_context().guards,
|
|
&style::selector_parser::PseudoElement::ServoText,
|
|
&self.info.style,
|
|
),
|
|
)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let mut children = std::mem::take(&mut self.jobs)
|
|
.into_par_iter()
|
|
.map(|job| match job {
|
|
FlexLevelJob::TextRuns(runs) => ArcRefCell::new(FlexLevelBox::FlexItem(
|
|
IndependentFormattingContext::construct_for_text_runs(
|
|
self.context,
|
|
&self
|
|
.info
|
|
.new_replacing_style(anonymous_style.clone().unwrap()),
|
|
runs.into_iter().map(|run| crate::flow::inline::TextRun {
|
|
tag: Tag::from_node_and_style_info(&run.info),
|
|
text: run.text.into(),
|
|
parent_style: run.info.style,
|
|
}),
|
|
ContentSizesRequest::None, // FIXME: request sizes when we start using them
|
|
self.text_decoration_line,
|
|
),
|
|
)),
|
|
FlexLevelJob::Element {
|
|
info,
|
|
display,
|
|
contents,
|
|
box_slot,
|
|
} => {
|
|
let display_inside = match display {
|
|
DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
|
|
};
|
|
let box_ = if info.style.get_box().position.is_absolutely_positioned() {
|
|
// https://drafts.csswg.org/css-flexbox/#abspos-items
|
|
ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(
|
|
ArcRefCell::new(AbsolutelyPositionedBox::construct(
|
|
self.context,
|
|
&info,
|
|
display_inside,
|
|
contents,
|
|
)),
|
|
))
|
|
} else {
|
|
ArcRefCell::new(FlexLevelBox::FlexItem(
|
|
IndependentFormattingContext::construct(
|
|
self.context,
|
|
&info,
|
|
display_inside,
|
|
contents,
|
|
ContentSizesRequest::None, // FIXME: request sizes when we start using them
|
|
self.text_decoration_line,
|
|
),
|
|
))
|
|
};
|
|
box_slot.set(LayoutBox::FlexLevel(box_.clone()));
|
|
box_
|
|
},
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
// https://drafts.csswg.org/css-flexbox/#order-modified-document-order
|
|
children.sort_by_key(|child| match &*child.borrow() {
|
|
FlexLevelBox::FlexItem(item) => item.style.clone_order(),
|
|
|
|
// “Absolutely-positioned children of a flex container are treated
|
|
// as having order: 0 for the purpose of determining their painting order
|
|
// relative to flex items.”
|
|
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => 0,
|
|
});
|
|
|
|
FlexContainer { children }
|
|
}
|
|
}
|
|
|
|
impl FlexContainer {
|
|
pub(crate) fn layout(
|
|
&self,
|
|
layout_context: &LayoutContext,
|
|
positioning_context: &mut PositioningContext,
|
|
containing_block: &ContainingBlock,
|
|
tree_rank: usize,
|
|
) -> IndependentLayout {
|
|
// FIXME
|
|
let _ = layout_context;
|
|
let _ = positioning_context;
|
|
let _ = containing_block;
|
|
let _ = tree_rank;
|
|
IndependentLayout {
|
|
fragments: Vec::new(),
|
|
content_block_size: Length::zero(),
|
|
}
|
|
}
|
|
}
|