diff options
author | bors-servo <servo-ops@mozilla.com> | 2020-06-10 17:19:16 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-10 17:19:16 -0400 |
commit | d85c6ee341aca37ca0385bf0ccdd3665eb6f6913 (patch) | |
tree | f797a62ff03755a0a06d6ec12ed4b9cb13a84b88 /components/layout_2020/flexbox/construct.rs | |
parent | 0367ec2e8b972d5a4f1b88fb39a5ed691e976057 (diff) | |
parent | dd0d6a2a6ffa52b45721448dc76ed44bde93b589 (diff) | |
download | servo-d85c6ee341aca37ca0385bf0ccdd3665eb6f6913.tar.gz servo-d85c6ee341aca37ca0385bf0ccdd3665eb6f6913.zip |
Auto merge of #26838 - servo:flexbox, r=nox
Flexbox, the boring parts
I have a local branch starting to implement https://drafts.csswg.org/css-flexbox/#layout-algorithm. It’s not PR-ready yet, but it’s going to be large so here are some of the less interesting parts meaningful by themselves. Landing the module split in particular will help reduce merge conflicts.
CC https://github.com/servo/servo/issues/26639
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 } + } +} |