diff --git a/components/layout/dom.rs b/components/layout/dom.rs index 9c203c052b9..4631ff818a6 100644 --- a/components/layout/dom.rs +++ b/components/layout/dom.rs @@ -27,6 +27,7 @@ use crate::flow::BlockLevelBox; use crate::flow::inline::{InlineItem, SharedInlineStyles}; use crate::fragment_tree::Fragment; use crate::geom::PhysicalSize; +use crate::layout_box_base::LayoutBoxBase; use crate::replaced::CanvasInfo; use crate::table::TableLevelBox; use crate::taffy::TaffyItemBox; @@ -73,7 +74,7 @@ impl InnerDOMLayoutData { self.self_box .borrow() .as_ref() - .map(LayoutBox::fragments) + .map(|layout_box| layout_box.with_base_flat(LayoutBoxBase::fragments)) .unwrap_or_default() } @@ -139,17 +140,38 @@ impl LayoutBox { } } - pub(crate) fn fragments(&self) -> Vec { + pub(crate) fn with_base_flat(&self, callback: impl Fn(&LayoutBoxBase) -> Vec) -> Vec { match self { LayoutBox::DisplayContents(..) => vec![], - LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().fragments(), + LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().with_base(callback), LayoutBox::InlineLevel(inline_items) => inline_items .iter() - .flat_map(|inline_item| inline_item.borrow().fragments()) + .flat_map(|inline_item| inline_item.borrow().with_base(&callback)) .collect(), - LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().fragments(), - LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().fragments(), - LayoutBox::TableLevelBox(table_box) => table_box.fragments(), + LayoutBox::FlexLevel(flex_level_box) => flex_level_box.borrow().with_base(callback), + LayoutBox::TaffyItemBox(taffy_item_box) => taffy_item_box.borrow().with_base(callback), + LayoutBox::TableLevelBox(table_box) => table_box.with_base(callback), + } + } + + pub(crate) fn with_base_mut(&mut self, callback: impl Fn(&mut LayoutBoxBase)) { + match self { + LayoutBox::DisplayContents(..) => {}, + LayoutBox::BlockLevel(block_level_box) => { + block_level_box.borrow_mut().with_base_mut(callback); + }, + LayoutBox::InlineLevel(inline_items) => { + for inline_item in inline_items { + inline_item.borrow_mut().with_base_mut(&callback); + } + }, + LayoutBox::FlexLevel(flex_level_box) => { + flex_level_box.borrow_mut().with_base_mut(callback) + }, + LayoutBox::TableLevelBox(table_level_box) => table_level_box.with_base_mut(callback), + LayoutBox::TaffyItemBox(taffy_item_box) => { + taffy_item_box.borrow_mut().with_base_mut(callback) + }, } } diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs index 4c477937306..f61e22fd639 100644 --- a/components/layout/flexbox/mod.rs +++ b/components/layout/flexbox/mod.rs @@ -22,7 +22,8 @@ use crate::context::LayoutContext; use crate::dom::LayoutBox; use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::{BaseFragmentInfo, Fragment}; +use crate::fragment_tree::BaseFragmentInfo; +use crate::layout_box_base::LayoutBoxBase; use crate::positioned::AbsolutelyPositionedBox; mod geom; @@ -190,14 +191,24 @@ impl FlexLevelBox { } } - pub(crate) fn fragments(&self) -> Vec { + pub(crate) fn with_base(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T { match self { - FlexLevelBox::FlexItem(flex_item_box) => flex_item_box - .independent_formatting_context - .base - .fragments(), + FlexLevelBox::FlexItem(flex_item_box) => { + callback(&flex_item_box.independent_formatting_context.base) + }, FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { - positioned_box.borrow().context.base.fragments() + callback(&positioned_box.borrow().context.base) + }, + } + } + + pub(crate) fn with_base_mut(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T { + match self { + FlexLevelBox::FlexItem(flex_item_box) => { + callback(&mut flex_item_box.independent_formatting_context.base) + }, + FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { + callback(&mut positioned_box.borrow_mut().context.base) }, } } diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index 8cc11dbbcd6..30099ce27a5 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -125,6 +125,7 @@ use crate::fragment_tree::{ PositioningFragment, }; use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; +use crate::layout_box_base::LayoutBoxBase; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; @@ -273,20 +274,38 @@ impl InlineItem { } } - pub(crate) fn fragments(&self) -> Vec { + pub(crate) fn with_base(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T { match self { - InlineItem::StartInlineBox(inline_box) => inline_box.borrow().base.fragments(), + InlineItem::StartInlineBox(inline_box) => callback(&inline_box.borrow().base), InlineItem::EndInlineBox | InlineItem::TextRun(..) => { unreachable!("Should never have these kind of fragments attached to a DOM node") }, InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => { - positioned_box.borrow().context.base.fragments() + callback(&positioned_box.borrow().context.base) + }, + InlineItem::OutOfFlowFloatBox(float_box) => callback(&float_box.borrow().contents.base), + InlineItem::Atomic(independent_formatting_context, ..) => { + callback(&independent_formatting_context.borrow().base) + }, + } + } + + pub(crate) fn with_base_mut(&mut self, callback: impl Fn(&mut LayoutBoxBase)) { + match self { + InlineItem::StartInlineBox(inline_box) => { + callback(&mut inline_box.borrow_mut().base); + }, + InlineItem::EndInlineBox | InlineItem::TextRun(..) => { + unreachable!("Should never have these kind of fragments attached to a DOM node") + }, + InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => { + callback(&mut positioned_box.borrow_mut().context.base) }, InlineItem::OutOfFlowFloatBox(float_box) => { - float_box.borrow().contents.base.fragments() + callback(&mut float_box.borrow_mut().contents.base) }, InlineItem::Atomic(independent_formatting_context, ..) => { - independent_formatting_context.borrow().base.fragments() + callback(&mut independent_formatting_context.borrow_mut().base) }, } } diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs index 74d1eec3da4..e19bace810c 100644 --- a/components/layout/flow/mod.rs +++ b/components/layout/flow/mod.rs @@ -139,10 +139,6 @@ impl BlockLevelBox { self.with_base(|base| base.clear_fragment_layout_cache()); } - pub(crate) fn fragments(&self) -> Vec { - self.with_base(LayoutBoxBase::fragments) - } - pub(crate) fn with_base(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T { match self { BlockLevelBox::Independent(independent_formatting_context) => { diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs index fb357de3c84..508593fb365 100644 --- a/components/layout/flow/root.rs +++ b/components/layout/flow/root.rs @@ -26,7 +26,7 @@ use crate::flow::float::FloatBox; use crate::flow::inline::InlineItem; use crate::flow::{BlockContainer, BlockFormattingContext, BlockLevelBox}; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::FragmentTree; +use crate::fragment_tree::{FragmentFlags, FragmentTree}; use crate::geom::{LogicalVec2, PhysicalSize}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::replaced::ReplacedContents; @@ -48,10 +48,6 @@ impl BoxTree { #[servo_tracing::instrument(name = "Box Tree Construction", skip_all)] pub(crate) fn construct(context: &LayoutContext, root_element: ServoLayoutNode<'_>) -> Self { let root_element = root_element.to_threadsafe(); - let boxes = construct_for_root_element(context, root_element); - - // Zero box for `:root { display: none }`, one for the root element otherwise. - assert!(boxes.len() <= 1); // From https://www.w3.org/TR/css-overflow-3/#overflow-propagation: // > UAs must apply the overflow-* values set on the root element to the viewport when the @@ -65,6 +61,7 @@ impl BoxTree { let mut viewport_overflow_x = root_style.clone_overflow_x(); let mut viewport_overflow_y = root_style.clone_overflow_y(); + let mut element_propagating_overflow = root_element; if viewport_overflow_x == Overflow::Visible && viewport_overflow_y == Overflow::Visible && !root_style.get_box().display.is_none() @@ -81,12 +78,28 @@ impl BoxTree { if !style.get_box().display.is_none() { viewport_overflow_x = style.clone_overflow_x(); viewport_overflow_y = style.clone_overflow_y(); + element_propagating_overflow = child; break; } } } + let boxes = construct_for_root_element(context, root_element); + + // Zero box for `:root { display: none }`, one for the root element otherwise. + assert!(boxes.len() <= 1); + + if let Some(layout_data) = element_propagating_overflow.inner_layout_data() { + if let Some(ref mut layout_box) = *layout_data.self_box.borrow_mut() { + layout_box.with_base_mut(|base| { + base.base_fragment_info + .flags + .insert(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) + }); + } + } + let contents = BlockContainer::BlockLevelBoxes(boxes); let contains_floats = contents.contains_floats(); Self { diff --git a/components/layout/fragment_tree/base_fragment.rs b/components/layout/fragment_tree/base_fragment.rs index 45d6ac59e92..0392647c502 100644 --- a/components/layout/fragment_tree/base_fragment.rs +++ b/components/layout/fragment_tree/base_fragment.rs @@ -176,6 +176,9 @@ bitflags! { const SIZE_DEPENDS_ON_BLOCK_CONSTRAINTS_AND_CAN_BE_CHILD_OF_FLEX_ITEM = 1 << 8; /// Whether or not the node that created this fragment is the root element. const IS_ROOT_ELEMENT = 1 << 9; + /// If element has propagated the overflow value to viewport. + const PROPAGATED_OVERFLOW_TO_VIEWPORT = 1 << 10; + } } diff --git a/components/layout/style_ext.rs b/components/layout/style_ext.rs index ff539716be8..38a0b5424a1 100644 --- a/components/layout/style_ext.rs +++ b/components/layout/style_ext.rs @@ -598,6 +598,15 @@ impl ComputedValuesExt for ComputedValues { let mut overflow_x = style_box.overflow_x; let mut overflow_y = style_box.overflow_y; + // https://www.w3.org/TR/css-overflow-3/#overflow-propagation + // The element from which the value is propagated must then have a used overflow value of visible. + if fragment_flags.contains(FragmentFlags::PROPAGATED_OVERFLOW_TO_VIEWPORT) { + return AxesOverflow { + x: Overflow::Visible, + y: Overflow::Visible, + }; + } + // From : // "On replaced elements, the used values of all computed values other than visible is clip." if fragment_flags.contains(FragmentFlags::IS_REPLACED) { diff --git a/components/layout/table/mod.rs b/components/layout/table/mod.rs index c44f23a31d8..5bb1330d8f8 100644 --- a/components/layout/table/mod.rs +++ b/components/layout/table/mod.rs @@ -88,7 +88,7 @@ use crate::SharedStyle; use crate::cell::ArcRefCell; use crate::flow::BlockContainer; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::{BaseFragmentInfo, Fragment}; +use crate::fragment_tree::BaseFragmentInfo; use crate::geom::PhysicalVec; use crate::layout_box_base::LayoutBoxBase; use crate::style_ext::BorderStyleColor; @@ -412,12 +412,21 @@ impl TableLevelBox { } } - pub(crate) fn fragments(&self) -> Vec { + pub(crate) fn with_base(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T { match self { - TableLevelBox::Caption(caption) => caption.borrow().context.base.fragments(), - TableLevelBox::Cell(cell) => cell.borrow().base.fragments(), - TableLevelBox::TrackGroup(track_group) => track_group.borrow().base.fragments(), - TableLevelBox::Track(track) => track.borrow().base.fragments(), + TableLevelBox::Caption(caption) => callback(&caption.borrow().context.base), + TableLevelBox::Cell(cell) => callback(&cell.borrow().base), + TableLevelBox::TrackGroup(track_group) => callback(&track_group.borrow().base), + TableLevelBox::Track(track) => callback(&track.borrow().base), + } + } + + pub(crate) fn with_base_mut(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T { + match self { + TableLevelBox::Caption(caption) => callback(&mut caption.borrow_mut().context.base), + TableLevelBox::Cell(cell) => callback(&mut cell.borrow_mut().base), + TableLevelBox::TrackGroup(track_group) => callback(&mut track_group.borrow_mut().base), + TableLevelBox::Track(track) => callback(&mut track.borrow_mut().base), } } diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs index 9610b88e6f2..3d3337c6d2d 100644 --- a/components/layout/taffy/mod.rs +++ b/components/layout/taffy/mod.rs @@ -21,6 +21,7 @@ use crate::dom::LayoutBox; use crate::dom_traversal::{NodeAndStyleInfo, NonReplacedContents}; use crate::formatting_contexts::IndependentFormattingContext; use crate::fragment_tree::Fragment; +use crate::layout_box_base::LayoutBoxBase; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; #[derive(Debug, MallocSizeOf)] @@ -146,13 +147,24 @@ impl TaffyItemBox { } } - pub(crate) fn fragments(&self) -> Vec { + pub(crate) fn with_base(&self, callback: impl Fn(&LayoutBoxBase) -> T) -> T { match self.taffy_level_box { TaffyItemBoxInner::InFlowBox(ref independent_formatting_context) => { - independent_formatting_context.base.fragments() + callback(&independent_formatting_context.base) }, TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(ref positioned_box) => { - positioned_box.borrow().context.base.fragments() + callback(&positioned_box.borrow().context.base) + }, + } + } + + pub(crate) fn with_base_mut(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T { + match &mut self.taffy_level_box { + TaffyItemBoxInner::InFlowBox(independent_formatting_context) => { + callback(&mut independent_formatting_context.base) + }, + TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { + callback(&mut positioned_box.borrow_mut().context.base) }, } } diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 706fbc060d1..3807f666032 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -607767,6 +607767,41 @@ {} ] ], + "scrollable-overflow-with-nested-elements-001.html": [ + "9bf0d16841996fbe9643b44868bc81ca190fe30b", + [ + null, + {} + ] + ], + "scrollable-overflow-with-nested-elements-002.html": [ + "10ec866995ebf5cb55425599dbd11083a29f543f", + [ + null, + {} + ] + ], + "scrollable-overflow-with-nested-elements-003.html": [ + "563cadb7faeb66ff0eb8611e61c3681fd576e9b6", + [ + null, + {} + ] + ], + "scrollable-overflow-with-nested-elements-004.html": [ + "07866f341b9b2ae74e3566b8883adc735cefe702", + [ + null, + {} + ] + ], + "scrollable-overflow-with-nested-elements-005.html": [ + "95e3e53e09fa9aaf8b54435dcb3b9a8236031e0d", + [ + null, + {} + ] + ], "scrollable-overflow-zero-one-axis.html": [ "1986a8d48b98fde17fda826c5bbf9d75d870f45e", [ diff --git a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-007.html.ini b/tests/wpt/meta/css/css-overflow/overflow-body-propagation-007.html.ini deleted file mode 100644 index b25f8377ecc..00000000000 --- a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-007.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[overflow-body-propagation-007.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-008.html.ini b/tests/wpt/meta/css/css-overflow/overflow-body-propagation-008.html.ini deleted file mode 100644 index 7f080edecf3..00000000000 --- a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-008.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[overflow-body-propagation-008.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-009.html.ini b/tests/wpt/meta/css/css-overflow/overflow-body-propagation-009.html.ini deleted file mode 100644 index 48ea118a917..00000000000 --- a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-009.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[overflow-body-propagation-009.html] - expected: FAIL diff --git a/tests/wpt/meta/css/css-overflow/overflow-body-propagation-010.html.ini b/tests/wpt/meta/css/css-overflow/overflow-body-propagation-010.html.ini new file mode 100644 index 00000000000..eecc119f489 --- /dev/null +++ b/tests/wpt/meta/css/css-overflow/overflow-body-propagation-010.html.ini @@ -0,0 +1,2 @@ +[overflow-body-propagation-010.html] + expected: FAIL diff --git a/tests/wpt/meta/css/css-overflow/scrollable-overflow-with-nested-elements-005.html.ini b/tests/wpt/meta/css/css-overflow/scrollable-overflow-with-nested-elements-005.html.ini new file mode 100644 index 00000000000..ddf1131a9c2 --- /dev/null +++ b/tests/wpt/meta/css/css-overflow/scrollable-overflow-with-nested-elements-005.html.ini @@ -0,0 +1,3 @@ +[scrollable-overflow-with-nested-elements-005.html] + [html 1] + expected: FAIL diff --git a/tests/wpt/meta/css/css-overflow/scrollbar-gutter-scroll-into-view.html.ini b/tests/wpt/meta/css/css-overflow/scrollbar-gutter-scroll-into-view.html.ini deleted file mode 100644 index 69d00bd9309..00000000000 --- a/tests/wpt/meta/css/css-overflow/scrollbar-gutter-scroll-into-view.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[scrollbar-gutter-scroll-into-view.html] - expected: FAIL diff --git a/tests/wpt/meta/css/cssom-view/scrolling-quirks-vs-nonquirks.html.ini b/tests/wpt/meta/css/cssom-view/scrolling-quirks-vs-nonquirks.html.ini deleted file mode 100644 index 84808d34540..00000000000 --- a/tests/wpt/meta/css/cssom-view/scrolling-quirks-vs-nonquirks.html.ini +++ /dev/null @@ -1,27 +0,0 @@ -[scrolling-quirks-vs-nonquirks.html] - [scrollWidth/scrollHeight on the root element in non-quirks mode] - expected: FAIL - - [scrollWidth/scrollHeight on the root element in quirks mode] - expected: FAIL - - [scrollWidth/scrollHeight on the HTML body element in quirks mode] - expected: FAIL - - [scroll() on the root element in non-quirks mode] - expected: FAIL - - [scrollBy() on the root element in non-quirks mode] - expected: FAIL - - [scrollLeft/scrollTop on the root element in non-quirks mode] - expected: FAIL - - [scroll() on the HTML body element in quirks mode] - expected: FAIL - - [scrollBy() on the HTML body element in quirks mode] - expected: FAIL - - [scrollLeft/scrollTop on the HTML body element in quirks mode] - expected: FAIL diff --git a/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-001.html b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-001.html new file mode 100644 index 00000000000..9bf0d168419 --- /dev/null +++ b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-001.html @@ -0,0 +1,37 @@ + + +CSS Overflow: Scrollable Overflow Transform Rotate Nested Element + + + + + + + + + +
+
+
hello
+
+
+ diff --git a/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-002.html b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-002.html new file mode 100644 index 00000000000..10ec866995e --- /dev/null +++ b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-002.html @@ -0,0 +1,50 @@ + + +CSS Overflow: Scrollable Overflow Transform Rotate Nested Element + + + + + + + + + +
+
+

+

+
+
+
+
+

+

+
+
+ diff --git a/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-003.html b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-003.html new file mode 100644 index 00000000000..563cadb7fae --- /dev/null +++ b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-003.html @@ -0,0 +1,34 @@ + + +CSS Overflow: Scrollable Overflow Transform Rotate Nested Element + + + + + + + + + +
+

+

+
+ diff --git a/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-004.html b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-004.html new file mode 100644 index 00000000000..07866f341b9 --- /dev/null +++ b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-004.html @@ -0,0 +1,42 @@ + + +CSS Overflow: Scrollable Overflow Transform Rotate Nested Element + + + + + + + + + +
+
+

+

+
+
+ diff --git a/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-005.html b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-005.html new file mode 100644 index 00000000000..95e3e53e09f --- /dev/null +++ b/tests/wpt/tests/css/css-overflow/scrollable-overflow-with-nested-elements-005.html @@ -0,0 +1,39 @@ + + + + + + CSS Overflow: Scrollable Overflow Transform Rotate Nested Element + + + + + + + + + + +
+

+

+
+ +