Add support for overflow:scroll and overflow:hidden to layout_2020

This adds clipping and interactive scrolling support, but scrolling from
script is still not functional.
This commit is contained in:
Martin Robinson 2020-01-23 15:30:11 +01:00
parent 8e0d037ee8
commit 7a5a320d74
19 changed files with 129 additions and 63 deletions

View file

@ -9,9 +9,11 @@ use crate::replaced::IntrinsicSizes;
use embedder_traits::Cursor;
use euclid::{Point2D, SideOffsets2D, Size2D};
use gfx::text::glyph::GlyphStore;
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
use mitochondria::OnceCell;
use net_traits::image_cache::UsePlaceholder;
use std::sync::Arc;
use style::computed_values::overflow_x::T as ComputedOverflow;
use style::dom::OpaqueNode;
use style::properties::ComputedValues;
use style::values::computed::{BorderStyle, Length, LengthPercentage};
@ -63,8 +65,6 @@ impl<'a> DisplayListBuilder<'a> {
wr::CommonItemProperties::new(clip_rect, self.current_space_and_clip)
}
// FIXME: use this for the `overflow` property or anything else that clips an entire subtree.
#[allow(unused)]
fn clipping_and_scrolling_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
let previous = self.current_space_and_clip;
let result = f(self);
@ -256,14 +256,49 @@ impl<'a> BuilderForBoxFragment<'a> {
self.build_background(builder);
self.build_border(builder);
let content_rect = self
.fragment
.content_rect
.to_physical(self.fragment.style.writing_mode, self.containing_block)
.translate(self.containing_block.origin.to_vector());
for child in &self.fragment.children {
child.build_display_list(builder, &content_rect)
}
builder.clipping_and_scrolling_scope(|builder| {
let overflow_x = self.fragment.style.get_box().overflow_x;
let overflow_y = self.fragment.style.get_box().overflow_y;
let original_scroll_and_clip_info = builder.current_space_and_clip;
if overflow_x != ComputedOverflow::Visible || overflow_y != ComputedOverflow::Visible {
// TODO(mrobinson): We should use the correct fragment type, once we generate
// fragments from ::before and ::after generated content selectors.
let id = combine_id_with_fragment_type(
self.fragment.tag.id() as usize,
FragmentType::FragmentBody,
) as u64;
let external_id = wr::ExternalScrollId(id, builder.wr.pipeline_id);
let sensitivity = if ComputedOverflow::Hidden == overflow_x &&
ComputedOverflow::Hidden == overflow_y
{
wr::ScrollSensitivity::Script
} else {
wr::ScrollSensitivity::ScriptAndInputEvents
};
builder.current_space_and_clip = builder.wr.define_scroll_frame(
&original_scroll_and_clip_info,
Some(external_id),
self.fragment.scrollable_overflow().to_webrender(),
*self.padding_rect(),
vec![], // complex_clips
None, // image_mask
sensitivity,
wr::units::LayoutVector2D::zero(),
);
}
let content_rect = self
.fragment
.content_rect
.to_physical(self.fragment.style.writing_mode, self.containing_block)
.translate(self.containing_block.origin.to_vector());
for child in &self.fragment.children {
child.build_display_list(builder, &content_rect)
}
});
}
fn build_background(&mut self, builder: &mut DisplayListBuilder) {

View file

@ -3,11 +3,12 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::geom::PhysicalRect;
use crate::geom::{PhysicalPoint, PhysicalRect};
use gfx::text::glyph::GlyphStore;
use gfx_traits::print_tree::PrintTree;
use servo_arc::Arc as ServoArc;
use std::sync::Arc;
use style::computed_values::overflow_x::T as ComputedOverflow;
use style::dom::OpaqueNode;
use style::logical_geometry::WritingMode;
use style::properties::ComputedValues;
@ -39,7 +40,7 @@ pub(crate) struct BoxFragment {
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
/// The scrollable overflow of this box fragment.
pub scrollable_overflow: PhysicalRect<Length>,
pub scrollable_overflow_from_children: PhysicalRect<Length>,
}
pub(crate) struct CollapsedBlockMargins {
@ -101,15 +102,16 @@ impl Fragment {
pub fn scrollable_overflow(&self) -> PhysicalRect<Length> {
// FIXME(mrobinson, bug 25564): We should be using the containing block
// here to properly convert scrollable overflow to physical geometry.
let containing_block = PhysicalRect::zero();
match self {
Fragment::Box(fragment) => fragment.scrollable_overflow.clone(),
Fragment::Box(fragment) => fragment.scrollable_overflow_for_parent(&containing_block),
Fragment::Anonymous(fragment) => fragment.scrollable_overflow.clone(),
Fragment::Text(fragment) => fragment
.rect
.to_physical(fragment.parent_style.writing_mode, &PhysicalRect::zero()),
.to_physical(fragment.parent_style.writing_mode, &containing_block),
Fragment::Image(fragment) => fragment
.rect
.to_physical(fragment.style.writing_mode, &PhysicalRect::zero()),
.to_physical(fragment.style.writing_mode, &containing_block),
}
}
}
@ -125,10 +127,13 @@ impl AnonymousFragment {
}
pub fn new(rect: Rect<Length>, children: Vec<Fragment>, mode: WritingMode) -> Self {
// FIXME(mrobinson, bug 25564): We should be using the containing block
// here to properly convert scrollable overflow to physical geometry.
let content_origin = rect.start_corner.to_physical(mode);
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
acc.union(&child.scrollable_overflow())
acc.union(
&child
.scrollable_overflow()
.translate(content_origin.to_vector()),
)
});
AnonymousFragment {
rect,
@ -141,8 +146,9 @@ impl AnonymousFragment {
pub fn print(&self, tree: &mut PrintTree) {
tree.new_level(format!(
"Anonymous\
\nrect={:?}",
self.rect
\nrect={:?}\
\nscrollable_overflow={:?}",
self.rect, self.scrollable_overflow
));
for child in &self.children {
@ -163,14 +169,10 @@ impl BoxFragment {
margin: Sides<Length>,
block_margins_collapsed_with_children: CollapsedBlockMargins,
) -> BoxFragment {
// FIXME(mrobinson, bug 25564): We should be using the containing block
// here to properly convert scrollable overflow to physical geometry.
let scrollable_overflow = children.iter().fold(
content_rect
.inflate(&border)
.to_physical(style.writing_mode, &PhysicalRect::zero()),
|acc, child| acc.union(&child.scrollable_overflow()),
);
let scrollable_overflow_from_children =
children.iter().fold(PhysicalRect::zero(), |acc, child| {
acc.union(&child.scrollable_overflow())
});
BoxFragment {
tag,
style,
@ -180,10 +182,28 @@ impl BoxFragment {
border,
margin,
block_margins_collapsed_with_children,
scrollable_overflow,
scrollable_overflow_from_children,
}
}
pub fn scrollable_overflow(&self) -> PhysicalRect<Length> {
// FIXME(mrobinson, bug 25564): We should be using the containing block
// here to properly convert scrollable overflow to physical geometry.
let physical_padding_rect = self
.padding_rect()
.to_physical(self.style.writing_mode, &PhysicalRect::zero());
let content_origin = self
.content_rect
.start_corner
.to_physical(self.style.writing_mode);
physical_padding_rect.union(
&self
.scrollable_overflow_from_children
.translate(content_origin.to_vector()),
)
}
pub fn padding_rect(&self) -> Rect<Length> {
self.content_rect.inflate(&self.padding)
}
@ -197,10 +217,17 @@ impl BoxFragment {
"Box\
\ncontent={:?}\
\npadding rect={:?}\
\nborder rect={:?}",
\nborder rect={:?}\
\nscrollable_overflow={:?}\
\noverflow={:?} / {:?}\
\nstyle={:p}",
self.content_rect,
self.padding_rect(),
self.border_rect()
self.border_rect(),
self.scrollable_overflow(),
self.style.get_box().overflow_x,
self.style.get_box().overflow_y,
self.style,
));
for child in &self.children {
@ -208,6 +235,41 @@ impl BoxFragment {
}
tree.end_level();
}
pub fn scrollable_overflow_for_parent(
&self,
containing_block: &PhysicalRect<Length>,
) -> PhysicalRect<Length> {
let mut overflow = self
.border_rect()
.to_physical(self.style.writing_mode, containing_block);
if self.style.get_box().overflow_y != ComputedOverflow::Visible &&
self.style.get_box().overflow_x != ComputedOverflow::Visible
{
return overflow;
}
// 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()),
);
if self.style.get_box().overflow_y == ComputedOverflow::Visible {
overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
overflow.size.height = bottom_right.y - overflow.origin.y;
}
if self.style.get_box().overflow_x == ComputedOverflow::Visible {
overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
overflow.size.width = bottom_right.x - overflow.origin.x;
}
overflow
}
}
impl TextFragment {

View file

@ -131,7 +131,6 @@ ${helpers.single_keyword(
"Overflow",
"computed::Overflow::Visible",
engines="gecko servo-2013 servo-2020",
servo_2020_pref="layout.2020.unimplemented",
logical_group="overflow",
logical=logical,
animation_value_type="discrete",

View file

@ -9,7 +9,7 @@ ${helpers.two_properties_shorthand(
"overflow-x",
"overflow-y",
"specified::Overflow::parse",
engines="gecko servo-2013",
engines="gecko servo-2013 servo-2020",
flags="SHORTHAND_IN_GETCS",
needs_context=False,
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",

View file

@ -1,2 +0,0 @@
[background-repeat-repeat-x.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[background-repeat-repeat-y.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[background-size-contain.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[background-size-cover.xht]
expected: FAIL

View file

@ -1,2 +0,0 @@
[acid2_ref.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[border_radius_clip_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-sticky-bottom.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-sticky-top.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[overflow_auto.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[overflow_position_abs_simple_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[overflow_scroll.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[overflow_simple_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[overflow_xy_a.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text_overflow_ellipsis.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[text_overflow_string.html]
expected: FAIL