Skip to content

Commit 75370b0

Browse files
committed
Bug 1831148. Allow nsLayoutUtils::TransformFrameRectToAncestor to use full nscoord range. r=gfx-reviewers,lsalzman
The problem with the previous patch was that a rect spanning (-reallybig,reallybig) would get reduced to fit in an nscoord based (x,y,width,height) rect by clamping each of x, y, width, height individually, which would result in the clamped rect having origin nscoord_min and size nscoord_max so that it would span (nscoord_min,0), which means it does not intersect the positive axis at all, which in most cases is the only part of the coord system that is visible. We already have a solution for this exact problem in the code base. nsLayoutUtils::RoundGfxRectToAppRect uses ConstrainToCoordValues which reduces the size of the rect equally on both ends so the above rect would span (nscoord_min/2,nscoord_max/2) and have origin nscoord_min/2 and size nscoord_max. However we can't use RoundGfxRectToAppRect here because it has different rounding behaviour (and we have tests that depend on it, so I assume web sites will too). So we create a new function that is like RoundGfxRectToAppRect but has the rounding behaviour that we have in the existing function here I re-used David's existing test from the previous patch. Differential Revision: https://phabricator.services.mozilla.com/D269158
1 parent 1667ec5 commit 75370b0

File tree

4 files changed

+101
-10
lines changed

4 files changed

+101
-10
lines changed

layout/base/nsLayoutUtils.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,9 +2185,9 @@ static Rect TransformGfxRectToAncestor(
21852185
}
21862186
const nsIFrame* ancestor = aOutAncestor ? *aOutAncestor : aAncestor.mFrame;
21872187
float factor = ancestor->PresContext()->AppUnitsPerDevPixel();
2188-
Rect maxBounds =
2189-
Rect(float(nscoord_MIN) / factor * 0.5, float(nscoord_MIN) / factor * 0.5,
2190-
float(nscoord_MAX) / factor, float(nscoord_MAX) / factor);
2188+
Rect maxBounds = Rect(
2189+
float(nscoord_MIN) / factor, float(nscoord_MIN) / factor,
2190+
float(nscoord_MAX) / factor * 2.0, float(nscoord_MAX) / factor * 2.0);
21912191
return ctm.TransformAndClipBounds(aRect, maxBounds);
21922192
}
21932193

@@ -2391,13 +2391,8 @@ nsRect nsLayoutUtils::TransformFrameRectToAncestor(
23912391
aMatrixCache, aStopAtStackingContextAndDisplayPortAndOOFFrame,
23922392
aOutAncestor);
23932393

2394-
float destAppUnitsPerDevPixel =
2395-
aAncestor.mFrame->PresContext()->AppUnitsPerDevPixel();
2396-
return nsRect(
2397-
NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
2398-
NSFloatPixelsToAppUnits(float(result.y), destAppUnitsPerDevPixel),
2399-
NSFloatPixelsToAppUnits(float(result.width), destAppUnitsPerDevPixel),
2400-
NSFloatPixelsToAppUnits(float(result.height), destAppUnitsPerDevPixel));
2394+
return ScaleThenRoundGfxRectToAppRect(
2395+
result, aAncestor.mFrame->PresContext()->AppUnitsPerDevPixel());
24012396
}
24022397

24032398
LayoutDeviceIntPoint nsLayoutUtils::WidgetToWidgetOffset(nsIWidget* aFrom,

layout/base/nsLayoutUtils.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,6 +1101,19 @@ class nsLayoutUtils {
11011101
template <typename T>
11021102
static nsRect RoundGfxRectToAppRect(const T& aRect, const float aFactor);
11031103

1104+
/**
1105+
* Like the above but slightly different scale and round behaviour. First
1106+
* scales, then constrains to nscoord, then rounds each component (x, y,
1107+
* width, height) individually.
1108+
*
1109+
* @param aRect The graphics rect to round outward.
1110+
* @param aFactor The number of app units per graphics unit.
1111+
* @return The rounaded rectangle in app space.
1112+
*/
1113+
template <typename T>
1114+
static nsRect ScaleThenRoundGfxRectToAppRect(const T& aRect,
1115+
const float aFactor);
1116+
11041117
/**
11051118
* Returns a subrectangle of aContainedRect that is entirely inside the
11061119
* rounded rect. Complex cases are handled conservatively by returning a
@@ -3274,6 +3287,41 @@ nsRect nsLayoutUtils::RoundGfxRectToAppRect(const T& aRect,
32743287
return retval;
32753288
}
32763289

3290+
template <typename T>
3291+
nsRect nsLayoutUtils::ScaleThenRoundGfxRectToAppRect(const T& aRect,
3292+
const float aFactor) {
3293+
// Get a new Rect whose units are app units by scaling by the specified
3294+
// factor.
3295+
T scaledRect = aRect;
3296+
scaledRect.Scale(aFactor);
3297+
3298+
nsRect retval;
3299+
3300+
double start = double(scaledRect.x);
3301+
double size = double(scaledRect.width);
3302+
// We now need to constrain our results to the max and min values for coords.
3303+
ConstrainToCoordValues(start, size);
3304+
// ConstrainToCoordValues ensures converting to nscoord is safe.
3305+
retval.x = NSToCoordRoundWithClamp(start);
3306+
retval.width = NSToCoordRoundWithClamp(size);
3307+
3308+
start = double(scaledRect.y);
3309+
size = double(scaledRect.height);
3310+
ConstrainToCoordValues(start, size);
3311+
retval.y = NSToCoordRoundWithClamp(start);
3312+
retval.height = NSToCoordRoundWithClamp(size);
3313+
3314+
if (!aRect.Width()) {
3315+
retval.SetWidth(0);
3316+
}
3317+
3318+
if (!aRect.Height()) {
3319+
retval.SetHeight(0);
3320+
}
3321+
3322+
return retval;
3323+
}
3324+
32773325
namespace mozilla {
32783326

32793327
/**

layout/generic/test/mochitest.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ skip-if = ["!debug"] # Bug 1838577
180180

181181
["test_bug1803209.html"]
182182

183+
["test_bug1831148.html"]
184+
183185
["test_bug1847329.html"]
184186

185187
["test_crash_on_mouse_move.html"]
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<script src="/tests/SimpleTest/SimpleTest.js"></script>
4+
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
5+
<style>
6+
#marker {
7+
width: 1px;
8+
height: 1px;
9+
}
10+
#outer {
11+
height: 1000px;
12+
background: #a0ffff;
13+
overflow: scroll;
14+
}
15+
16+
#inner {
17+
height: 16593200px;
18+
background: #ffa0a0;
19+
}
20+
21+
.log {
22+
white-space: pre;
23+
}
24+
</style>
25+
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1831148">Mozilla Bug 1831148</a>
26+
<div id="marker"></div>
27+
<div id="outer">
28+
<div id="inner"></div>
29+
</div>
30+
<script>
31+
SimpleTest.waitForExplicitFinish();
32+
addLoadEvent(() => {
33+
const interval = 20;
34+
const increment = outer.scrollHeight / interval;
35+
const offset = marker.getBoundingClientRect().bottom;
36+
for (let i = 0; i < interval; i++) {
37+
outer.scrollTop = i * increment;
38+
console.log(outer.scrollTop);
39+
// Shift to account for viewport coordinate shift.
40+
const bcrTop = -inner.getBoundingClientRect().top + offset;
41+
// Floating point value diverges from scrollTop.
42+
isfuzzy(bcrTop, outer.scrollTop, 1, "scrollTop and BCR top match");
43+
}
44+
SimpleTest.finish();
45+
});
46+
</script>

0 commit comments

Comments
 (0)