aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorNico Burns <nico@nicoburns.com>2024-11-22 09:21:01 +1300
committerGitHub <noreply@github.com>2024-11-21 20:21:01 +0000
commit6cbd89dbb0452f30477671ff72ff6c03b3fac097 (patch)
treecb2df73c07a5cddf6c05ccc9e11f4f1cb146bf2f /components
parent339062c890361017d91ec84121c833ce5ee43c84 (diff)
downloadservo-6cbd89dbb0452f30477671ff72ff6c03b3fac097.tar.gz
servo-6cbd89dbb0452f30477671ff72ff6c03b3fac097.zip
Layout: Implement CSS Grid using `taffy` (#32619)
* Add layout.grid.enabled pref Signed-off-by: Nico Burns <nico@nicoburns.com> * Add taffy dependency Signed-off-by: Nico Burns <nico@nicoburns.com> * Import taffy <-> stylo conversion code from taffy_stylo crate Signed-off-by: Nico Burns <nico@nicoburns.com> * Add `Grid` variant to DisplayInside Signed-off-by: Nico Burns <nico@nicoburns.com> * Implement CSS Grid using Taffy Signed-off-by: Nico Burns <nico@nicoburns.com> Import full stylo_taffy crate Signed-off-by: Nico Burns <nico@nicoburns.com> Squashed PR feedback changes Deduplicate is_document_only_whitespace Signed-off-by: Nico Burns <nico@nicoburns.com> Import taffy::AvailableSpace Signed-off-by: Nico Burns <nico@nicoburns.com> Rename FlexContext to TaffyContainerContext Signed-off-by: Nico Burns <nico@nicoburns.com> Eliminate references to flexbox in taffy/layout module Signed-off-by: Nico Burns <nico@nicoburns.com> Use constructors for geom types Signed-off-by: Nico Burns <nico@nicoburns.com> Remove comment about abspos elements splitting contiguous text runs Signed-off-by: Nico Burns <nico@nicoburns.com> Remove reference to flexbox in taffy/construct Signed-off-by: Nico Burns <nico@nicoburns.com> Deduplicate construction of flexbox/grid containers Signed-off-by: Nico Burns <nico@nicoburns.com> Make anonymous text runs InFlow Signed-off-by: Nico Burns <nico@nicoburns.com> Remove commented code Signed-off-by: Nico Burns <nico@nicoburns.com> Update comments Signed-off-by: Nico Burns <nico@nicoburns.com> Inline/vendor the stylo/taffy interop code Signed-off-by: Nico Burns <nico@nicoburns.com> * Update test expectations Signed-off-by: Nico Burns <nico@nicoburns.com> * Fix nits from PR review Signed-off-by: Nico Burns <nico@nicoburns.com> --------- Signed-off-by: Nico Burns <nico@nicoburns.com>
Diffstat (limited to 'components')
-rw-r--r--components/config/prefs.rs3
-rw-r--r--components/layout_2020/Cargo.toml1
-rw-r--r--components/layout_2020/construct_modern.rs (renamed from components/layout_2020/flexbox/construct.rs)205
-rw-r--r--components/layout_2020/dom.rs2
-rw-r--r--components/layout_2020/flexbox/mod.rs54
-rw-r--r--components/layout_2020/flow/root.rs19
-rw-r--r--components/layout_2020/formatting_contexts.rs17
-rw-r--r--components/layout_2020/lib.rs2
-rw-r--r--components/layout_2020/positioned.rs4
-rw-r--r--components/layout_2020/style_ext.rs3
-rw-r--r--components/layout_2020/taffy/layout.rs604
-rw-r--r--components/layout_2020/taffy/mod.rs122
-rw-r--r--components/layout_2020/taffy/stylo_taffy/convert.rs338
-rw-r--r--components/layout_2020/taffy/stylo_taffy/mod.rs9
-rw-r--r--components/layout_2020/taffy/stylo_taffy/wrapper.rs226
15 files changed, 1500 insertions, 109 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index d525f7bf4fa..e76c6c3128b 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -547,6 +547,9 @@ mod gen {
flexbox: {
enabled: bool,
},
+ grid: {
+ enabled: bool,
+ },
legacy_layout: bool,
#[serde(default = "default_layout_threads")]
threads: i64,
diff --git a/components/layout_2020/Cargo.toml b/components/layout_2020/Cargo.toml
index bcd04e67f86..31a78ed2361 100644
--- a/components/layout_2020/Cargo.toml
+++ b/components/layout_2020/Cargo.toml
@@ -49,6 +49,7 @@ servo_geometry = { path = "../geometry" }
servo_url = { path = "../url" }
style = { workspace = true }
style_traits = { workspace = true }
+taffy = { workspace = true }
tracing = { workspace = true, optional = true }
unicode-bidi = { workspace = true }
unicode-script = { workspace = true }
diff --git a/components/layout_2020/flexbox/construct.rs b/components/layout_2020/construct_modern.rs
index 260e5266b82..5ff9aa08216 100644
--- a/components/layout_2020/flexbox/construct.rs
+++ b/components/layout_2020/construct_modern.rs
@@ -2,80 +2,80 @@
* 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/. */
+//! Layout construction code that is shared between modern layout modes (Flexbox and CSS Grid)
+
use std::borrow::Cow;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
-use style::values::specified::text::TextDecorationLine;
+use style::values::computed::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::dom::{BoxSlot, NodeExt};
+use crate::dom_traversal::{Contents, NodeAndStyleInfo, 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()
- }
-}
-
/// <https://drafts.csswg.org/css-flexbox/#flex-items>
-struct FlexContainerBuilder<'a, 'dom, Node> {
+pub(crate) struct ModernContainerBuilder<'a, 'dom, Node> {
context: &'a LayoutContext<'a>,
info: &'a NodeAndStyleInfo<Node>,
text_decoration_line: TextDecorationLine,
- contiguous_text_runs: Vec<FlexTextRun<'dom, Node>>,
+ contiguous_text_runs: Vec<ModernContainerTextRun<'dom, Node>>,
/// To be run in parallel with rayon in `finish`
- jobs: Vec<FlexLevelJob<'dom, Node>>,
+ jobs: Vec<ModernContainerJob<'dom, Node>>,
has_text_runs: bool,
}
-enum FlexLevelJob<'dom, Node> {
- /// Or pseudo-element
- Element {
+enum ModernContainerJob<'dom, Node> {
+ ElementOrPseudoElement {
info: NodeAndStyleInfo<Node>,
display: DisplayGeneratingBox,
contents: Contents,
box_slot: BoxSlot<'dom>,
},
- TextRuns(Vec<FlexTextRun<'dom, Node>>),
+ TextRuns(Vec<ModernContainerTextRun<'dom, Node>>),
}
-struct FlexTextRun<'dom, Node> {
+struct ModernContainerTextRun<'dom, Node> {
info: NodeAndStyleInfo<Node>,
text: Cow<'dom, str>,
}
-impl<'a, 'dom, Node: 'dom> TraversalHandler<'dom, Node> for FlexContainerBuilder<'a, 'dom, Node>
+impl<Node> ModernContainerTextRun<'_, Node> {
+ /// <https://drafts.csswg.org/css-text/#white-space>
+ fn is_only_document_white_space(&self) -> bool {
+ // FIXME: is this the right definition? See
+ // https://github.com/w3c/csswg-drafts/issues/5146
+ // https://github.com/w3c/csswg-drafts/issues/5147
+ self.text
+ .bytes()
+ .all(|byte| matches!(byte, b' ' | b'\n' | b'\t'))
+ }
+}
+
+pub(crate) enum ModernItemKind {
+ InFlow,
+ OutOfFlow,
+}
+
+pub(crate) struct ModernItem<'dom> {
+ pub kind: ModernItemKind,
+ pub order: i32,
+ pub box_slot: Option<BoxSlot<'dom>>,
+ pub formatting_context: IndependentFormattingContext,
+}
+
+impl<'a, 'dom, Node: 'dom> TraversalHandler<'dom, Node> for ModernContainerBuilder<'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 {
+ self.contiguous_text_runs.push(ModernContainerTextRun {
info: info.clone(),
text,
})
@@ -89,12 +89,9 @@ where
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 {
+ self.jobs.push(ModernContainerJob::ElementOrPseudoElement {
info: info.clone(),
display,
contents,
@@ -103,31 +100,39 @@ where
}
}
-/// <https://drafts.csswg.org/css-text/#white-space>
-fn is_only_document_white_space<Node>(run: &FlexTextRun<'_, 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>
+impl<'a, 'dom, Node: 'dom> ModernContainerBuilder<'a, 'dom, Node>
where
Node: NodeExt<'dom>,
{
+ pub fn new(
+ context: &'a LayoutContext<'a>,
+ info: &'a NodeAndStyleInfo<Node>,
+ text_decoration_line: TextDecorationLine,
+ ) -> Self {
+ ModernContainerBuilder {
+ context,
+ info,
+ text_decoration_line,
+ contiguous_text_runs: Vec::new(),
+ jobs: Vec::new(),
+ has_text_runs: false,
+ }
+ }
+
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) {
+ if runs
+ .iter()
+ .all(ModernContainerTextRun::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.jobs.push(ModernContainerJob::TextRuns(runs));
self.has_text_runs = true;
}
}
- fn finish(mut self) -> FlexContainer {
+ pub(crate) fn finish(mut self) -> Vec<ModernItem<'dom>> {
self.wrap_any_text_in_anonymous_block_container();
let anonymous_style = if self.has_text_runs {
@@ -145,10 +150,10 @@ where
None
};
- let mut children = std::mem::take(&mut self.jobs)
+ let mut children: Vec<ModernItem> = std::mem::take(&mut self.jobs)
.into_par_iter()
.filter_map(|job| match job {
- FlexLevelJob::TextRuns(runs) => {
+ ModernContainerJob::TextRuns(runs) => {
let mut inline_formatting_context_builder =
InlineFormattingContextBuilder::new();
for flex_text_run in runs.into_iter() {
@@ -176,62 +181,58 @@ where
block_formatting_context,
),
};
-
- Some(ArcRefCell::new(FlexLevelBox::FlexItem(FlexItemBox {
- independent_formatting_context: IndependentFormattingContext::NonReplaced(
- non_replaced,
- ),
- block_content_size_cache: Default::default(),
- })))
+ let formatting_context =
+ IndependentFormattingContext::NonReplaced(non_replaced);
+
+ Some(ModernItem {
+ kind: ModernItemKind::InFlow,
+ order: 0,
+ box_slot: None,
+ formatting_context,
+ })
},
- FlexLevelJob::Element {
+ ModernContainerJob::ElementOrPseudoElement {
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() {
- // https://drafts.csswg.org/css-flexbox/#abspos-items
- ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(
- ArcRefCell::new(AbsolutelyPositionedBox::construct(
- self.context,
- &info,
- display_inside,
- contents,
- )),
- ))
+ let is_abspos = info.style.get_box().position.is_absolutely_positioned();
+ let formatting_context = IndependentFormattingContext::construct(
+ self.context,
+ &info,
+ display.display_inside(),
+ contents,
+ // Text decorations are not propagated to any out-of-flow descendants.
+ if is_abspos {
+ TextDecorationLine::NONE
+ } else {
+ self.text_decoration_line
+ },
+ );
+
+ if is_abspos {
+ Some(ModernItem {
+ kind: ModernItemKind::OutOfFlow,
+ order: 0,
+ box_slot: Some(box_slot),
+ formatting_context,
+ })
} 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_)
+ Some(ModernItem {
+ kind: ModernItemKind::InFlow,
+ order: info.style.clone_order(),
+ box_slot: Some(box_slot),
+ formatting_context,
+ })
+ }
},
})
.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,
- });
+ children.sort_by_key(|child| child.order);
- FlexContainer::new(&self.info.style, children)
+ children
}
}
diff --git a/components/layout_2020/dom.rs b/components/layout_2020/dom.rs
index e742b7e663a..1b561969121 100644
--- a/components/layout_2020/dom.rs
+++ b/components/layout_2020/dom.rs
@@ -27,6 +27,7 @@ use crate::flow::inline::InlineItem;
use crate::flow::BlockLevelBox;
use crate::geom::PhysicalSize;
use crate::replaced::{CanvasInfo, CanvasSource};
+use crate::taffy::TaffyItemBox;
/// The data that is stored in each DOM node that is used by layout.
#[derive(Default)]
@@ -44,6 +45,7 @@ pub(super) enum LayoutBox {
InlineBox(ArcRefCell<InlineBox>),
InlineLevel(ArcRefCell<InlineItem>),
FlexLevel(ArcRefCell<FlexLevelBox>),
+ TaffyItemBox(ArcRefCell<TaffyItemBox>),
}
/// A wrapper for [`InnerDOMLayoutData`]. This is necessary to give the entire data
diff --git a/components/layout_2020/flexbox/mod.rs b/components/layout_2020/flexbox/mod.rs
index 0e6f7837425..ceca8bad161 100644
--- a/components/layout_2020/flexbox/mod.rs
+++ b/components/layout_2020/flexbox/mod.rs
@@ -13,13 +13,17 @@ use style::properties::longhands::flex_wrap::computed_value::T as FlexWrap;
use style::properties::ComputedValues;
use style::values::computed::{AlignContent, JustifyContent};
use style::values::specified::align::AlignFlags;
+use style::values::specified::text::TextDecorationLine;
use crate::cell::ArcRefCell;
+use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
+use crate::context::LayoutContext;
+use crate::dom::{LayoutBox, NodeExt};
+use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragment_tree::BaseFragmentInfo;
use crate::positioned::AbsolutelyPositionedBox;
-mod construct;
mod geom;
mod layout;
@@ -95,14 +99,45 @@ pub(crate) struct FlexContainer {
}
impl FlexContainer {
- pub(crate) fn new(
- style: &ServoArc<ComputedValues>,
- children: Vec<ArcRefCell<FlexLevelBox>>,
+ 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 = ModernContainerBuilder::new(context, info, text_decoration_line);
+ contents.traverse(context, info, &mut builder);
+ let items = builder.finish();
+
+ let children = items
+ .into_iter()
+ .map(|item| {
+ let box_ = match item.kind {
+ ModernItemKind::InFlow => ArcRefCell::new(FlexLevelBox::FlexItem(
+ FlexItemBox::new(item.formatting_context),
+ )),
+ ModernItemKind::OutOfFlow => {
+ let abs_pos_box =
+ ArcRefCell::new(AbsolutelyPositionedBox::new(item.formatting_context));
+ ArcRefCell::new(FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(abs_pos_box))
+ },
+ };
+
+ if let Some(box_slot) = item.box_slot {
+ box_slot.set(LayoutBox::FlexLevel(box_.clone()));
+ }
+
+ box_
+ })
+ .collect();
+
Self {
children,
- style: style.clone(),
- config: FlexContainerConfig::new(style),
+ style: info.style.clone(),
+ config: FlexContainerConfig::new(&info.style),
}
}
}
@@ -127,6 +162,13 @@ impl std::fmt::Debug for FlexItemBox {
}
impl FlexItemBox {
+ fn new(independent_formatting_context: IndependentFormattingContext) -> Self {
+ Self {
+ independent_formatting_context,
+ block_content_size_cache: Default::default(),
+ }
+ }
+
fn style(&self) -> &ServoArc<ComputedValues> {
self.independent_formatting_context.style()
}
diff --git a/components/layout_2020/flow/root.rs b/components/layout_2020/flow/root.rs
index 885e9be2024..b1c62416ecc 100644
--- a/components/layout_2020/flow/root.rs
+++ b/components/layout_2020/flow/root.rs
@@ -30,6 +30,7 @@ use crate::geom::{LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSize};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
use crate::replaced::ReplacedContent;
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayInside};
+use crate::taffy::{TaffyItemBox, TaffyItemBoxInner};
use crate::DefiniteContainingBlock;
#[derive(Serialize)]
@@ -127,6 +128,7 @@ impl BoxTree {
AbsolutelyPositionedBlockLevelBox(ArcRefCell<BlockLevelBox>),
AbsolutelyPositionedInlineLevelBox(ArcRefCell<InlineItem>, usize),
AbsolutelyPositionedFlexLevelBox(ArcRefCell<FlexLevelBox>),
+ AbsolutelyPositionedTaffyLevelBox(ArcRefCell<TaffyItemBox>),
}
fn update_point<'dom, Node>(
@@ -203,6 +205,17 @@ impl BoxTree {
},
_ => return None,
},
+ LayoutBox::TaffyItemBox(taffy_level_box) => match &taffy_level_box
+ .borrow()
+ .taffy_level_box
+ {
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(_)
+ if box_style.position.is_absolutely_positioned() =>
+ {
+ UpdatePoint::AbsolutelyPositionedTaffyLevelBox(taffy_level_box.clone())
+ },
+ _ => return None,
+ },
};
Some((primary_style.clone(), display_inside, update_point))
}
@@ -238,6 +251,12 @@ impl BoxTree {
out_of_flow_absolutely_positioned_box,
);
},
+ UpdatePoint::AbsolutelyPositionedTaffyLevelBox(taffy_level_box) => {
+ taffy_level_box.borrow_mut().taffy_level_box =
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(
+ out_of_flow_absolutely_positioned_box,
+ );
+ },
}
return true;
}
diff --git a/components/layout_2020/formatting_contexts.rs b/components/layout_2020/formatting_contexts.rs
index 8b638b028f7..494e9859983 100644
--- a/components/layout_2020/formatting_contexts.rs
+++ b/components/layout_2020/formatting_contexts.rs
@@ -23,6 +23,7 @@ use crate::replaced::ReplacedContent;
use crate::sizing::{self, InlineContentSizesResult};
use crate::style_ext::{AspectRatio, DisplayInside};
use crate::table::Table;
+use crate::taffy::TaffyContainer;
use crate::{
ConstraintSpace, ContainingBlock, IndefiniteContainingBlock, LogicalVec2, SizeConstraint,
};
@@ -58,6 +59,7 @@ pub(crate) struct ReplacedFormattingContext {
pub(crate) enum NonReplacedFormattingContextContents {
Flow(BlockFormattingContext),
Flex(FlexContainer),
+ Grid(TaffyContainer),
Table(Table),
// Other layout modes go here
}
@@ -129,6 +131,14 @@ impl IndependentFormattingContext {
),
)
},
+ DisplayInside::Grid => {
+ NonReplacedFormattingContextContents::Grid(TaffyContainer::construct(
+ context,
+ node_and_style_info,
+ non_replaced_contents,
+ propagated_text_decoration_line,
+ ))
+ },
DisplayInside::Flex => {
NonReplacedFormattingContextContents::Flex(FlexContainer::construct(
context,
@@ -268,6 +278,12 @@ impl NonReplacedFormattingContext {
containing_block_for_children,
containing_block,
),
+ NonReplacedFormattingContextContents::Grid(fc) => fc.layout(
+ layout_context,
+ positioning_context,
+ containing_block_for_children,
+ containing_block,
+ ),
NonReplacedFormattingContextContents::Table(table) => table.layout(
layout_context,
positioning_context,
@@ -331,6 +347,7 @@ impl NonReplacedFormattingContextContents {
.contents
.inline_content_sizes(layout_context, constraint_space),
Self::Flex(inner) => inner.inline_content_sizes(layout_context, constraint_space),
+ Self::Grid(inner) => inner.inline_content_sizes(layout_context, constraint_space),
Self::Table(table) => table.inline_content_sizes(layout_context, constraint_space),
}
}
diff --git a/components/layout_2020/lib.rs b/components/layout_2020/lib.rs
index 5a4dfc5e72b..341e5d3f320 100644
--- a/components/layout_2020/lib.rs
+++ b/components/layout_2020/lib.rs
@@ -14,8 +14,10 @@ pub mod flow;
mod formatting_contexts;
mod fragment_tree;
pub mod geom;
+mod taffy;
#[macro_use]
pub mod layout_debug;
+mod construct_modern;
mod lists;
mod positioned;
pub mod query;
diff --git a/components/layout_2020/positioned.rs b/components/layout_2020/positioned.rs
index 5a85733a603..2e4aebaf4c4 100644
--- a/components/layout_2020/positioned.rs
+++ b/components/layout_2020/positioned.rs
@@ -56,6 +56,10 @@ pub(crate) struct HoistedAbsolutelyPositionedBox {
}
impl AbsolutelyPositionedBox {
+ pub fn new(context: IndependentFormattingContext) -> Self {
+ Self { context }
+ }
+
pub fn construct<'dom>(
context: &LayoutContext,
node_info: &NodeAndStyleInfo<impl NodeExt<'dom>>,
diff --git a/components/layout_2020/style_ext.rs b/components/layout_2020/style_ext.rs
index 0e9e873c701..0db2d8d7f8f 100644
--- a/components/layout_2020/style_ext.rs
+++ b/components/layout_2020/style_ext.rs
@@ -91,6 +91,7 @@ pub(crate) enum DisplayInside {
Flow { is_list_item: bool },
FlowRoot { is_list_item: bool },
Flex,
+ Grid,
Table,
}
@@ -1064,7 +1065,7 @@ impl From<stylo::Display> for Display {
is_list_item: packed.is_list_item(),
},
stylo::DisplayInside::Flex => DisplayInside::Flex,
- stylo::DisplayInside::Grid => todo!("Grid support is not yet implemented."),
+ stylo::DisplayInside::Grid => DisplayInside::Grid,
// These should not be values of DisplayInside, but oh well
stylo::DisplayInside::None => return Display::None,
diff --git a/components/layout_2020/taffy/layout.rs b/components/layout_2020/taffy/layout.rs
new file mode 100644
index 00000000000..e1d35380513
--- /dev/null
+++ b/components/layout_2020/taffy/layout.rs
@@ -0,0 +1,604 @@
+/* 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 app_units::Au;
+use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
+use style::properties::ComputedValues;
+use style::values::generics::length::{GenericLengthPercentageOrAuto, LengthPercentageOrAuto};
+use style::values::specified::align::AlignFlags;
+use style::values::specified::box_::DisplayInside;
+use style::Zero;
+use taffy::style_helpers::{TaffyMaxContent, TaffyMinContent};
+use taffy::{AvailableSpace, MaybeMath};
+
+use super::{TaffyContainer, TaffyItemBox, TaffyItemBoxInner, TaffyStyloStyle};
+use crate::cell::ArcRefCell;
+use crate::context::LayoutContext;
+use crate::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout};
+use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment};
+use crate::geom::{
+ LogicalSides, LogicalVec2, PhysicalPoint, PhysicalRect, PhysicalSides, PhysicalSize, Size,
+ SizeConstraint,
+};
+use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
+use crate::sizing::{ContentSizes, InlineContentSizesResult};
+use crate::style_ext::ComputedValuesExt;
+use crate::{ConstraintSpace, ContainingBlock};
+
+const DUMMY_NODE_ID: taffy::NodeId = taffy::NodeId::new(u64::MAX);
+
+fn resolve_content_size(constraint: AvailableSpace, content_sizes: ContentSizes) -> f32 {
+ match constraint {
+ AvailableSpace::Definite(limit) => {
+ let min = content_sizes.min_content.to_f32_px();
+ let max = content_sizes.max_content.to_f32_px();
+ limit.min(max).max(min)
+ },
+ AvailableSpace::MinContent => content_sizes.min_content.to_f32_px(),
+ AvailableSpace::MaxContent => content_sizes.max_content.to_f32_px(),
+ }
+}
+
+#[inline(always)]
+fn with_independant_formatting_context<T>(
+ item: &mut TaffyItemBoxInner,
+ cb: impl FnOnce(&mut IndependentFormattingContext) -> T,
+) -> T {
+ match item {
+ TaffyItemBoxInner::InFlowBox(ref mut context) => cb(context),
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(ref abspos_box) => {
+ let mut abspos_box = AtomicRefCell::borrow_mut(abspos_box);
+ cb(&mut abspos_box.context)
+ },
+ }
+}
+
+/// Layout parameters and intermediate results about a taffy container,
+/// grouped to avoid passing around many parameters
+struct TaffyContainerContext<'a> {
+ source_child_nodes: &'a [ArcRefCell<TaffyItemBox>],
+ layout_context: &'a LayoutContext<'a>,
+ positioning_context: &'a mut PositioningContext,
+ content_box_size_override: &'a ContainingBlock<'a>,
+ style: &'a ComputedValues,
+}
+
+struct ChildIter(std::ops::Range<usize>);
+impl Iterator for ChildIter {
+ type Item = taffy::NodeId;
+ fn next(&mut self) -> Option<Self::Item> {
+ self.0.next().map(taffy::NodeId::from)
+ }
+}
+
+impl taffy::TraversePartialTree for TaffyContainerContext<'_> {
+ type ChildIter<'a> = ChildIter where Self: 'a;
+
+ fn child_ids(&self, _node_id: taffy::NodeId) -> Self::ChildIter<'_> {
+ ChildIter(0..self.source_child_nodes.len())
+ }
+
+ fn child_count(&self, _node_id: taffy::NodeId) -> usize {
+ self.source_child_nodes.len()
+ }
+
+ fn get_child_id(&self, _node_id: taffy::NodeId, index: usize) -> taffy::NodeId {
+ taffy::NodeId::from(index)
+ }
+}
+
+impl taffy::LayoutPartialTree for TaffyContainerContext<'_> {
+ type CoreContainerStyle<'a> = TaffyStyloStyle<&'a ComputedValues> where Self: 'a;
+ type CacheMut<'b> = AtomicRefMut<'b, taffy::Cache> where Self: 'b;
+
+ fn get_core_container_style(&self, _node_id: taffy::NodeId) -> Self::CoreContainerStyle<'_> {
+ TaffyStyloStyle(self.style)
+ }
+
+ fn set_unrounded_layout(&mut self, node_id: taffy::NodeId, layout: &taffy::Layout) {
+ let id = usize::from(node_id);
+ (*self.source_child_nodes[id]).borrow_mut().taffy_layout = *layout;
+ }
+
+ fn get_cache_mut(&mut self, node_id: taffy::NodeId) -> AtomicRefMut<'_, taffy::Cache> {
+ let id = usize::from(node_id);
+ let mut_ref: AtomicRefMut<'_, _> = (*self.source_child_nodes[id]).borrow_mut();
+ AtomicRefMut::map(mut_ref, |node| &mut node.taffy_layout_cache)
+ }
+
+ fn compute_child_layout(
+ &mut self,
+ node_id: taffy::NodeId,
+ inputs: taffy::LayoutInput,
+ ) -> taffy::LayoutOutput {
+ let mut child = (*self.source_child_nodes[usize::from(node_id)]).borrow_mut();
+ let child = &mut *child;
+
+ fn option_f32_to_lpa(input: Option<f32>) -> LengthPercentageOrAuto<Au> {
+ match input {
+ None => LengthPercentageOrAuto::Auto,
+ Some(length) => LengthPercentageOrAuto::LengthPercentage(Au::from_f32_px(length)),
+ }
+ }
+
+ fn option_f32_to_size(input: Option<f32>) -> Size<Au> {
+ match input {
+ None => Size::Initial,
+ Some(length) => Size::Numeric(Au::from_f32_px(length)),
+ }
+ }
+
+ with_independant_formatting_context(
+ &mut child.taffy_level_box,
+ |independent_context| -> taffy::LayoutOutput {
+ match independent_context {
+ IndependentFormattingContext::Replaced(replaced) => {
+ // TODO: re-evaluate sizing constraint conversions in light of recent layout_2020 changes
+ let containing_block = &self.content_box_size_override;
+
+ // Adjust known_dimensions from border box to content box
+ let pbm = replaced.style.padding_border_margin(containing_block);
+ let pb_sum = pbm.padding_border_sums.map(|v| v.to_f32_px());
+ let content_box_known_dimensions = taffy::Size {
+ width: inputs
+ .known_dimensions
+ .width
+ .map(|width| width - pb_sum.inline),
+ height: inputs
+ .known_dimensions
+ .height
+ .map(|height| height - pb_sum.block),
+ };
+
+ let content_box_size = replaced
+ .contents
+ .used_size_as_if_inline_element_from_content_box_sizes(
+ containing_block,
+ &replaced.style,
+ LogicalVec2 {
+ inline: option_f32_to_size(content_box_known_dimensions.width),
+ block: option_f32_to_size(content_box_known_dimensions.height),
+ },
+ LogicalVec2 {
+ inline: Size::Numeric(Au::zero()),
+ block: Size::Numeric(Au::zero()),
+ },
+ LogicalVec2 {
+ inline: Size::Initial,
+ block: Size::Initial,
+ },
+ pbm.padding_border_sums,
+ )
+ .to_physical_size(self.style.writing_mode);
+
+ child.child_fragments = replaced.contents.make_fragments(
+ &replaced.style,
+ containing_block,
+ content_box_size,
+ );
+
+ let computed_size = taffy::Size {
+ width: inputs.known_dimensions.width.unwrap_or_else(|| {
+ content_box_size.width.to_f32_px() +
+ pbm.padding_border_sums.inline.to_f32_px()
+ }),
+ height: inputs.known_dimensions.height.unwrap_or_else(|| {
+ content_box_size.height.to_f32_px() +
+ pbm.padding_border_sums.block.to_f32_px()
+ }),
+ };
+ let size = inputs.known_dimensions.unwrap_or(computed_size);
+ taffy::LayoutOutput {
+ size,
+ ..taffy::LayoutOutput::DEFAULT
+ }
+ },
+
+ IndependentFormattingContext::NonReplaced(non_replaced) => {
+ // TODO: re-evaluate sizing constraint conversions in light of recent layout_2020 changes
+ let containing_block = &self.content_box_size_override;
+
+ // Adjust known_dimensions from border box to content box
+ let pbm = non_replaced.style.padding_border_margin(containing_block);
+ let margin_sum = pbm.margin.auto_is(Au::zero).sum();
+ let content_box_inset =
+ (pbm.padding_border_sums + margin_sum).map(|v| v.to_f32_px());
+ let content_box_known_dimensions =
+ taffy::Size {
+ width: inputs.known_dimensions.width.map(|width| {
+ width - pbm.padding_border_sums.inline.to_f32_px()
+ }),
+ height: inputs.known_dimensions.height.map(|height| {
+ height - pbm.padding_border_sums.block.to_f32_px()
+ }),
+ };
+
+ // Compute inline size
+ let inline_size = content_box_known_dimensions.width.unwrap_or_else(|| {
+ let constraint_space = ConstraintSpace {
+ // TODO: pass min- and max- size
+ block_size: SizeConstraint::new(
+ inputs.parent_size.height.map(Au::from_f32_px),
+ Au::zero(),
+ None,
+ ),
+ writing_mode: self.style.writing_mode,
+ };
+
+ let result = non_replaced
+ .inline_content_sizes(self.layout_context, &constraint_space);
+ let adjusted_available_space = inputs
+ .available_space
+ .width
+ .map_definite_value(|width| width - content_box_inset.inline);
+
+ resolve_content_size(adjusted_available_space, result.sizes)
+ });
+
+ let maybe_block_size =
+ option_f32_to_lpa(content_box_known_dimensions.height);
+ let content_box_size_override = ContainingBlock {
+ inline_size: Au::from_f32_px(inline_size),
+ block_size: maybe_block_size,
+ style: &non_replaced.style,
+ };
+
+ let layout = {
+ let mut child_positioning_context = PositioningContext::new_for_subtree(
+ self.positioning_context
+ .collects_for_nearest_positioned_ancestor(),
+ );
+ let layout = non_replaced.layout(
+ self.layout_context,
+ &mut child_positioning_context,
+ &content_box_size_override,
+ containing_block,
+ );
+
+ // Store layout data on child for later access
+ child.positioning_context = child_positioning_context;
+
+ layout
+ };
+
+ child.child_fragments = layout.fragments;
+
+ let block_size = layout.content_block_size.to_f32_px();
+
+ let computed_size = taffy::Size {
+ width: inline_size + pbm.padding_border_sums.inline.to_f32_px(),
+ height: block_size + pbm.padding_border_sums.block.to_f32_px(),
+ };
+ let size = inputs.known_dimensions.unwrap_or(computed_size);
+
+ taffy::LayoutOutput {
+ size,
+ first_baselines: taffy::Point {
+ x: None,
+ y: layout.baselines.first.map(|au| au.to_f32_px()),
+ },
+ ..taffy::LayoutOutput::DEFAULT
+ }
+ },
+ }
+ },
+ )
+ }
+}
+
+impl taffy::LayoutGridContainer for TaffyContainerContext<'_> {
+ type GridContainerStyle<'a> = TaffyStyloStyle<&'a ComputedValues>
+ where
+ Self: 'a;
+
+ type GridItemStyle<'a> = TaffyStyloStyle<AtomicRef<'a, ComputedValues>>
+ where
+ Self: 'a;
+
+ fn get_grid_container_style(
+ &self,
+ _node_id: taffy::prelude::NodeId,
+ ) -> Self::GridContainerStyle<'_> {
+ TaffyStyloStyle(self.style)
+ }
+
+ fn get_grid_child_style(
+ &self,
+ child_node_id: taffy::prelude::NodeId,
+ ) -> Self::GridItemStyle<'_> {
+ let id = usize::from(child_node_id);
+ let child = (*self.source_child_nodes[id]).borrow();
+ TaffyStyloStyle(AtomicRef::map(child, |c| &*c.style))
+ }
+}
+
+impl TaffyContainer {
+ pub fn inline_content_sizes(
+ &self,
+ layout_context: &LayoutContext,
+ _constraint_space: &ConstraintSpace,
+ ) -> InlineContentSizesResult {
+ let style = &self.style;
+
+ let max_content_inputs = taffy::LayoutInput {
+ run_mode: taffy::RunMode::ComputeSize,
+ sizing_mode: taffy::SizingMode::InherentSize,
+ axis: taffy::RequestedAxis::Horizontal,
+ vertical_margins_are_collapsible: taffy::Line::FALSE,
+
+ known_dimensions: taffy::Size::NONE,
+ parent_size: taffy::Size::NONE,
+ available_space: taffy::Size::MAX_CONTENT,
+ };
+
+ let min_content_inputs = taffy::LayoutInput {
+ available_space: taffy::Size::MIN_CONTENT,
+ ..max_content_inputs
+ };
+
+ let containing_block = &ContainingBlock {
+ inline_size: Au::zero(),
+ block_size: GenericLengthPercentageOrAuto::Auto,
+ style,
+ };
+
+ let mut grid_context = TaffyContainerContext {
+ layout_context,
+ positioning_context:
+ &mut PositioningContext::new_for_containing_block_for_all_descendants(),
+ content_box_size_override: containing_block,
+ style,
+ source_child_nodes: &self.children,
+ };
+
+ let (max_content_output, min_content_output) = match style.clone_display().inside() {
+ DisplayInside::Grid => {
+ let max_content_output = taffy::compute_grid_layout(
+ &mut grid_context,
+ DUMMY_NODE_ID,
+ max_content_inputs,
+ );
+ let min_content_output = taffy::compute_grid_layout(
+ &mut grid_context,
+ DUMMY_NODE_ID,
+ min_content_inputs,
+ );
+ (max_content_output, min_content_output)
+ },
+ _ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
+ };
+
+ let pb_sums = style
+ .padding_border_margin(containing_block)
+ .padding_border_sums;
+
+ InlineContentSizesResult {
+ sizes: ContentSizes {
+ max_content: Au::from_f32_px(max_content_output.size.width) - pb_sums.inline,
+ min_content: Au::from_f32_px(min_content_output.size.width) - pb_sums.inline,
+ },
+
+ // TODO: determine this accurately
+ //
+ // "true" is a safe default as it will prevent Servo from performing optimizations based
+ // on the assumption that the node's size does not depend on block constraints.
+ depends_on_block_constraints: true,
+ }
+ }
+
+ /// <https://drafts.csswg.org/css-grid/#layout-algorithm>
+ pub(crate) fn layout(
+ &self,
+ layout_context: &LayoutContext,
+ positioning_context: &mut PositioningContext,
+ content_box_size_override: &ContainingBlock,
+ containing_block: &ContainingBlock,
+ ) -> IndependentLayout {
+ let mut container_ctx = TaffyContainerContext {
+ layout_context,
+ positioning_context,
+ content_box_size_override,
+ style: content_box_size_override.style,
+ source_child_nodes: &self.children,
+ };
+
+ fn auto_or_to_option<T>(input: GenericLengthPercentageOrAuto<T>) -> Option<T> {
+ match input {
+ LengthPercentageOrAuto::LengthPercentage(val) => Some(val),
+ LengthPercentageOrAuto::Auto => None,
+ }
+ }
+
+ let container_style = &content_box_size_override.style;
+ let align_items = container_style.clone_align_items();
+ let justify_items = container_style.clone_justify_items();
+ let pbm = container_style.padding_border_margin(containing_block);
+
+ let known_dimensions = taffy::Size {
+ width: Some(
+ (content_box_size_override.inline_size + pbm.padding_border_sums.inline)
+ .to_f32_px(),
+ ),
+ height: auto_or_to_option(content_box_size_override.block_size)
+ .map(Au::to_f32_px)
+ .maybe_add(pbm.padding_border_sums.block.to_f32_px()),
+ };
+
+ let taffy_containing_block = taffy::Size {
+ width: Some(containing_block.inline_size.to_f32_px()),
+ height: auto_or_to_option(containing_block.block_size).map(Au::to_f32_px),
+ };
+
+ let layout_input = taffy::LayoutInput {
+ run_mode: taffy::RunMode::PerformLayout,
+ sizing_mode: taffy::SizingMode::InherentSize,
+ axis: taffy::RequestedAxis::Vertical,
+ vertical_margins_are_collapsible: taffy::Line::FALSE,
+
+ known_dimensions,
+ parent_size: taffy_containing_block,
+ available_space: taffy_containing_block.map(AvailableSpace::from),
+ };
+
+ let output = match container_ctx.style.clone_display().inside() {
+ DisplayInside::Grid => {
+ taffy::compute_grid_layout(&mut container_ctx, DUMMY_NODE_ID, layout_input)
+ },
+ _ => panic!("Servo is only configured to use Taffy for CSS Grid layout"),
+ };
+
+ // Convert `taffy::Layout` into Servo `Fragment`s
+ let fragments: Vec<Fragment> = self
+ .children
+ .iter()
+ .map(|child| (**child).borrow_mut())
+ .map(|mut child| {
+ fn rect_to_logical_sides<T>(rect: taffy::Rect<T>) -> LogicalSides<T> {
+ LogicalSides {
+ inline_start: rect.left,
+ inline_end: rect.right,
+ block_start: rect.top,
+ block_end: rect.bottom,
+ }
+ }
+
+ fn rect_to_physical_sides<T>(rect: taffy::Rect<T>) -> PhysicalSides<T> {
+ PhysicalSides::new(rect.top, rect.right, rect.bottom, rect.left)
+ }
+
+ fn size_and_pos_to_logical_rect<T: Default>(
+ position: taffy::Point<T>,
+ size: taffy::Size<T>,
+ ) -> PhysicalRect<T> {
+ PhysicalRect::new(
+ PhysicalPoint::new(position.x, position.y),
+ PhysicalSize::new(size.width, size.height),
+ )
+ }
+
+ let layout = &child.taffy_layout;
+
+ let padding = rect_to_physical_sides(layout.padding.map(Au::from_f32_px));
+ let border = rect_to_physical_sides(layout.border.map(Au::from_f32_px));
+ let margin = rect_to_physical_sides(layout.margin.map(Au::from_f32_px));
+ let logical_margin = rect_to_logical_sides(layout.margin.map(Au::from_f32_px));
+ let collapsed_margin = CollapsedBlockMargins::from_margin(&logical_margin);
+
+ // Compute content box size and position.
+ //
+ // For the x/y position we have to correct for the difference between the
+ // content box and the border box for both the parent and the child.
+ let content_size = size_and_pos_to_logical_rect(
+ taffy::Point {
+ x: Au::from_f32_px(
+ layout.location.x + layout.padding.left + layout.border.left,
+ ) - pbm.padding.inline_start -
+ pbm.border.inline_start,
+ y: Au::from_f32_px(
+ layout.location.y + layout.padding.top + layout.border.top,
+ ) - pbm.padding.block_start -
+ pbm.border.block_start,
+ },
+ taffy::Size {
+ width: layout.size.width -
+ layout.padding.left -
+ layout.padding.right -
+ layout.border.left -
+ layout.border.right,
+ height: layout.size.height -
+ layout.padding.top -
+ layout.padding.bottom -
+ layout.border.top -
+ layout.border.bottom,
+ }
+ .map(Au::from_f32_px),
+ );
+
+ match &mut child.taffy_level_box {
+ TaffyItemBoxInner::InFlowBox(independent_box) => {
+ let fragment = Fragment::Box(
+ BoxFragment::new(
+ independent_box.base_fragment_info(),
+ independent_box.style().clone(),
+ std::mem::take(&mut child.child_fragments),
+ content_size,
+ padding,
+ border,
+ margin,
+ None, /* clearance */
+ collapsed_margin,
+ )
+ .with_baselines(Baselines {
+ first: output.first_baselines.y.map(Au::from_f32_px),
+ last: None,
+ }),
+ );
+
+ child
+ .positioning_context
+ .adjust_static_position_of_hoisted_fragments(
+ &fragment,
+ PositioningContextLength::zero(),
+ );
+ let child_positioning_context = std::mem::replace(
+ &mut child.positioning_context,
+ PositioningContext::new_for_containing_block_for_all_descendants(),
+ );
+ container_ctx
+ .positioning_context
+ .append(child_positioning_context);
+
+ fragment
+ },
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box) => {
+ fn resolve_alignment(value: AlignFlags, auto: AlignFlags) -> AlignFlags {
+ match value {
+ AlignFlags::AUTO => auto,
+ AlignFlags::NORMAL => AlignFlags::STRETCH,
+ value => value,
+ }
+ }
+
+ let hoisted_box = AbsolutelyPositionedBox::to_hoisted(
+ abs_pos_box.clone(),
+ PhysicalRect::from_size(PhysicalSize::new(
+ Au::from_f32_px(output.size.width),
+ Au::from_f32_px(output.size.height),
+ )),
+ LogicalVec2 {
+ inline: resolve_alignment(
+ child.style.clone_align_self().0 .0,
+ align_items.0,
+ ),
+ block: resolve_alignment(
+ child.style.clone_justify_self().0 .0,
+ justify_items.computed.0,
+ ),
+ },
+ container_ctx.style.writing_mode,
+ );
+ let hoisted_fragment = hoisted_box.fragment.clone();
+ container_ctx.positioning_context.push(hoisted_box);
+ Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
+ },
+ }
+ })
+ .collect();
+
+ IndependentLayout {
+ fragments,
+ content_block_size: Au::from_f32_px(output.size.height) - pbm.padding_border_sums.block,
+ content_inline_size_for_table: Some(
+ Au::from_f32_px(output.size.width) - pbm.padding_border_sums.inline,
+ ),
+ baselines: Baselines::default(),
+
+ // TODO: determine this accurately
+ //
+ // "true" is a safe default as it will prevent Servo from performing optimizations based
+ // on the assumption that the node's size does not depend on block constraints.
+ depends_on_block_constraints: true,
+ }
+ }
+}
diff --git a/components/layout_2020/taffy/mod.rs b/components/layout_2020/taffy/mod.rs
new file mode 100644
index 00000000000..cdefacee97b
--- /dev/null
+++ b/components/layout_2020/taffy/mod.rs
@@ -0,0 +1,122 @@
+/* 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/. */
+mod layout;
+mod stylo_taffy;
+use std::fmt;
+
+use serde::Serialize;
+use servo_arc::Arc;
+use style::properties::ComputedValues;
+use style::values::computed::TextDecorationLine;
+use stylo_taffy::TaffyStyloStyle;
+
+use crate::cell::ArcRefCell;
+use crate::construct_modern::{ModernContainerBuilder, ModernItemKind};
+use crate::context::LayoutContext;
+use crate::dom::{LayoutBox, NodeExt};
+use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents};
+use crate::formatting_contexts::IndependentFormattingContext;
+use crate::fragment_tree::Fragment;
+use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
+
+#[derive(Debug, Serialize)]
+pub(crate) struct TaffyContainer {
+ children: Vec<ArcRefCell<TaffyItemBox>>,
+ #[serde(skip_serializing)]
+ style: Arc<ComputedValues>,
+}
+
+impl TaffyContainer {
+ 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 = ModernContainerBuilder::new(context, info, text_decoration_line);
+ contents.traverse(context, info, &mut builder);
+ let items = builder.finish();
+
+ let children = items
+ .into_iter()
+ .map(|item| {
+ let box_ = match item.kind {
+ ModernItemKind::InFlow => ArcRefCell::new(TaffyItemBox::new(
+ TaffyItemBoxInner::InFlowBox(item.formatting_context),
+ )),
+ ModernItemKind::OutOfFlow => {
+ let abs_pos_box =
+ ArcRefCell::new(AbsolutelyPositionedBox::new(item.formatting_context));
+ ArcRefCell::new(TaffyItemBox::new(
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(abs_pos_box),
+ ))
+ },
+ };
+
+ if let Some(box_slot) = item.box_slot {
+ box_slot.set(LayoutBox::TaffyItemBox(box_.clone()));
+ }
+
+ box_
+ })
+ .collect();
+
+ Self {
+ children,
+ style: info.style.clone(),
+ }
+ }
+}
+
+#[derive(Serialize)]
+pub(crate) struct TaffyItemBox {
+ pub(crate) taffy_layout_cache: taffy::Cache,
+ pub(crate) taffy_layout: taffy::Layout,
+ pub(crate) child_fragments: Vec<Fragment>,
+ #[serde(skip_serializing)]
+ pub(crate) positioning_context: PositioningContext,
+ #[serde(skip_serializing)]
+ pub(crate) style: Arc<ComputedValues>,
+ pub(crate) taffy_level_box: TaffyItemBoxInner,
+}
+
+#[derive(Debug, Serialize)]
+pub(crate) enum TaffyItemBoxInner {
+ InFlowBox(IndependentFormattingContext),
+ OutOfFlowAbsolutelyPositionedBox(ArcRefCell<AbsolutelyPositionedBox>),
+}
+
+impl fmt::Debug for TaffyItemBox {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TaffyItemBox")
+ .field("taffy_layout_cache", &self.taffy_layout_cache)
+ .field("taffy_layout", &self.taffy_layout)
+ .field("child_fragments", &self.child_fragments.len())
+ .field("style", &self.style)
+ .field("taffy_level_box", &self.taffy_level_box)
+ .finish()
+ }
+}
+
+impl TaffyItemBox {
+ fn new(inner: TaffyItemBoxInner) -> Self {
+ let style: Arc<ComputedValues> = match &inner {
+ TaffyItemBoxInner::InFlowBox(item) => item.style().clone(),
+ TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(absbox) => {
+ (*absbox).borrow().context.style().clone()
+ },
+ };
+
+ Self {
+ taffy_layout_cache: Default::default(),
+ taffy_layout: Default::default(),
+ child_fragments: Vec::new(),
+ positioning_context: PositioningContext::new_for_containing_block_for_all_descendants(),
+ style,
+ taffy_level_box: inner,
+ }
+ }
+}
diff --git a/components/layout_2020/taffy/stylo_taffy/convert.rs b/components/layout_2020/taffy/stylo_taffy/convert.rs
new file mode 100644
index 00000000000..9e8d8307a04
--- /dev/null
+++ b/components/layout_2020/taffy/stylo_taffy/convert.rs
@@ -0,0 +1,338 @@
+/* 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/. */
+
+/// Private module of type aliases so we can refer to stylo types with nicer names
+mod stylo {
+ pub(crate) use style::properties::generated::longhands::box_sizing::computed_value::T as BoxSizing;
+ pub(crate) use style::properties::longhands::aspect_ratio::computed_value::T as AspectRatio;
+ pub(crate) use style::properties::longhands::position::computed_value::T as Position;
+ pub(crate) use style::values::computed::{LengthPercentage, Percentage};
+ pub(crate) use style::values::generics::length::{
+ GenericLengthPercentageOrNormal, GenericMargin, GenericMaxSize, GenericSize,
+ };
+ pub(crate) use style::values::generics::position::{Inset as GenericInset, PreferredRatio};
+ pub(crate) use style::values::generics::NonNegative;
+ pub(crate) use style::values::specified::align::{AlignFlags, ContentDistribution};
+ pub(crate) use style::values::specified::box_::{
+ Display, DisplayInside, DisplayOutside, Overflow,
+ };
+ pub(crate) type MarginVal = GenericMargin<LengthPercentage>;
+ pub(crate) type InsetVal = GenericInset<Percentage, LengthPercentage>;
+ pub(crate) type Size = GenericSize<NonNegative<LengthPercentage>>;
+ pub(crate) type MaxSize = GenericMaxSize<NonNegative<LengthPercentage>>;
+
+ pub(crate) type Gap = GenericLengthPercentageOrNormal<NonNegative<LengthPercentage>>;
+
+ pub(crate) use style::computed_values::grid_auto_flow::T as GridAutoFlow;
+ pub(crate) use style::values::computed::{GridLine, GridTemplateComponent, ImplicitGridTracks};
+ pub(crate) use style::values::generics::grid::{
+ RepeatCount, TrackBreadth, TrackListValue, TrackSize,
+ };
+ pub(crate) use style::values::specified::GenericGridTemplateComponent;
+}
+
+#[inline]
+pub fn length_percentage(val: &stylo::LengthPercentage) -> taffy::LengthPercentage {
+ if let Some(length) = val.to_length() {
+ taffy::LengthPercentage::Length(length.px())
+ } else if let Some(val) = val.to_percentage() {
+ taffy::LengthPercentage::Percent(val.0)
+ } else {
+ // TODO: Support calc
+ taffy::LengthPercentage::Percent(0.0)
+ }
+}
+
+#[inline]
+pub fn dimension(val: &stylo::Size) -> taffy::Dimension {
+ match val {
+ stylo::Size::LengthPercentage(val) => length_percentage(&val.0).into(),
+ stylo::Size::Auto => taffy::Dimension::Auto,
+
+ // TODO: implement other values in Taffy
+ stylo::Size::MaxContent => taffy::Dimension::Auto,
+ stylo::Size::MinContent => taffy::Dimension::Auto,
+ stylo::Size::FitContent => taffy::Dimension::Auto,
+ stylo::Size::Stretch => taffy::Dimension::Auto,
+
+ // Anchor positioning will be flagged off for time being
+ stylo::Size::AnchorSizeFunction(_) => unreachable!(),
+ }
+}
+
+#[inline]
+pub fn max_size_dimension(val: &stylo::MaxSize) -> taffy::Dimension {
+ match val {
+ stylo::MaxSize::LengthPercentage(val) => length_percentage(&val.0).into(),
+ stylo::MaxSize::None => taffy::Dimension::Auto,
+
+ // TODO: implement other values in Taffy
+ stylo::MaxSize::MaxContent => taffy::Dimension::Auto,
+ stylo::MaxSize::MinContent => taffy::Dimension::Auto,
+ stylo::MaxSize::FitContent => taffy::Dimension::Auto,
+ stylo::MaxSize::Stretch => taffy::Dimension::Auto,
+
+ // Anchor positioning will be flagged off for time being
+ stylo::MaxSize::AnchorSizeFunction(_) => unreachable!(),
+ }
+}
+
+#[inline]
+pub fn margin(val: &stylo::MarginVal) -> taffy::LengthPercentageAuto {
+ match val {
+ stylo::MarginVal::Auto => taffy::LengthPercentageAuto::Auto,
+ stylo::MarginVal::LengthPercentage(val) => length_percentage(val).into(),
+
+ // Anchor positioning will be flagged off for time being
+ stylo::MarginVal::AnchorSizeFunction(_) => unreachable!(),
+ }
+}
+
+#[inline]
+pub fn inset(val: &stylo::InsetVal) -> taffy::LengthPercentageAuto {
+ match val {
+ stylo::InsetVal::Auto => taffy::LengthPercentageAuto::Auto,
+ stylo::InsetVal::LengthPercentage(val) => length_percentage(val).into(),
+
+ // Anchor positioning will be flagged off for time being
+ stylo::InsetVal::AnchorSizeFunction(_) => unreachable!(),
+ stylo::InsetVal::AnchorFunction(_) => unreachable!(),
+ }
+}
+
+#[inline]
+pub fn is_block(input: stylo::Display) -> bool {
+ matches!(input.outside(), stylo::DisplayOutside::Block) &&
+ matches!(
+ input.inside(),
+ stylo::DisplayInside::Flow | stylo::DisplayInside::FlowRoot
+ )
+}
+
+#[inline]
+pub fn box_generation_mode(input: stylo::Display) -> taffy::BoxGenerationMode {
+ match input.inside() {
+ stylo::DisplayInside::None => taffy::BoxGenerationMode::None,
+ _ => taffy::BoxGenerationMode::Normal,
+ }
+}
+
+#[inline]
+pub fn box_sizing(input: stylo::BoxSizing) -> taffy::BoxSizing {
+ match input {
+ stylo::BoxSizing::BorderBox => taffy::BoxSizing::BorderBox,
+ stylo::BoxSizing::ContentBox => taffy::BoxSizing::ContentBox,
+ }
+}
+
+#[inline]
+pub fn position(input: stylo::Position) -> taffy::Position {
+ match input {
+ // TODO: support position:static
+ stylo::Position::Relative => taffy::Position::Relative,
+ stylo::Position::Static => taffy::Position::Relative,
+
+ // TODO: support position:fixed and sticky
+ stylo::Position::Absolute => taffy::Position::Absolute,
+ stylo::Position::Fixed => taffy::Position::Absolute,
+ stylo::Position::Sticky => taffy::Position::Absolute,
+ }
+}
+
+#[inline]
+pub fn overflow(input: stylo::Overflow) -> taffy::Overflow {
+ // TODO: Enable Overflow::Clip in servo configuration of stylo
+ match input {
+ stylo::Overflow::Visible => taffy::Overflow::Visible,
+ stylo::Overflow::Hidden => taffy::Overflow::Hidden,
+ stylo::Overflow::Scroll => taffy::Overflow::Scroll,
+ // TODO: Support Overflow::Auto in Taffy
+ stylo::Overflow::Auto => taffy::Overflow::Scroll,
+ }
+}
+
+#[inline]
+pub fn aspect_ratio(input: stylo::AspectRatio) -> Option<f32> {
+ match input.ratio {
+ stylo::PreferredRatio::None => None,
+ stylo::PreferredRatio::Ratio(val) => Some(val.0 .0 / val.1 .0),
+ }
+}
+
+#[inline]
+pub fn content_alignment(input: stylo::ContentDistribution) -> Option<taffy::AlignContent> {
+ match input.primary().value() {
+ stylo::AlignFlags::NORMAL => None,
+ stylo::AlignFlags::AUTO => None,
+ stylo::AlignFlags::START => Some(taffy::AlignContent::Start),
+ stylo::AlignFlags::END => Some(taffy::AlignContent::End),
+ stylo::AlignFlags::FLEX_START => Some(taffy::AlignContent::FlexStart),
+ stylo::AlignFlags::STRETCH => Some(taffy::AlignContent::Stretch),
+ stylo::AlignFlags::FLEX_END => Some(taffy::AlignContent::FlexEnd),
+ stylo::AlignFlags::CENTER => Some(taffy::AlignContent::Center),
+ stylo::AlignFlags::SPACE_BETWEEN => Some(taffy::AlignContent::SpaceBetween),
+ stylo::AlignFlags::SPACE_AROUND => Some(taffy::AlignContent::SpaceAround),
+ stylo::AlignFlags::SPACE_EVENLY => Some(taffy::AlignContent::SpaceEvenly),
+ // Should never be hit. But no real reason to panic here.
+ _ => None,
+ }
+}
+
+#[inline]
+pub fn item_alignment(input: stylo::AlignFlags) -> Option<taffy::AlignItems> {
+ match input.value() {
+ stylo::AlignFlags::NORMAL => None,
+ stylo::AlignFlags::AUTO => None,
+ stylo::AlignFlags::STRETCH => Some(taffy::AlignItems::Stretch),
+ stylo::AlignFlags::FLEX_START => Some(taffy::AlignItems::FlexStart),
+ stylo::AlignFlags::FLEX_END => Some(taffy::AlignItems::FlexEnd),
+ stylo::AlignFlags::START => Some(taffy::AlignItems::Start),
+ stylo::AlignFlags::END => Some(taffy::AlignItems::End),
+ stylo::AlignFlags::CENTER => Some(taffy::AlignItems::Center),
+ stylo::AlignFlags::BASELINE => Some(taffy::AlignItems::Baseline),
+ // Should never be hit. But no real reason to panic here.
+ _ => None,
+ }
+}
+
+#[inline]
+pub fn gap(input: &stylo::Gap) -> taffy::LengthPercentage {
+ match input {
+ // For Flexbox and CSS Grid the "normal" value is 0px. This may need to be updated
+ // if we ever implement multi-column layout.
+ stylo::Gap::Normal => taffy::LengthPercentage::Length(0.0),
+ stylo::Gap::LengthPercentage(val) => length_percentage(&val.0),
+ }
+}
+
+// CSS Grid styles
+// ===============
+
+#[inline]
+pub fn grid_auto_flow(input: stylo::GridAutoFlow) -> taffy::GridAutoFlow {
+ let is_row = input.contains(stylo::GridAutoFlow::ROW);
+ let is_dense = input.contains(stylo::GridAutoFlow::DENSE);
+
+ match (is_row, is_dense) {
+ (true, false) => taffy::GridAutoFlow::Row,
+ (true, true) => taffy::GridAutoFlow::RowDense,
+ (false, false) => taffy::GridAutoFlow::Column,
+ (false, true) => taffy::GridAutoFlow::ColumnDense,
+ }
+}
+
+#[inline]
+pub fn grid_line(input: &stylo::GridLine) -> taffy::GridPlacement {
+ if input.is_auto() {
+ taffy::GridPlacement::Auto
+ } else if input.is_span {
+ taffy::style_helpers::span(input.line_num.try_into().unwrap())
+ } else if input.line_num == 0 {
+ taffy::GridPlacement::Auto
+ } else {
+ taffy::style_helpers::line(input.line_num.try_into().unwrap())
+ }
+}
+
+#[inline]
+pub fn grid_template_tracks(
+ input: &stylo::GridTemplateComponent,
+) -> Vec<taffy::TrackSizingFunction> {
+ match input {
+ stylo::GenericGridTemplateComponent::None => Vec::new(),
+ stylo::GenericGridTemplateComponent::TrackList(list) => list
+ .values
+ .iter()
+ .map(|track| match track {
+ stylo::TrackListValue::TrackSize(size) => {
+ taffy::TrackSizingFunction::Single(track_size(size))
+ },
+ stylo::TrackListValue::TrackRepeat(repeat) => taffy::TrackSizingFunction::Repeat(
+ track_repeat(repeat.count),
+ repeat.track_sizes.iter().map(track_size).collect(),
+ ),
+ })
+ .collect(),
+
+ // TODO: Implement subgrid and masonry
+ stylo::GenericGridTemplateComponent::Subgrid(_) => Vec::new(),
+ stylo::GenericGridTemplateComponent::Masonry => Vec::new(),
+ }
+}
+
+#[inline]
+pub fn grid_auto_tracks(
+ input: &stylo::ImplicitGridTracks,
+) -> Vec<taffy::NonRepeatedTrackSizingFunction> {
+ input.0.iter().map(track_size).collect()
+}
+
+#[inline]
+pub fn track_repeat(input: stylo::RepeatCount<i32>) -> taffy::GridTrackRepetition {
+ match input {
+ stylo::RepeatCount::Number(val) => {
+ taffy::GridTrackRepetition::Count(val.try_into().unwrap())
+ },
+ stylo::RepeatCount::AutoFill => taffy::GridTrackRepetition::AutoFill,
+ stylo::RepeatCount::AutoFit => taffy::GridTrackRepetition::AutoFit,
+ }
+}
+
+#[inline]
+pub fn track_size(
+ input: &stylo::TrackSize<stylo::LengthPercentage>,
+) -> taffy::NonRepeatedTrackSizingFunction {
+ match input {
+ stylo::TrackSize::Breadth(breadth) => taffy::MinMax {
+ min: min_track(breadth),
+ max: max_track(breadth),
+ },
+ stylo::TrackSize::Minmax(min, max) => taffy::MinMax {
+ min: min_track(min),
+ max: max_track(max),
+ },
+ stylo::TrackSize::FitContent(limit) => taffy::MinMax {
+ min: taffy::MinTrackSizingFunction::Auto,
+ max: taffy::MaxTrackSizingFunction::FitContent(match limit {
+ stylo::TrackBreadth::Breadth(lp) => length_percentage(lp),
+
+ // Are these valid? Taffy doesn't support this in any case
+ stylo::TrackBreadth::Fr(_) => unreachable!(),
+ stylo::TrackBreadth::Auto => unreachable!(),
+ stylo::TrackBreadth::MinContent => unreachable!(),
+ stylo::TrackBreadth::MaxContent => unreachable!(),
+ }),
+ },
+ }
+}
+
+#[inline]
+pub fn min_track(
+ input: &stylo::TrackBreadth<stylo::LengthPercentage>,
+) -> taffy::MinTrackSizingFunction {
+ match input {
+ stylo::TrackBreadth::Breadth(lp) => {
+ taffy::MinTrackSizingFunction::Fixed(length_percentage(lp))
+ },
+ stylo::TrackBreadth::Fr(_) => taffy::MinTrackSizingFunction::Auto,
+ stylo::TrackBreadth::Auto => taffy::MinTrackSizingFunction::Auto,
+ stylo::TrackBreadth::MinContent => taffy::MinTrackSizingFunction::MinContent,
+ stylo::TrackBreadth::MaxContent => taffy::MinTrackSizingFunction::MaxContent,
+ }
+}
+
+#[inline]
+pub fn max_track(
+ input: &stylo::TrackBreadth<stylo::LengthPercentage>,
+) -> taffy::MaxTrackSizingFunction {
+ match input {
+ stylo::TrackBreadth::Breadth(lp) => {
+ taffy::MaxTrackSizingFunction::Fixed(length_percentage(lp))
+ },
+ stylo::TrackBreadth::Fr(val) => taffy::MaxTrackSizingFunction::Fraction(*val),
+ stylo::TrackBreadth::Auto => taffy::MaxTrackSizingFunction::Auto,
+ stylo::TrackBreadth::MinContent => taffy::MaxTrackSizingFunction::MinContent,
+ stylo::TrackBreadth::MaxContent => taffy::MaxTrackSizingFunction::MaxContent,
+ }
+}
diff --git a/components/layout_2020/taffy/stylo_taffy/mod.rs b/components/layout_2020/taffy/stylo_taffy/mod.rs
new file mode 100644
index 00000000000..05eece6ace7
--- /dev/null
+++ b/components/layout_2020/taffy/stylo_taffy/mod.rs
@@ -0,0 +1,9 @@
+/* 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/. */
+
+//! Conversion functions from Stylo types to Taffy types
+
+mod convert;
+mod wrapper;
+pub use wrapper::TaffyStyloStyle;
diff --git a/components/layout_2020/taffy/stylo_taffy/wrapper.rs b/components/layout_2020/taffy/stylo_taffy/wrapper.rs
new file mode 100644
index 00000000000..d7f6c44526c
--- /dev/null
+++ b/components/layout_2020/taffy/stylo_taffy/wrapper.rs
@@ -0,0 +1,226 @@
+/* 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 std::ops::Deref;
+
+use style::properties::ComputedValues;
+
+use super::convert;
+
+/// A wrapper struct for anything that Deref's to a [`stylo::ComputedValues`], which
+/// implements Taffy's layout traits and can used with Taffy's layout algorithms.
+pub struct TaffyStyloStyle<T: Deref<Target = ComputedValues>>(pub T);
+
+impl<T: Deref<Target = ComputedValues>> From<T> for TaffyStyloStyle<T> {
+ fn from(value: T) -> Self {
+ Self(value)
+ }
+}
+
+impl<T: Deref<Target = ComputedValues>> taffy::CoreStyle for TaffyStyloStyle<T> {
+ #[inline]
+ fn box_generation_mode(&self) -> taffy::BoxGenerationMode {
+ convert::box_generation_mode(self.0.get_box().display)
+ }
+
+ #[inline]
+ fn is_block(&self) -> bool {
+ convert::is_block(self.0.get_box().display)
+ }
+
+ #[inline]
+ fn box_sizing(&self) -> taffy::BoxSizing {
+ convert::box_sizing(self.0.get_position().box_sizing)
+ }
+
+ #[inline]
+ fn overflow(&self) -> taffy::Point<taffy::Overflow> {
+ let box_styles = self.0.get_box();
+ taffy::Point {
+ x: convert::overflow(box_styles.overflow_x),
+ y: convert::overflow(box_styles.overflow_y),
+ }
+ }
+
+ #[inline]
+ fn scrollbar_width(&self) -> f32 {
+ 0.0
+ }
+
+ #[inline]
+ fn position(&self) -> taffy::Position {
+ convert::position(self.0.get_box().position)
+ }
+
+ #[inline]
+ fn inset(&self) -> taffy::Rect<taffy::LengthPercentageAuto> {
+ let position_styles = self.0.get_position();
+ taffy::Rect {
+ left: convert::inset(&position_styles.left),
+ right: convert::inset(&position_styles.right),
+ top: convert::inset(&position_styles.top),
+ bottom: convert::inset(&position_styles.bottom),
+ }
+ }
+
+ #[inline]
+ fn size(&self) -> taffy::Size<taffy::Dimension> {
+ let position_styles = self.0.get_position();
+ taffy::Size {
+ width: convert::dimension(&position_styles.width),
+ height: convert::dimension(&position_styles.height),
+ }
+ }
+
+ #[inline]
+ fn min_size(&self) -> taffy::Size<taffy::Dimension> {
+ let position_styles = self.0.get_position();
+ taffy::Size {
+ width: convert::dimension(&position_styles.min_width),
+ height: convert::dimension(&position_styles.min_height),
+ }
+ }
+
+ #[inline]
+ fn max_size(&self) -> taffy::Size<taffy::Dimension> {
+ let position_styles = self.0.get_position();
+ taffy::Size {
+ width: convert::max_size_dimension(&position_styles.max_width),
+ height: convert::max_size_dimension(&position_styles.max_height),
+ }
+ }
+
+ #[inline]
+ fn aspect_ratio(&self) -> Option<f32> {
+ convert::aspect_ratio(self.0.get_position().aspect_ratio)
+ }
+
+ #[inline]
+ fn margin(&self) -> taffy::Rect<taffy::LengthPercentageAuto> {
+ let margin_styles = self.0.get_margin();
+ taffy::Rect {
+ left: convert::margin(&margin_styles.margin_left),
+ right: convert::margin(&margin_styles.margin_right),
+ top: convert::margin(&margin_styles.margin_top),
+ bottom: convert::margin(&margin_styles.margin_bottom),
+ }
+ }
+
+ #[inline]
+ fn padding(&self) -> taffy::Rect<taffy::LengthPercentage> {
+ let padding_styles = self.0.get_padding();
+ taffy::Rect {
+ left: convert::length_percentage(&padding_styles.padding_left.0),
+ right: convert::length_percentage(&padding_styles.padding_right.0),
+ top: convert::length_percentage(&padding_styles.padding_top.0),
+ bottom: convert::length_percentage(&padding_styles.padding_bottom.0),
+ }
+ }
+
+ #[inline]
+ fn border(&self) -> taffy::Rect<taffy::LengthPercentage> {
+ let border_styles = self.0.get_border();
+ taffy::Rect {
+ left: taffy::LengthPercentage::Length(border_styles.border_left_width.to_f32_px()),
+ right: taffy::LengthPercentage::Length(border_styles.border_right_width.to_f32_px()),
+ top: taffy::LengthPercentage::Length(border_styles.border_top_width.to_f32_px()),
+ bottom: taffy::LengthPercentage::Length(border_styles.border_bottom_width.to_f32_px()),
+ }
+ }
+}
+
+impl<T: Deref<Target = ComputedValues>> taffy::GridContainerStyle for TaffyStyloStyle<T> {
+ type TemplateTrackList<'a>
+ = Vec<taffy::TrackSizingFunction>
+ where
+ Self: 'a;
+ type AutoTrackList<'a>
+ = Vec<taffy::NonRepeatedTrackSizingFunction>
+ where
+ Self: 'a;
+
+ #[inline]
+ fn grid_template_rows(&self) -> Self::TemplateTrackList<'_> {
+ convert::grid_template_tracks(&self.0.get_position().grid_template_rows)
+ }
+
+ #[inline]
+ fn grid_template_columns(&self) -> Self::TemplateTrackList<'_> {
+ convert::grid_template_tracks(&self.0.get_position().grid_template_columns)
+ }
+
+ #[inline]
+ fn grid_auto_rows(&self) -> Self::AutoTrackList<'_> {
+ convert::grid_auto_tracks(&self.0.get_position().grid_auto_rows)
+ }
+
+ #[inline]
+ fn grid_auto_columns(&self) -> Self::AutoTrackList<'_> {
+ convert::grid_auto_tracks(&self.0.get_position().grid_auto_columns)
+ }
+
+ #[inline]
+ fn grid_auto_flow(&self) -> taffy::GridAutoFlow {
+ convert::grid_auto_flow(self.0.get_position().grid_auto_flow)
+ }
+
+ #[inline]
+ fn gap(&self) -> taffy::Size<taffy::LengthPercentage> {
+ let position_styles = self.0.get_position();
+ taffy::Size {
+ width: convert::gap(&position_styles.column_gap),
+ height: convert::gap(&position_styles.row_gap),
+ }
+ }
+
+ #[inline]
+ fn align_content(&self) -> Option<taffy::AlignContent> {
+ convert::content_alignment(self.0.get_position().align_content.0)
+ }
+
+ #[inline]
+ fn justify_content(&self) -> Option<taffy::JustifyContent> {
+ convert::content_alignment(self.0.get_position().justify_content.0)
+ }
+
+ #[inline]
+ fn align_items(&self) -> Option<taffy::AlignItems> {
+ convert::item_alignment(self.0.get_position().align_items.0)
+ }
+
+ #[inline]
+ fn justify_items(&self) -> Option<taffy::AlignItems> {
+ convert::item_alignment(self.0.get_position().justify_items.computed.0)
+ }
+}
+
+impl<T: Deref<Target = ComputedValues>> taffy::GridItemStyle for TaffyStyloStyle<T> {
+ #[inline]
+ fn grid_row(&self) -> taffy::Line<taffy::GridPlacement> {
+ let position_styles = self.0.get_position();
+ taffy::Line {
+ start: convert::grid_line(&position_styles.grid_row_start),
+ end: convert::grid_line(&position_styles.grid_row_end),
+ }
+ }
+
+ #[inline]
+ fn grid_column(&self) -> taffy::Line<taffy::GridPlacement> {
+ let position_styles = self.0.get_position();
+ taffy::Line {
+ start: convert::grid_line(&position_styles.grid_column_start),
+ end: convert::grid_line(&position_styles.grid_column_end),
+ }
+ }
+
+ #[inline]
+ fn align_self(&self) -> Option<taffy::AlignSelf> {
+ convert::item_alignment(self.0.get_position().align_self.0 .0)
+ }
+
+ #[inline]
+ fn justify_self(&self) -> Option<taffy::AlignSelf> {
+ convert::item_alignment(self.0.get_position().justify_self.0 .0)
+ }
+}