use std::borrow::Cow;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use style::values::specified::text::TextDecorationLine;
use super::{FlexContainer, FlexItemBox, FlexLevelBox};
use crate::cell::ArcRefCell;
use crate::context::LayoutContext;
use crate::dom::{BoxSlot, LayoutBox, NodeExt};
use crate::dom_traversal::{Contents, NodeAndStyleInfo, NonReplacedContents, TraversalHandler};
use crate::flow::inline::construct::InlineFormattingContextBuilder;
use crate::flow::{BlockContainer, BlockFormattingContext};
use crate::formatting_contexts::{
IndependentFormattingContext, NonReplacedFormattingContext,
NonReplacedFormattingContextContents,
};
use crate::positioned::AbsolutelyPositionedBox;
use crate::style_ext::DisplayGeneratingBox;
impl FlexContainer {
pub fn construct<'dom>(
context: &LayoutContext,
info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
contents: NonReplacedContents,
propagated_text_decoration_line: TextDecorationLine,
) -> Self {
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);
builder.finish()
}
}
struct FlexContainerBuilder<'a, 'dom, Node> {
context: &'a LayoutContext<'a>,
info: &'a NodeAndStyleInfo<Node>,
text_decoration_line: TextDecorationLine,
contiguous_text_runs: Vec<FlexTextRun<'dom, Node>>,
jobs: Vec<FlexLevelJob<'dom, Node>>,
has_text_runs: bool,
}
enum FlexLevelJob<'dom, Node> {
Element {
info: NodeAndStyleInfo<Node>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
},
TextRuns(Vec<FlexTextRun<'dom, Node>>),
}
struct FlexTextRun<'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(FlexTextRun {
info: info.clone(),
text,
})
}
fn handle_element(
&mut self,
info: &NodeAndStyleInfo<Node>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
) {
self.wrap_any_text_in_anonymous_block_container();
self.jobs.push(FlexLevelJob::Element {
info: info.clone(),
display,
contents,
box_slot,
})
}
}
fn is_only_document_white_space<Node>(run: &FlexTextRun<'_, Node>) -> bool {
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) {
} 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::ServoAnonymousBox,
&self.info.style,
),
)
} else {
None
};
let mut children = std::mem::take(&mut self.jobs)
.into_par_iter()
.filter_map(|job| match job {
FlexLevelJob::TextRuns(runs) => {
let mut inline_formatting_context_builder =
InlineFormattingContextBuilder::new();
for flex_text_run in runs.into_iter() {
inline_formatting_context_builder
.push_text(flex_text_run.text, &flex_text_run.info);
}
let inline_formatting_context = inline_formatting_context_builder.finish(
self.context,
self.text_decoration_line,
true, false, self.info.style.writing_mode.to_bidi_level(),
)?;
let block_formatting_context = BlockFormattingContext::from_block_container(
BlockContainer::InlineFormattingContext(inline_formatting_context),
);
let info = &self.info.new_anonymous(anonymous_style.clone().unwrap());
let non_replaced = NonReplacedFormattingContext {
base_fragment_info: info.into(),
style: info.style.clone(),
content_sizes_result: Default::default(),
contents: NonReplacedFormattingContextContents::Flow(
block_formatting_context,
),
};
Some(ArcRefCell::new(FlexLevelBox::FlexItem(FlexItemBox {
independent_formatting_context: IndependentFormattingContext::NonReplaced(
non_replaced,
),
block_content_size_cache: Default::default(),
})))
},
FlexLevelJob::Element {
info,
display,
contents,
box_slot,
} => {
let display_inside = match display {
DisplayGeneratingBox::OutsideInside { inside, .. } => inside,
DisplayGeneratingBox::LayoutInternal(_) => display.display_inside(),
};
let box_ = if info.style.get_box().position.is_absolutely_positioned() {
ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(
ArcRefCell::new(AbsolutelyPositionedBox::construct(
self.context,
&info,
display_inside,
contents,
)),
))
} else {
ArcRefCell::new(FlexLevelBox::FlexItem(FlexItemBox {
independent_formatting_context: IndependentFormattingContext::construct(
self.context,
&info,
display_inside,
contents,
self.text_decoration_line,
),
block_content_size_cache: Default::default(),
}))
};
box_slot.set(LayoutBox::FlexLevel(box_.clone()));
Some(box_)
},
})
.collect::<Vec<_>>();
children.sort_by_key(|child| match &*child.borrow() {
FlexLevelBox::FlexItem(item) => item.style().clone_order(),
FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => 0,
});
FlexContainer::new(&self.info.style, children)
}
}