Include the scrollable overflow of a child box if either its parent or child has overflow: visible (#38443)

Include the scrollable overflow of a child box if either its parent or
child has `overflow: visible`

**Issue**: For the blocks having property `overflow:hidden`, their
scroll overflow is not added to parent's scroll overflow.
Causing unable to scroll the parent block aka `Root` block in our Issue
#38248 .

**Testing**: css/cssom-view/scrolling-quirks-vs-nonquirks.html
**Fixes**: #38248

Signed-off-by: Shubham Gupta <shubham13297@gmail.com>
This commit is contained in:
Shubham Gupta 2025-08-06 15:46:54 +08:00 committed by GitHub
parent 5b148cf5de
commit dcb90bb85e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 51 additions and 55 deletions

View file

@ -24,7 +24,7 @@ use crate::formatting_contexts::Baselines;
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, ToLogical,
};
use crate::style_ext::ComputedValuesExt;
use crate::style_ext::{AxesOverflow, ComputedValuesExt};
use crate::table::SpecificTableGridInfo;
use crate::taffy::SpecificTaffyGridInfo;
@ -271,12 +271,13 @@ impl BoxFragment {
// overflow together, but from the specification it seems that if the border
// box of an item is in the "wholly unreachable scrollable overflow region", but
// its scrollable overflow is not, it should also be excluded.
let overflow_style = self.style.effective_overflow(self.base.flags);
let scrollable_overflow = self
.children
.iter()
.fold(physical_padding_rect, |acc, child| {
let scrollable_overflow_from_child = child
.calculate_scrollable_overflow_for_parent()
.calculate_scrollable_overflow_for_parent(Some(overflow_style))
.translate(content_origin);
// Note that this doesn't just exclude scrollable overflow outside the
@ -359,27 +360,33 @@ impl BoxFragment {
tree.end_level();
}
pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
pub(crate) fn scrollable_overflow_for_parent(
&self,
parent_overflow_style: Option<AxesOverflow>,
) -> PhysicalRect<Au> {
let mut overflow = self.border_rect();
if !self.style.establishes_scroll_container(self.base.flags) {
// https://www.w3.org/TR/css-overflow-3/#scrollable
// Only include the scrollable overflow of a child box if it has overflow: visible.
let scrollable_overflow = self.scrollable_overflow();
let bottom_right = PhysicalPoint::new(
overflow.max_x().max(scrollable_overflow.max_x()),
overflow.max_y().max(scrollable_overflow.max_y()),
);
let overflow_style = self.style.effective_overflow(self.base.flags);
let scrollable_overflow = self.scrollable_overflow();
let bottom_right = PhysicalPoint::new(
overflow.max_x().max(scrollable_overflow.max_x()),
overflow.max_y().max(scrollable_overflow.max_y()),
);
let overflow_style = self.style.effective_overflow(self.base.flags);
if overflow_style.y == ComputedOverflow::Visible {
overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
overflow.size.height = bottom_right.y - overflow.origin.y;
}
// https://www.w3.org/TR/css-overflow-3/#scrollable
// For each axis, we only include the scrollable overflow of a child box
// if either its parent or child has overflow: visible.
if parent_overflow_style.is_some_and(|style| style.y == ComputedOverflow::Visible) ||
overflow_style.y == ComputedOverflow::Visible
{
overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
overflow.size.height = bottom_right.y - overflow.origin.y;
}
if overflow_style.x == ComputedOverflow::Visible {
overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
overflow.size.width = bottom_right.x - overflow.origin.x;
}
if parent_overflow_style.is_some_and(|style| style.x == ComputedOverflow::Visible) ||
overflow_style.x == ComputedOverflow::Visible
{
overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
overflow.size.width = bottom_right.x - overflow.origin.x;
}
if !self

View file

@ -23,7 +23,7 @@ use super::{
use crate::cell::ArcRefCell;
use crate::flow::inline::SharedInlineStyles;
use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect};
use crate::style_ext::ComputedValuesExt;
use crate::style_ext::{AxesOverflow, ComputedValuesExt};
#[derive(Clone, MallocSizeOf)]
pub(crate) enum Fragment {
@ -168,14 +168,19 @@ impl Fragment {
let fragment = fragment.borrow();
fragment.offset_by_containing_block(&fragment.scrollable_overflow())
},
_ => self.scrollable_overflow_for_parent(),
_ => self.scrollable_overflow_for_parent(None),
}
}
pub(crate) fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
pub(crate) fn scrollable_overflow_for_parent(
&self,
parent_overflow_style: Option<AxesOverflow>,
) -> PhysicalRect<Au> {
match self {
Fragment::Box(fragment) | Fragment::Float(fragment) => {
return fragment.borrow().scrollable_overflow_for_parent();
return fragment
.borrow()
.scrollable_overflow_for_parent(parent_overflow_style);
},
Fragment::AbsoluteOrFixedPositioned(_) => PhysicalRect::zero(),
Fragment::Positioning(fragment) => fragment.borrow().scrollable_overflow_for_parent(),
@ -185,9 +190,12 @@ impl Fragment {
}
}
pub(crate) fn calculate_scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
pub(crate) fn calculate_scrollable_overflow_for_parent(
&self,
parent_overflow_style: Option<AxesOverflow>,
) -> PhysicalRect<Au> {
self.calculate_scrollable_overflow();
self.scrollable_overflow_for_parent()
self.scrollable_overflow_for_parent(parent_overflow_style)
}
pub(crate) fn calculate_scrollable_overflow(&self) {

View file

@ -15,6 +15,7 @@ use super::{BoxFragment, ContainingBlockManager, Fragment};
use crate::ArcRefCell;
use crate::context::LayoutContext;
use crate::geom::PhysicalRect;
use crate::style_ext::ComputedValuesExt;
#[derive(MallocSizeOf)]
pub struct FragmentTree {
@ -117,8 +118,15 @@ impl FragmentTree {
let scrollable_overflow = self.root_fragments.iter().fold(
self.initial_containing_block,
|overflow, fragment| {
let overflow_style = match fragment {
Fragment::Box(fragment) | Fragment::Float(fragment) => {
let fragment_flags = fragment.borrow().base.flags;
Some(fragment.borrow().style.effective_overflow(fragment_flags))
},
_ => None,
};
fragment
.calculate_scrollable_overflow_for_parent()
.calculate_scrollable_overflow_for_parent(overflow_style)
.union(&overflow)
},
);

View file

@ -79,7 +79,7 @@ impl PositioningFragment {
|acc, child| {
acc.union(
&child
.calculate_scrollable_overflow_for_parent()
.calculate_scrollable_overflow_for_parent(None)
.translate(self.rect.origin.to_vector()),
)
},

View file

@ -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