mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
layout: Unify scrollable overflow calculation and include position: absolute
(#37475)
Previously, layout was handling scrollable overflow and srolling area calculation separately, only excluding the "unreachable scrollable overflow region" at the last step. In addition, `position: absolute` was not included in scrollable overflow calculation. This change combines the two concepts into a single scrollable overflow calculation and starts taking into account `position: absolute`. Finally, `BoxFragment::scrollable_overflow_for_parent` is converted to use early returns which reduces the amount of indentation. Fixes #35928. Fixes #37204. Testing: This causes some WPT test to pass, but also two to start failing: - `/css/css-masking/clip-path/clip-path-fixed-scroll.html`: This seems to fail because script is scrolling past the boundaries of the document. This is a failure that was uncovered by the fixed element now being added to the page's scroll area. - `/css/css-overflow/overflow-outside-padding.html`: One test has started to fail here because now the absolutely positioned element is included in the scroll area, and I think there is an issue with how we are placing RTL items with negative margins. Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
29e618dcf7
commit
0f61361e27
14 changed files with 123 additions and 142 deletions
|
@ -102,29 +102,41 @@ impl FragmentTree {
|
|||
.expect("Should only call `scrollable_overflow()` after calculating overflow")
|
||||
}
|
||||
|
||||
/// Calculate the scrollable overflow / scrolling area for this [`FragmentTree`] according
|
||||
/// to <https://drafts.csswg.org/cssom-view/#scrolling-area>.
|
||||
pub(crate) fn calculate_scrollable_overflow(&self) {
|
||||
self.scrollable_overflow
|
||||
.set(Some(self.root_fragments.iter().fold(
|
||||
PhysicalRect::zero(),
|
||||
|acc, child| {
|
||||
let child_overflow = child.calculate_scrollable_overflow_for_parent();
|
||||
let scrollable_overflow = || {
|
||||
let Some(first_root_fragment) = self.root_fragments.first() else {
|
||||
return self.initial_containing_block;
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/css-overflow/#scrolling-direction
|
||||
// We want to clip scrollable overflow on box-start and inline-start
|
||||
// sides of the scroll container.
|
||||
//
|
||||
// FIXME(mrobinson, bug 25564): This should take into account writing
|
||||
// mode.
|
||||
let child_overflow = PhysicalRect::new(
|
||||
euclid::Point2D::zero(),
|
||||
euclid::Size2D::new(
|
||||
child_overflow.size.width + child_overflow.origin.x,
|
||||
child_overflow.size.height + child_overflow.origin.y,
|
||||
),
|
||||
);
|
||||
acc.union(&child_overflow)
|
||||
let scrollable_overflow = self.root_fragments.iter().fold(
|
||||
self.initial_containing_block,
|
||||
|overflow, fragment| {
|
||||
fragment
|
||||
.calculate_scrollable_overflow_for_parent()
|
||||
.union(&overflow)
|
||||
},
|
||||
)));
|
||||
);
|
||||
|
||||
// Assuming that the first fragment is the root element, ensure that
|
||||
// scrollable overflow that is unreachable is not included in the final
|
||||
// rectangle. See
|
||||
// <https://drafts.csswg.org/css-overflow/#scrolling-direction>.
|
||||
let first_root_fragment = match first_root_fragment {
|
||||
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment.borrow(),
|
||||
_ => return scrollable_overflow,
|
||||
};
|
||||
if !first_root_fragment.is_root_element() {
|
||||
return scrollable_overflow;
|
||||
}
|
||||
first_root_fragment.clip_wholly_unreachable_scrollable_overflow(
|
||||
scrollable_overflow,
|
||||
self.initial_containing_block,
|
||||
)
|
||||
};
|
||||
|
||||
self.scrollable_overflow.set(Some(scrollable_overflow()))
|
||||
}
|
||||
|
||||
pub(crate) fn find<T>(
|
||||
|
@ -141,29 +153,6 @@ impl FragmentTree {
|
|||
.find_map(|child| child.find(&info, 0, &mut process_func))
|
||||
}
|
||||
|
||||
/// <https://drafts.csswg.org/cssom-view/#scrolling-area>
|
||||
///
|
||||
/// Scrolling area for a viewport that is clipped according to overflow direction of root element.
|
||||
pub fn get_scrolling_area_for_viewport(&self) -> PhysicalRect<Au> {
|
||||
let mut scroll_area = self.initial_containing_block;
|
||||
if let Some(root_fragment) = self.root_fragments.first() {
|
||||
for fragment in self.root_fragments.iter() {
|
||||
scroll_area = fragment.unclipped_scrolling_area().union(&scroll_area);
|
||||
}
|
||||
match root_fragment {
|
||||
Fragment::Box(fragment) | Fragment::Float(fragment) => fragment
|
||||
.borrow()
|
||||
.clip_unreachable_scrollable_overflow_region(
|
||||
scroll_area,
|
||||
self.initial_containing_block,
|
||||
),
|
||||
_ => scroll_area,
|
||||
}
|
||||
} else {
|
||||
scroll_area
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the `<body>` element's [`Fragment`], if it exists in this [`FragmentTree`].
|
||||
pub(crate) fn body_fragment(&self) -> Option<ArcRefCell<BoxFragment>> {
|
||||
fn find_body(children: &[Fragment]) -> Option<ArcRefCell<BoxFragment>> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue