mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
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:
parent
cb3ecd4417
commit
8c46749740
33 changed files with 261 additions and 200 deletions
|
@ -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 element’s 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,
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue