aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/fragment_tree
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/fragment_tree')
-rw-r--r--components/layout/fragment_tree/box_fragment.rs106
-rw-r--r--components/layout/fragment_tree/fragment.rs17
-rw-r--r--components/layout/fragment_tree/fragment_tree.rs21
-rw-r--r--components/layout/fragment_tree/positioning_fragment.rs2
4 files changed, 120 insertions, 26 deletions
diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs
index e87826ec3ca..65ad1c4aa93 100644
--- a/components/layout/fragment_tree/box_fragment.rs
+++ b/components/layout/fragment_tree/box_fragment.rs
@@ -2,11 +2,12 @@
* 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 app_units::Au;
+use app_units::{Au, MAX_AU, MIN_AU};
use atomic_refcell::AtomicRefCell;
use base::print_tree::PrintTree;
use malloc_size_of_derive::MallocSizeOf;
use servo_arc::Arc as ServoArc;
+use servo_geometry::f32_rect_to_au_rect;
use style::Zero;
use style::computed_values::border_collapse::T as BorderCollapse;
use style::computed_values::overflow_x::T as ComputedOverflow;
@@ -16,6 +17,7 @@ use style::properties::ComputedValues;
use style::values::specified::box_::DisplayOutside;
use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment};
+use crate::display_list::ToWebRender;
use crate::formatting_contexts::Baselines;
use crate::geom::{
AuOrAuto, LengthPercentageOrAuto, PhysicalPoint, PhysicalRect, PhysicalSides, ToLogical,
@@ -116,7 +118,7 @@ impl BoxFragment {
) -> BoxFragment {
let scrollable_overflow_from_children =
children.iter().fold(PhysicalRect::zero(), |acc, child| {
- acc.union(&child.scrollable_overflow())
+ acc.union(&child.scrollable_overflow_for_parent())
});
BoxFragment {
@@ -267,30 +269,96 @@ impl BoxFragment {
pub fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
let mut overflow = self.border_rect();
- if self.style.establishes_scroll_container(self.base.flags) {
- return overflow;
+ if !self.style.establishes_scroll_container(self.base.flags) {
+ // 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()),
+ );
+
+ let overflow_style = self.style.effective_overflow(self.base.flags);
+ if overflow_style.y == ComputedOverflow::Visible {
+ overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
+ overflow.size.height = bottom_right.y - overflow.origin.y;
+ }
+
+ if overflow_style.x == ComputedOverflow::Visible {
+ overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
+ overflow.size.width = bottom_right.x - overflow.origin.x;
+ }
}
- // 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()),
- );
+ // <https://drafts.csswg.org/css-overflow-3/#scrollable-overflow-region>
+ // > ...accounting for transforms by projecting each box onto the plane of
+ // > the element that establishes its 3D rendering context. [CSS3-TRANSFORMS]
+ // Both boxes and its scrollable overflow (if it is included) should be transformed accordingly.
+ //
+ // TODO(stevennovaryo): We are supposed to handle perspective transform and 3d context, but it is yet to happen.
+ if self
+ .style
+ .has_effective_transform_or_perspective(self.base.flags)
+ {
+ if let Some(transform) =
+ self.calculate_transform_matrix(&self.border_rect().to_untyped())
+ {
+ if let Some(transformed_overflow_box) =
+ transform.outer_transformed_rect(&overflow.to_webrender().to_rect())
+ {
+ overflow =
+ f32_rect_to_au_rect(transformed_overflow_box.to_untyped()).cast_unit();
+ }
+ }
+ }
+
+ overflow
+ }
- let overflow_style = self.style.effective_overflow(self.base.flags);
- if overflow_style.y == ComputedOverflow::Visible {
- overflow.origin.y = overflow.origin.y.min(scrollable_overflow.origin.y);
- overflow.size.height = bottom_right.y - overflow.origin.y;
+ /// <https://drafts.csswg.org/css-overflow/#unreachable-scrollable-overflow-region>
+ /// > area beyond the scroll origin in either axis is considered the unreachable scrollable overflow region
+ ///
+ /// Return the clipped the scrollable overflow based on its scroll origin, determined by overflow direction.
+ /// For an element, the clip rect is the padding rect and for viewport, it is the initial containing block.
+ pub fn clip_unreachable_scrollable_overflow_region(
+ &self,
+ scrollable_overflow: PhysicalRect<Au>,
+ clipping_rect: PhysicalRect<Au>,
+ ) -> PhysicalRect<Au> {
+ let scrolling_direction = self.style.overflow_direction();
+ let mut scrollable_overflow_box = scrollable_overflow.to_box2d();
+ let mut clipping_box = clipping_rect.to_box2d();
+
+ if scrolling_direction.rightward {
+ clipping_box.max.x = MAX_AU;
+ } else {
+ clipping_box.min.x = MIN_AU;
}
- if overflow_style.x == ComputedOverflow::Visible {
- overflow.origin.x = overflow.origin.x.min(scrollable_overflow.origin.x);
- overflow.size.width = bottom_right.x - overflow.origin.x;
+ if scrolling_direction.downward {
+ clipping_box.max.y = MAX_AU;
+ } else {
+ clipping_box.min.y = MIN_AU;
}
- overflow
+ scrollable_overflow_box = scrollable_overflow_box.intersection_unchecked(&clipping_box);
+
+ match scrollable_overflow_box.is_negative() {
+ true => PhysicalRect::zero(),
+ false => scrollable_overflow_box.to_rect(),
+ }
+ }
+
+ /// <https://drafts.csswg.org/css-overflow/#unreachable-scrollable-overflow-region>
+ /// > area beyond the scroll origin in either axis is considered the unreachable scrollable overflow region
+ ///
+ /// Return the clipped the scrollable overflow based on its scroll origin, determined by overflow direction.
+ /// This will coincides with the scrollport if the fragment is a scroll container.
+ pub fn reachable_scrollable_overflow_region(&self) -> PhysicalRect<Au> {
+ self.clip_unreachable_scrollable_overflow_region(
+ self.scrollable_overflow(),
+ self.padding_rect(),
+ )
}
pub(crate) fn calculate_resolved_insets_if_positioned(&self) -> PhysicalSides<AuOrAuto> {
diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs
index 7708b0893ee..1c5324fa1c4 100644
--- a/components/layout/fragment_tree/fragment.rs
+++ b/components/layout/fragment_tree/fragment.rs
@@ -170,17 +170,28 @@ impl Fragment {
}
}
- pub fn scrolling_area(&self) -> PhysicalRect<Au> {
+ pub fn unclipped_scrolling_area(&self) -> PhysicalRect<Au> {
match self {
Fragment::Box(fragment) | Fragment::Float(fragment) => {
let fragment = fragment.borrow();
fragment.offset_by_containing_block(&fragment.scrollable_overflow())
},
- _ => self.scrollable_overflow(),
+ _ => self.scrollable_overflow_for_parent(),
+ }
+ }
+
+ pub fn scrolling_area(&self) -> PhysicalRect<Au> {
+ match self {
+ Fragment::Box(fragment) | Fragment::Float(fragment) => {
+ let fragment = fragment.borrow();
+ fragment
+ .offset_by_containing_block(&fragment.reachable_scrollable_overflow_region())
+ },
+ _ => self.scrollable_overflow_for_parent(),
}
}
- pub fn scrollable_overflow(&self) -> PhysicalRect<Au> {
+ pub fn scrollable_overflow_for_parent(&self) -> PhysicalRect<Au> {
match self {
Fragment::Box(fragment) | Fragment::Float(fragment) => {
fragment.borrow().scrollable_overflow_for_parent()
diff --git a/components/layout/fragment_tree/fragment_tree.rs b/components/layout/fragment_tree/fragment_tree.rs
index 3a082c99389..1499a50dacf 100644
--- a/components/layout/fragment_tree/fragment_tree.rs
+++ b/components/layout/fragment_tree/fragment_tree.rs
@@ -138,11 +138,26 @@ 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;
- for fragment in self.root_fragments.iter() {
- scroll_area = fragment.scrolling_area().union(&scroll_area);
+ 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
}
- scroll_area
}
}
diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs
index 1fe968eb484..0cf525a3479 100644
--- a/components/layout/fragment_tree/positioning_fragment.rs
+++ b/components/layout/fragment_tree/positioning_fragment.rs
@@ -56,7 +56,7 @@ impl PositioningFragment {
let scrollable_overflow = children.iter().fold(PhysicalRect::zero(), |acc, child| {
acc.union(
&child
- .scrollable_overflow()
+ .scrollable_overflow_for_parent()
.translate(content_origin.to_vector()),
)
});