diff options
Diffstat (limited to 'components/layout_2020/flexbox/construct.rs')
-rw-r--r-- | components/layout_2020/flexbox/construct.rs | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/components/layout_2020/flexbox/construct.rs b/components/layout_2020/flexbox/construct.rs new file mode 100644 index 00000000000..e23858efe7f --- /dev/null +++ b/components/layout_2020/flexbox/construct.rs @@ -0,0 +1,217 @@ +/* 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::{FlexContainer, FlexLevelBox}; +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; +use crate::fragments::Tag; +use crate::positioned::AbsolutelyPositionedBox; +use crate::sizing::{BoxContentSizes, ContentSizes, ContentSizesRequest}; +use crate::style_ext::DisplayGeneratingBox; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use std::borrow::Cow; +use style::values::specified::text::TextDecorationLine; + +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 } + } +} |