Implement overflow:clip (#35103)

* Implement overflow clip

Signed-off-by: longvatrong111 <longvatrong111@gmail.com>

* Modify test ini for overflow clip

Signed-off-by: longvatrong111 <longvatrong111@gmail.com>

* Update overflow_clip_rect calculation

Signed-off-by: batu_hoang <longvatrong111@gmail.com>

* Update overflow-clip-margin border-radius according to shadow box

Signed-off-by: longvatrong111 <longvatrong111@gmail.com>

---------

Signed-off-by: longvatrong111 <longvatrong111@gmail.com>
Signed-off-by: batu_hoang <longvatrong111@gmail.com>
Signed-off-by: batu_hoang <55729155+longvatrong111@users.noreply.github.com>
This commit is contained in:
batu_hoang 2025-02-12 15:02:06 +08:00 committed by GitHub
parent cb3ecd4417
commit 8c46749740
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 261 additions and 200 deletions

View file

@ -2,6 +2,7 @@
* 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 core::f32;
use std::cell::RefCell;
use std::mem;
@ -23,8 +24,8 @@ use style::values::generics::box_::Perspective;
use style::values::generics::transform;
use style::values::specified::box_::DisplayOutside;
use style::Zero;
use webrender_api as wr;
use webrender_api::units::{LayoutPoint, LayoutRect, LayoutTransform, LayoutVector2D};
use webrender_api::{self as wr, BorderRadius};
use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId, ScrollableNodeInfo};
use wr::units::{LayoutPixel, LayoutSize};
use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds};
@ -32,12 +33,12 @@ use wr::{ClipChainId, SpatialTreeItemKey, StickyOffsetBounds};
use super::clip_path::build_clip_path_clip_chain_if_necessary;
use super::DisplayList;
use crate::display_list::conversions::{FilterToWebRender, ToWebRender};
use crate::display_list::{BuilderForBoxFragment, DisplayListBuilder};
use crate::display_list::{offset_radii, BuilderForBoxFragment, DisplayListBuilder};
use crate::fragment_tree::{
BoxFragment, ContainingBlockManager, Fragment, FragmentFlags, FragmentTree,
PositioningFragment, SpecificLayoutInfo,
};
use crate::geom::{AuOrAuto, PhysicalRect, PhysicalSides};
use crate::geom::{AuOrAuto, PhysicalRect, PhysicalSides, PhysicalVec};
use crate::style_ext::ComputedValuesExt;
#[derive(Clone)]
@ -181,15 +182,13 @@ impl DisplayList {
self.wr.pop_reference_frame();
}
fn clip_scroll_frame(
fn clip_overflow_frame(
&mut self,
parent_scroll_node_id: &ScrollTreeNodeId,
parent_clip_id: &ClipChainId,
clip_rect: LayoutRect,
fragment_builder: BuilderForBoxFragment,
radii: wr::BorderRadius,
) -> ClipChainId {
let radii = fragment_builder.border_radius;
let new_clip_id = if radii.is_zero() {
self.wr
.define_clip_rect(parent_scroll_node_id.spatial_id, clip_rect)
@ -959,10 +958,14 @@ struct ReferenceFrameData {
}
struct ScrollFrameData {
scroll_tree_node_id: ScrollTreeNodeId,
clip_chain_id: wr::ClipChainId,
scroll_frame_rect: LayoutRect,
}
struct OverflowFrameData {
clip_chain_id: wr::ClipChainId,
scroll_frame_data: Option<ScrollFrameData>,
}
impl BoxFragment {
fn get_stacking_context_type(&self) -> Option<StackingContextType> {
if self.style.establishes_stacking_context(self.base.flags) {
@ -1245,28 +1248,31 @@ impl BoxFragment {
// We want to build the scroll frame after the background and border, because
// they shouldn't scroll with the rest of the box content.
if let Some(scroll_frame_data) = self.build_scroll_frame_if_necessary(
if let Some(overflow_frame_data) = self.build_overflow_frame_if_necessary(
display_list,
&new_scroll_node_id,
&new_clip_chain_id,
&containing_block.rect,
) {
new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
new_clip_chain_id = scroll_frame_data.clip_chain_id;
new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
new_clip_chain_id = overflow_frame_data.clip_chain_id;
if let Some(scroll_frame_data) = overflow_frame_data.scroll_frame_data {
new_scroll_node_id = scroll_frame_data.scroll_tree_node_id;
new_scroll_frame_size = Some(scroll_frame_data.scroll_frame_rect.size());
stacking_context
.contents
.push(StackingContextContent::Fragment {
scroll_node_id: new_scroll_node_id,
reference_frame_scroll_node_id: reference_frame_scroll_node_id_for_fragments,
clip_chain_id: new_clip_chain_id,
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: true,
is_collapsed_table_borders: false,
});
stacking_context
.contents
.push(StackingContextContent::Fragment {
scroll_node_id: new_scroll_node_id,
reference_frame_scroll_node_id:
reference_frame_scroll_node_id_for_fragments,
clip_chain_id: new_clip_chain_id,
section,
containing_block: containing_block.rect,
fragment: fragment.clone(),
is_hit_test_for_scrollable_overflow: true,
is_collapsed_table_borders: false,
});
}
}
let padding_rect = self
@ -1366,17 +1372,65 @@ impl BoxFragment {
Some(display_list.define_clip_chain(*parent_clip_chain_id, [clip_id]))
}
fn build_scroll_frame_if_necessary(
// TODO: merge this function with style.effective_overflow()
fn used_overflow(&self) -> PhysicalVec<ComputedOverflow> {
let mut overflow = self.style.effective_overflow();
let is_replaced_element = self.base.flags.contains(FragmentFlags::IS_REPLACED);
if is_replaced_element {
if overflow.x != ComputedOverflow::Visible {
overflow.x = ComputedOverflow::Clip;
}
if overflow.y != ComputedOverflow::Visible {
overflow.y = ComputedOverflow::Clip;
}
}
overflow
}
fn build_overflow_frame_if_necessary(
&self,
display_list: &mut DisplayList,
parent_scroll_node_id: &ScrollTreeNodeId,
parent_clip_id: &wr::ClipChainId,
parent_clip_chain_id: &wr::ClipChainId,
containing_block_rect: &PhysicalRect<Au>,
) -> Option<ScrollFrameData> {
if !self.style.establishes_scroll_container() {
) -> Option<OverflowFrameData> {
let overflow = self.used_overflow();
if overflow.x == ComputedOverflow::Visible && overflow.y == ComputedOverflow::Visible {
return None;
}
// Non-scrollable overflow path
if overflow.x == ComputedOverflow::Clip || overflow.y == ComputedOverflow::Clip {
let clip_margin = self.style.get_margin().overflow_clip_margin.px();
let overflow_clip_rect =
self.overflow_clip_rect(containing_block_rect, overflow, clip_margin);
let mut radii = BorderRadius::zero();
if overflow.x == ComputedOverflow::Clip && overflow.y == ComputedOverflow::Clip {
radii = offset_radii(
BuilderForBoxFragment::new(self, containing_block_rect, false, false)
.border_radius,
clip_margin,
);
}
let clip_chain_id = display_list.clip_overflow_frame(
parent_scroll_node_id,
parent_clip_chain_id,
overflow_clip_rect,
radii,
);
return Some(OverflowFrameData {
clip_chain_id,
scroll_frame_data: None,
});
}
// scrollable overflow path
// From https://drafts.csswg.org/css-overflow/#propdef-overflow:
// > UAs must apply the overflow-* values set on the root element to the viewport when the
// > root elements display value is not none. However, when the root element is an [HTML]
@ -1395,13 +1449,24 @@ impl BoxFragment {
return None;
}
let scroll_frame_rect = self
.padding_rect()
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
let clip_chain_id = display_list.clip_overflow_frame(
parent_scroll_node_id,
parent_clip_chain_id,
scroll_frame_rect,
BuilderForBoxFragment::new(self, containing_block_rect, false, false).border_radius,
);
let tag = self.base.tag?;
let external_id = wr::ExternalScrollId(
tag.to_display_list_fragment_id(),
display_list.wr.pipeline_id,
);
let overflow = self.style.effective_overflow();
let sensitivity =
if ComputedOverflow::Hidden == overflow.x && ComputedOverflow::Hidden == overflow.y {
ScrollSensitivity::Script
@ -1409,19 +1474,8 @@ impl BoxFragment {
ScrollSensitivity::ScriptAndInputEvents
};
let scroll_frame_rect = self
.padding_rect()
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
let content_rect = self.scrollable_overflow().to_webrender();
let clip_chain_id = display_list.clip_scroll_frame(
parent_scroll_node_id,
parent_clip_id,
scroll_frame_rect,
BuilderForBoxFragment::new(self, containing_block_rect, false, false),
);
let scroll_tree_node_id = display_list.define_scroll_frame(
parent_scroll_node_id,
external_id,
@ -1430,13 +1484,43 @@ impl BoxFragment {
sensitivity,
);
Some(ScrollFrameData {
scroll_tree_node_id,
Some(OverflowFrameData {
clip_chain_id,
scroll_frame_rect,
scroll_frame_data: Some(ScrollFrameData {
scroll_tree_node_id,
scroll_frame_rect,
}),
})
}
fn overflow_clip_rect(
&self,
containing_block_rect: &PhysicalRect<Au>,
overflow: PhysicalVec<ComputedOverflow>,
clip_margin: f32,
) -> LayoutRect {
// TODO: update this to the proper box after the parser is ready
let mut clip_rect = self
.padding_rect()
.translate(containing_block_rect.origin.to_vector())
.to_webrender();
// Adjust by the overflow clip margin.
// https://drafts.csswg.org/css-overflow/#overflow-clip-margin
clip_rect = clip_rect.inflate(clip_margin, clip_margin);
if overflow.x != ComputedOverflow::Clip {
clip_rect.min.x = f32::MIN;
clip_rect.max.x = f32::MAX;
}
if overflow.y != ComputedOverflow::Clip {
clip_rect.min.y = f32::MIN;
clip_rect.max.y = f32::MAX;
}
clip_rect
}
fn build_sticky_frame_if_necessary(
&self,
display_list: &mut DisplayList,

View file

@ -147,6 +147,7 @@ pub fn overflow(input: stylo::Overflow) -> taffy::Overflow {
stylo::Overflow::Visible => taffy::Overflow::Visible,
stylo::Overflow::Hidden => taffy::Overflow::Hidden,
stylo::Overflow::Scroll => taffy::Overflow::Scroll,
stylo::Overflow::Clip => taffy::Overflow::Clip,
// TODO: Support Overflow::Auto in Taffy
stylo::Overflow::Auto => taffy::Overflow::Scroll,
}