mirror of
https://github.com/servo/servo.git
synced 2025-07-30 02:30:21 +01:00
Auto merge of #25645 - mrobinson:scrollable-overflow-v2, r=SimonSapin
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. <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #___ (GitHub issue number if applicable) <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
c4785e3256
19 changed files with 129 additions and 63 deletions
|
@ -9,9 +9,11 @@ use crate::replaced::IntrinsicSizes;
|
||||||
use embedder_traits::Cursor;
|
use embedder_traits::Cursor;
|
||||||
use euclid::{Point2D, SideOffsets2D, Size2D};
|
use euclid::{Point2D, SideOffsets2D, Size2D};
|
||||||
use gfx::text::glyph::GlyphStore;
|
use gfx::text::glyph::GlyphStore;
|
||||||
|
use gfx_traits::{combine_id_with_fragment_type, FragmentType};
|
||||||
use mitochondria::OnceCell;
|
use mitochondria::OnceCell;
|
||||||
use net_traits::image_cache::UsePlaceholder;
|
use net_traits::image_cache::UsePlaceholder;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use style::computed_values::overflow_x::T as ComputedOverflow;
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
use style::values::computed::{BorderStyle, Length, LengthPercentage};
|
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)
|
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 {
|
fn clipping_and_scrolling_scope<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> R {
|
||||||
let previous = self.current_space_and_clip;
|
let previous = self.current_space_and_clip;
|
||||||
let result = f(self);
|
let result = f(self);
|
||||||
|
@ -256,14 +256,49 @@ impl<'a> BuilderForBoxFragment<'a> {
|
||||||
|
|
||||||
self.build_background(builder);
|
self.build_background(builder);
|
||||||
self.build_border(builder);
|
self.build_border(builder);
|
||||||
let content_rect = self
|
|
||||||
.fragment
|
builder.clipping_and_scrolling_scope(|builder| {
|
||||||
.content_rect
|
let overflow_x = self.fragment.style.get_box().overflow_x;
|
||||||
.to_physical(self.fragment.style.writing_mode, self.containing_block)
|
let overflow_y = self.fragment.style.get_box().overflow_y;
|
||||||
.translate(self.containing_block.origin.to_vector());
|
let original_scroll_and_clip_info = builder.current_space_and_clip;
|
||||||
for child in &self.fragment.children {
|
if overflow_x != ComputedOverflow::Visible || overflow_y != ComputedOverflow::Visible {
|
||||||
child.build_display_list(builder, &content_rect)
|
// 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) {
|
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
|
||||||
|
|
|
@ -3,11 +3,12 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
||||||
use crate::geom::PhysicalRect;
|
use crate::geom::{PhysicalPoint, PhysicalRect};
|
||||||
use gfx::text::glyph::GlyphStore;
|
use gfx::text::glyph::GlyphStore;
|
||||||
use gfx_traits::print_tree::PrintTree;
|
use gfx_traits::print_tree::PrintTree;
|
||||||
use servo_arc::Arc as ServoArc;
|
use servo_arc::Arc as ServoArc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use style::computed_values::overflow_x::T as ComputedOverflow;
|
||||||
use style::dom::OpaqueNode;
|
use style::dom::OpaqueNode;
|
||||||
use style::logical_geometry::WritingMode;
|
use style::logical_geometry::WritingMode;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
|
@ -39,7 +40,7 @@ pub(crate) struct BoxFragment {
|
||||||
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
|
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
|
|
||||||
/// The scrollable overflow of this box fragment.
|
/// The scrollable overflow of this box fragment.
|
||||||
pub scrollable_overflow: PhysicalRect<Length>,
|
pub scrollable_overflow_from_children: PhysicalRect<Length>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct CollapsedBlockMargins {
|
pub(crate) struct CollapsedBlockMargins {
|
||||||
|
@ -101,15 +102,16 @@ impl Fragment {
|
||||||
pub fn scrollable_overflow(&self) -> PhysicalRect<Length> {
|
pub fn scrollable_overflow(&self) -> PhysicalRect<Length> {
|
||||||
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
||||||
// here to properly convert scrollable overflow to physical geometry.
|
// here to properly convert scrollable overflow to physical geometry.
|
||||||
|
let containing_block = PhysicalRect::zero();
|
||||||
match self {
|
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::Anonymous(fragment) => fragment.scrollable_overflow.clone(),
|
||||||
Fragment::Text(fragment) => fragment
|
Fragment::Text(fragment) => fragment
|
||||||
.rect
|
.rect
|
||||||
.to_physical(fragment.parent_style.writing_mode, &PhysicalRect::zero()),
|
.to_physical(fragment.parent_style.writing_mode, &containing_block),
|
||||||
Fragment::Image(fragment) => fragment
|
Fragment::Image(fragment) => fragment
|
||||||
.rect
|
.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 {
|
pub fn new(rect: Rect<Length>, children: Vec<Fragment>, mode: WritingMode) -> Self {
|
||||||
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
let content_origin = rect.start_corner.to_physical(mode);
|
||||||
// here to properly convert scrollable overflow to physical geometry.
|
|
||||||
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
|
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 {
|
AnonymousFragment {
|
||||||
rect,
|
rect,
|
||||||
|
@ -141,8 +146,9 @@ impl AnonymousFragment {
|
||||||
pub fn print(&self, tree: &mut PrintTree) {
|
pub fn print(&self, tree: &mut PrintTree) {
|
||||||
tree.new_level(format!(
|
tree.new_level(format!(
|
||||||
"Anonymous\
|
"Anonymous\
|
||||||
\nrect={:?}",
|
\nrect={:?}\
|
||||||
self.rect
|
\nscrollable_overflow={:?}",
|
||||||
|
self.rect, self.scrollable_overflow
|
||||||
));
|
));
|
||||||
|
|
||||||
for child in &self.children {
|
for child in &self.children {
|
||||||
|
@ -163,14 +169,10 @@ impl BoxFragment {
|
||||||
margin: Sides<Length>,
|
margin: Sides<Length>,
|
||||||
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
block_margins_collapsed_with_children: CollapsedBlockMargins,
|
||||||
) -> BoxFragment {
|
) -> BoxFragment {
|
||||||
// FIXME(mrobinson, bug 25564): We should be using the containing block
|
let scrollable_overflow_from_children =
|
||||||
// here to properly convert scrollable overflow to physical geometry.
|
children.iter().fold(PhysicalRect::zero(), |acc, child| {
|
||||||
let scrollable_overflow = children.iter().fold(
|
acc.union(&child.scrollable_overflow())
|
||||||
content_rect
|
});
|
||||||
.inflate(&border)
|
|
||||||
.to_physical(style.writing_mode, &PhysicalRect::zero()),
|
|
||||||
|acc, child| acc.union(&child.scrollable_overflow()),
|
|
||||||
);
|
|
||||||
BoxFragment {
|
BoxFragment {
|
||||||
tag,
|
tag,
|
||||||
style,
|
style,
|
||||||
|
@ -180,10 +182,28 @@ impl BoxFragment {
|
||||||
border,
|
border,
|
||||||
margin,
|
margin,
|
||||||
block_margins_collapsed_with_children,
|
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> {
|
pub fn padding_rect(&self) -> Rect<Length> {
|
||||||
self.content_rect.inflate(&self.padding)
|
self.content_rect.inflate(&self.padding)
|
||||||
}
|
}
|
||||||
|
@ -197,10 +217,17 @@ impl BoxFragment {
|
||||||
"Box\
|
"Box\
|
||||||
\ncontent={:?}\
|
\ncontent={:?}\
|
||||||
\npadding rect={:?}\
|
\npadding rect={:?}\
|
||||||
\nborder rect={:?}",
|
\nborder rect={:?}\
|
||||||
|
\nscrollable_overflow={:?}\
|
||||||
|
\noverflow={:?} / {:?}\
|
||||||
|
\nstyle={:p}",
|
||||||
self.content_rect,
|
self.content_rect,
|
||||||
self.padding_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 {
|
for child in &self.children {
|
||||||
|
@ -208,6 +235,41 @@ impl BoxFragment {
|
||||||
}
|
}
|
||||||
tree.end_level();
|
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 {
|
impl TextFragment {
|
||||||
|
|
|
@ -131,7 +131,6 @@ ${helpers.single_keyword(
|
||||||
"Overflow",
|
"Overflow",
|
||||||
"computed::Overflow::Visible",
|
"computed::Overflow::Visible",
|
||||||
engines="gecko servo-2013 servo-2020",
|
engines="gecko servo-2013 servo-2020",
|
||||||
servo_2020_pref="layout.2020.unimplemented",
|
|
||||||
logical_group="overflow",
|
logical_group="overflow",
|
||||||
logical=logical,
|
logical=logical,
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
|
|
|
@ -9,7 +9,7 @@ ${helpers.two_properties_shorthand(
|
||||||
"overflow-x",
|
"overflow-x",
|
||||||
"overflow-y",
|
"overflow-y",
|
||||||
"specified::Overflow::parse",
|
"specified::Overflow::parse",
|
||||||
engines="gecko servo-2013",
|
engines="gecko servo-2013 servo-2020",
|
||||||
flags="SHORTHAND_IN_GETCS",
|
flags="SHORTHAND_IN_GETCS",
|
||||||
needs_context=False,
|
needs_context=False,
|
||||||
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
|
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-repeat-repeat-x.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-repeat-repeat-y.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-size-contain.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[background-size-cover.xht]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[acid2_ref.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[border_radius_clip_a.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[position-sticky-bottom.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[position-sticky-top.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow_auto.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow_position_abs_simple_a.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow_scroll.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow_simple_a.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[overflow_xy_a.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text_overflow_ellipsis.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,2 +0,0 @@
|
||||||
[text_overflow_string.html]
|
|
||||||
expected: FAIL
|
|
Loading…
Add table
Add a link
Reference in a new issue