tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit cf03ca40e8851d529c1c5a07e05cd3fa40375901
parent 7c4b3317be721ed9cc21942ca1723c6c37535781
Author: Glenn Watson <git@chillybin.org>
Date:   Wed,  5 Nov 2025 19:25:37 +0000

Bug 1998317 - Ensure scroll offsets used in nested sticky frames are snapped to device pixels r=gfx-reviewers,lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D271337

Diffstat:
Mgfx/wr/webrender/src/spatial_node.rs | 10+++++++++-
Mgfx/wr/webrender/src/util.rs | 16++++++++++++++++
Agfx/wr/wrench/reftests/scrolling/fractional-sticky-ref.yaml | 11+++++++++++
Agfx/wr/wrench/reftests/scrolling/fractional-sticky.yaml | 27+++++++++++++++++++++++++++
Mgfx/wr/wrench/reftests/scrolling/reftest.list | 1+
5 files changed, 64 insertions(+), 1 deletion(-)

diff --git a/gfx/wr/webrender/src/spatial_node.rs b/gfx/wr/webrender/src/spatial_node.rs @@ -12,7 +12,8 @@ use crate::spatial_tree::{CoordinateSystem, SpatialNodeIndex, TransformUpdateSta use crate::spatial_tree::CoordinateSystemId; use euclid::{Vector2D, SideOffsets2D}; use crate::scene::SceneProperties; -use crate::util::{LayoutFastTransform, MatrixHelpers, ScaleOffset, TransformedRectKind, PointHelpers}; +use crate::util::{LayoutFastTransform, MatrixHelpers, ScaleOffset, TransformedRectKind}; +use crate::util::{PointHelpers, VectorHelpers}; /// The kind of a spatial node uid. These are required because we currently create external /// nodes during DL building, but the internal nodes aren't created until scene building. @@ -338,6 +339,13 @@ impl SpatialNode { for element in offsets.iter_mut() { element.offset = -element.offset - scrolling.external_scroll_offset; + + // Once the final scroll offset (APZ + content external offset) is + // calculated, we need to snap it to a device pixel. We already snap + // the final transforms in `update_transform`. However, we need to + // ensure the offsets are also snapped so that if the offset is used + // in a nested sticky frame, it is pre-snapped. + element.offset = element.offset.snap(); } if scrolling.offsets == offsets { diff --git a/gfx/wr/webrender/src/util.rs b/gfx/wr/webrender/src/util.rs @@ -583,6 +583,22 @@ impl<U> PointHelpers<U> for Point2D<f32, U> { } } +pub trait VectorHelpers<U> +where + Self: Sized, +{ + fn snap(&self) -> Self; +} + +impl<U> VectorHelpers<U> for Vector2D<f32, U> { + fn snap(&self) -> Self { + Vector2D::new( + self.x.round(), + self.y.round(), + ) + } +} + pub trait RectHelpers<U> where Self: Sized, diff --git a/gfx/wr/wrench/reftests/scrolling/fractional-sticky-ref.yaml b/gfx/wr/wrench/reftests/scrolling/fractional-sticky-ref.yaml @@ -0,0 +1,11 @@ +root: + items: + - type: rect + bounds: [0, 0, 400, 400] + color: black + - type: rect + bounds: [0, 379, 20, 20] + color: green + - type: rect + bounds: [0, 400, 20, 20] + color: red diff --git a/gfx/wr/wrench/reftests/scrolling/fractional-sticky.yaml b/gfx/wr/wrench/reftests/scrolling/fractional-sticky.yaml @@ -0,0 +1,27 @@ +# Verify that fractional scroll offsets are correctly snapped before +# being included in nested sticky frames. +root: + items: + - type: scroll-frame + id: 2 + bounds: [0, 0, 1000, 1000] + content-size: [1000, 8000] + scroll-offset: [0, 1000.7] + items: + - type: rect + bounds: [0, 0, 200, 800.6667] + color: blue + - type: rect + bounds: [0, 800.6667, 400, 600] + color: black + - type: sticky-frame + bounds: [0, 818.1667, 20, 20] + margin-top: 399 + vertical-offset-bounds: [0, 561.5] + items: + - type: rect + bounds: [0, 819.1667, 20, 20] + color: green + - type: rect + bounds: [0, 1400.6667, 20, 20] + color: red diff --git a/gfx/wr/wrench/reftests/scrolling/reftest.list b/gfx/wr/wrench/reftests/scrolling/reftest.list @@ -24,3 +24,4 @@ == scroll-generation-3.yaml scroll-generation-ref.yaml == scroll-generation-4.yaml scroll-generation-ref.yaml == ext-scroll-offset-rounded-clip.yaml ext-scroll-offset-rounded-clip-ref.yaml +== fractional-sticky.yaml fractional-sticky-ref.yaml