зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1746098 - Reflow scrolled inner frame in TryLayout() only when sizes of scrollbar gutter change. r=emilio
We now support scrollbar-gutter property. So for example, assume the scroll container has "scrollbar-gutter:stable". When toggling the visibility of inline-end scrollbar, we can skip the reflow for the scroll inner frame because the available inline-size for it cannot change. This patch teaches TryLayout() to consider the sizes of scrollbar gutter rather than the (assumed) visibility of scrollbars when deciding the need to call ReflowScrolledFrame(). Also, TryLayout() doesn't need to report an inconsistent layout unless the (showHScrollbar, showVScrollbar) pair changes the sizes of scrollbar gutters. Differential Revision: https://phabricator.services.mozilla.com/D134373
This commit is contained in:
Родитель
3adbed7d01
Коммит
d570e88e1f
|
@ -701,7 +701,7 @@ load 1474768.html
|
|||
load 1478178.html
|
||||
load 1483972.html
|
||||
load 1486457.html
|
||||
asserts(2-4) load 1488762-1.html # asserts from integer overflow & bogus sizes
|
||||
asserts(1-4) load 1488762-1.html # asserts from integer overflow & bogus sizes
|
||||
load 1489287.html
|
||||
load 1489863.html
|
||||
load 1489770.html
|
||||
|
@ -798,5 +798,5 @@ pref(layout.css.aspect-ratio.enabled,true) load 1682032.html
|
|||
pref(layout.css.aspect-ratio.enabled,true) load 1699263.html
|
||||
pref(layout.css.aspect-ratio.enabled,true) load 1699468.html
|
||||
load 1728319.html
|
||||
asserts(4-8) load 1730506.html # asserts from integer overflow & bogus sizes
|
||||
asserts(2-8) load 1730506.html # asserts from integer overflow & bogus sizes
|
||||
asserts(1-4) load 1730570.html # asserts from integer overflow & bogus sizes
|
||||
|
|
|
@ -351,6 +351,10 @@ struct MOZ_STACK_CLASS ScrollReflowInput {
|
|||
|
||||
// === Filled in by ReflowScrolledFrame ===
|
||||
OverflowAreas mContentsOverflowAreas;
|
||||
// The scrollbar gutter sizes used in the most recent reflow of
|
||||
// mHelper.mScrolledFrame. The writing-mode is the same as the scroll
|
||||
// container.
|
||||
LogicalMargin mScrollbarGutterFromLastReflow;
|
||||
// True if the most recent reflow of mHelper.mScrolledFrame is with the
|
||||
// horizontal scrollbar.
|
||||
bool mReflowedContentsWithHScrollbar = false;
|
||||
|
@ -410,6 +414,8 @@ struct MOZ_STACK_CLASS ScrollReflowInput {
|
|||
nsSize mVScrollbarPrefSize;
|
||||
nsSize mHScrollbarMinSize;
|
||||
nsSize mHScrollbarPrefSize;
|
||||
// The scrollbar gutter sizes resolved from the scrollbar-gutter and
|
||||
// scrollbar-width property.
|
||||
nsMargin mScrollbarGutter;
|
||||
};
|
||||
|
||||
|
@ -421,7 +427,8 @@ ScrollReflowInput::ScrollReflowInput(nsHTMLScrollFrame* aFrame,
|
|||
mBoxState(aReflowInput.mFrame->PresContext(),
|
||||
aReflowInput.mRenderingContext),
|
||||
mComputedBorder(aReflowInput.ComputedPhysicalBorderPadding() -
|
||||
aReflowInput.ComputedPhysicalPadding()) {
|
||||
aReflowInput.ComputedPhysicalPadding()),
|
||||
mScrollbarGutterFromLastReflow(aFrame->GetWritingMode()) {
|
||||
ScrollStyles styles = aFrame->GetScrollStyles();
|
||||
mHScrollbar = ShouldShowScrollbar(styles.mHorizontal);
|
||||
mVScrollbar = ShouldShowScrollbar(styles.mVertical);
|
||||
|
@ -546,23 +553,23 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput& aState,
|
|||
return false;
|
||||
}
|
||||
|
||||
const bool assumeVScrollChanged =
|
||||
aAssumeVScroll != aState.mReflowedContentsWithVScrollbar;
|
||||
const bool assumeHScrollChanged =
|
||||
aAssumeHScroll != aState.mReflowedContentsWithHScrollbar;
|
||||
const bool isVertical = GetWritingMode().IsVertical();
|
||||
const auto wm = GetWritingMode();
|
||||
const nsMargin scrollbarGutter = aState.ScrollbarGutter(
|
||||
aAssumeVScroll, aAssumeHScroll, IsScrollbarOnRight());
|
||||
const LogicalMargin logicalScrollbarGutter(wm, scrollbarGutter);
|
||||
|
||||
const bool shouldReflowScolledFrame = [=]() {
|
||||
if (isVertical) {
|
||||
return assumeHScrollChanged ||
|
||||
(assumeVScrollChanged && ScrolledContentDependsOnBSize(aState));
|
||||
}
|
||||
return assumeVScrollChanged ||
|
||||
(assumeHScrollChanged && ScrolledContentDependsOnBSize(aState));
|
||||
}();
|
||||
const bool inlineEndsGutterChanged =
|
||||
aState.mScrollbarGutterFromLastReflow.IStartEnd(wm) !=
|
||||
logicalScrollbarGutter.IStartEnd(wm);
|
||||
const bool blockEndsGutterChanged =
|
||||
aState.mScrollbarGutterFromLastReflow.BStartEnd(wm) !=
|
||||
logicalScrollbarGutter.BStartEnd(wm);
|
||||
const bool shouldReflowScrolledFrame =
|
||||
inlineEndsGutterChanged ||
|
||||
(blockEndsGutterChanged && ScrolledContentDependsOnBSize(aState));
|
||||
|
||||
if (shouldReflowScolledFrame) {
|
||||
if (isVertical ? assumeVScrollChanged : assumeHScrollChanged) {
|
||||
if (shouldReflowScrolledFrame) {
|
||||
if (blockEndsGutterChanged) {
|
||||
nsLayoutUtils::MarkIntrinsicISizesDirtyIfDependentOnBSize(
|
||||
mHelper.mScrolledFrame);
|
||||
}
|
||||
|
@ -573,8 +580,6 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput& aState,
|
|||
ReflowScrolledFrame(aState, aAssumeHScroll, aAssumeVScroll, aKidMetrics);
|
||||
}
|
||||
|
||||
const nsMargin scrollbarGutter = aState.ScrollbarGutter(
|
||||
aAssumeVScroll, aAssumeHScroll, IsScrollbarOnRight());
|
||||
const nsSize scrollbarGutterSize(scrollbarGutter.LeftRight(),
|
||||
scrollbarGutter.TopBottom());
|
||||
|
||||
|
@ -644,15 +649,17 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput& aState,
|
|||
ToString(scrollPortSize).c_str());
|
||||
nscoord oneDevPixel = aState.mBoxState.PresContext()->DevPixelsToAppUnits(1);
|
||||
|
||||
bool showHScrollbar = aAssumeHScroll;
|
||||
bool showVScrollbar = aAssumeVScroll;
|
||||
if (!aForce) {
|
||||
nsSize sizeToCompare = visualViewportSize;
|
||||
if (gfxPlatform::UseDesktopZoomingScrollbars()) {
|
||||
sizeToCompare = scrollPortSize;
|
||||
}
|
||||
|
||||
// If the style is HIDDEN then we already know that aAssumeHScroll is false
|
||||
// No need to compute showHScrollbar if we got ShowScrollbar::Never.
|
||||
if (aState.mHScrollbar != ShowScrollbar::Never) {
|
||||
bool wantHScrollbar =
|
||||
showHScrollbar =
|
||||
aState.mHScrollbar == ShowScrollbar::Always ||
|
||||
scrolledRect.XMost() >= sizeToCompare.width + oneDevPixel ||
|
||||
scrolledRect.x <= -oneDevPixel;
|
||||
|
@ -660,18 +667,15 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput& aState,
|
|||
// in both axes, for consistency?
|
||||
if (aState.mHScrollbar == ShowScrollbar::Auto &&
|
||||
scrollPortSize.width < aState.HScrollbarMinWidth()) {
|
||||
wantHScrollbar = false;
|
||||
showHScrollbar = false;
|
||||
}
|
||||
ROOT_SCROLLBAR_LOG("TryLayout wants H Scrollbar: %d =? %d\n",
|
||||
wantHScrollbar, aAssumeHScroll);
|
||||
if (wantHScrollbar != aAssumeHScroll) {
|
||||
return false;
|
||||
}
|
||||
showHScrollbar, aAssumeHScroll);
|
||||
}
|
||||
|
||||
// If the style is HIDDEN then we already know that aAssumeVScroll is false
|
||||
// No need to compute showVScrollbar if we got ShowScrollbar::Never.
|
||||
if (aState.mVScrollbar != ShowScrollbar::Never) {
|
||||
bool wantVScrollbar =
|
||||
showVScrollbar =
|
||||
aState.mVScrollbar == ShowScrollbar::Always ||
|
||||
scrolledRect.YMost() >= sizeToCompare.height + oneDevPixel ||
|
||||
scrolledRect.y <= -oneDevPixel;
|
||||
|
@ -679,18 +683,27 @@ bool nsHTMLScrollFrame::TryLayout(ScrollReflowInput& aState,
|
|||
// in both axes, for consistency?
|
||||
if (aState.mVScrollbar == ShowScrollbar::Auto &&
|
||||
scrollPortSize.height < aState.VScrollbarMinHeight()) {
|
||||
wantVScrollbar = false;
|
||||
showVScrollbar = false;
|
||||
}
|
||||
ROOT_SCROLLBAR_LOG("TryLayout wants V Scrollbar: %d =? %d\n",
|
||||
wantVScrollbar, aAssumeVScroll);
|
||||
if (wantVScrollbar != aAssumeVScroll) {
|
||||
showVScrollbar, aAssumeVScroll);
|
||||
}
|
||||
|
||||
if (showHScrollbar != aAssumeHScroll || showVScrollbar != aAssumeVScroll) {
|
||||
const nsMargin wantedScrollbarGutter = aState.ScrollbarGutter(
|
||||
showVScrollbar, showHScrollbar, IsScrollbarOnRight());
|
||||
// We report an inconsistent layout only when the desired visibility of
|
||||
// the scrollbars can change the size of the scrollbar gutters.
|
||||
if (scrollbarGutter != wantedScrollbarGutter) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aState.mShowHScrollbar = aAssumeHScroll;
|
||||
aState.mShowVScrollbar = aAssumeVScroll;
|
||||
// If we reach here, the layout is consistent. Record the desired visibility
|
||||
// of the scrollbars.
|
||||
aState.mShowHScrollbar = showHScrollbar;
|
||||
aState.mShowVScrollbar = showVScrollbar;
|
||||
const nsPoint scrollPortOrigin(
|
||||
aState.mComputedBorder.left + scrollbarGutter.left,
|
||||
aState.mComputedBorder.top + scrollbarGutter.top);
|
||||
|
@ -752,7 +765,7 @@ void nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowInput& aState,
|
|||
bool aAssumeHScroll,
|
||||
bool aAssumeVScroll,
|
||||
ReflowOutput* aMetrics) {
|
||||
WritingMode wm = mHelper.mScrolledFrame->GetWritingMode();
|
||||
const WritingMode wm = GetWritingMode();
|
||||
|
||||
// these could be NS_UNCONSTRAINEDSIZE ... std::min arithmetic should
|
||||
// be OK
|
||||
|
@ -884,6 +897,7 @@ void nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowInput& aState,
|
|||
}
|
||||
|
||||
aState.mContentsOverflowAreas = aMetrics->mOverflowAreas;
|
||||
aState.mScrollbarGutterFromLastReflow = scrollbarGutter;
|
||||
aState.mReflowedContentsWithHScrollbar = aAssumeHScroll;
|
||||
aState.mReflowedContentsWithVScrollbar = aAssumeVScroll;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
[scrollbar-gutter-reflow-counts-001.html]
|
||||
[Enlarge the child's block-size to 200%]
|
||||
expected:
|
||||
if os == "android": FAIL # Overlay scrollbars do not create scrollbar gutters.
|
||||
|
||||
[Enlarge the child's block-size to 300px]
|
||||
expected:
|
||||
if os == "android": FAIL # Overlay scrollbars do not create scrollbar gutters.
|
||||
|
||||
[Enlarge the child's block-size to 200% in a vertical-lr scroll container]
|
||||
expected:
|
||||
if os == "android": FAIL # Overlay scrollbars do not create scrollbar gutters.
|
||||
|
||||
[Enlarge the child's block-size to 300px in a vertical-lr scroll container]
|
||||
expected:
|
||||
if os == "android": FAIL # Overlay scrollbars do not create scrollbar gutters.
|
||||
|
||||
[Enlarge the child's block-size to 200% in a vertical-rl scroll container]
|
||||
expected:
|
||||
if os == "android": FAIL # Overlay scrollbars do not create scrollbar gutters.
|
||||
|
||||
[Enlarge the child's block-size to 300px in a vertical-rl scroll container]
|
||||
expected:
|
||||
if os == "android": FAIL # Overlay scrollbars do not create scrollbar gutters.
|
|
@ -0,0 +1,115 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Overflow: Test scrollbar-gutter reflow counts</title>
|
||||
<link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
|
||||
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1746098">
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<style>
|
||||
#target {
|
||||
inline-size: 200px;
|
||||
block-size: 100px;
|
||||
background: lightgray;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#targetChild {
|
||||
inline-size: 100%;
|
||||
block-size: 100%;
|
||||
background: orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Here is a scroll contaier for testing:</p>
|
||||
<div id="target">
|
||||
<div id="targetChild"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let gUtils = SpecialPowers.getDOMWindowUtils(window);
|
||||
let gTarget = document.getElementById("target");
|
||||
let gTargetChild = document.getElementById("targetChild");
|
||||
|
||||
function getReflowCount()
|
||||
{
|
||||
document.documentElement.offsetHeight; // flush layout
|
||||
return gUtils.framesReflowed;
|
||||
}
|
||||
|
||||
function cleanUp() {
|
||||
gTarget.style.writingMode = "";
|
||||
gTarget.style.scrollbarGutter = "";
|
||||
gTargetChild.style.blockSize = "";
|
||||
}
|
||||
|
||||
function tweakStyleAndCountReflows(aAddStyle, aAddScrollbarGutter)
|
||||
{
|
||||
let beforeCount = getReflowCount();
|
||||
if (aAddScrollbarGutter) {
|
||||
gTarget.style.scrollbarGutter = "stable";
|
||||
}
|
||||
aAddStyle();
|
||||
let afterCount = getReflowCount();
|
||||
cleanUp();
|
||||
|
||||
let numReflows = afterCount - beforeCount;
|
||||
assert_greater_than(numReflows, 0, "We should've reflowed *something* after changing styles:");
|
||||
return numReflows;
|
||||
}
|
||||
|
||||
let gTestCases = [
|
||||
{
|
||||
name : "Enlarge the child's block-size to 200%",
|
||||
addStyle : function () {
|
||||
gTargetChild.style.blockSize = "200%";
|
||||
},
|
||||
},
|
||||
{
|
||||
name : "Enlarge the child's block-size to 300px",
|
||||
addStyle : function () {
|
||||
gTargetChild.style.blockSize = "300px";
|
||||
},
|
||||
},
|
||||
{
|
||||
name : "Enlarge the child's block-size to 200% in a vertical-lr scroll container",
|
||||
addStyle : function () {
|
||||
gTarget.style.writingMode = "vertical-lr";
|
||||
gTargetChild.style.blockSize = "200%";
|
||||
},
|
||||
},
|
||||
{
|
||||
name : "Enlarge the child's block-size to 300px in a vertical-lr scroll container",
|
||||
addStyle : function () {
|
||||
gTarget.style.writingMode = "vertical-lr";
|
||||
gTargetChild.style.blockSize = "300px";
|
||||
},
|
||||
},
|
||||
{
|
||||
name : "Enlarge the child's block-size to 200% in a vertical-rl scroll container",
|
||||
addStyle : function () {
|
||||
gTarget.style.writingMode = "vertical-rl";
|
||||
gTargetChild.style.blockSize = "200%";
|
||||
},
|
||||
},
|
||||
{
|
||||
name : "Enlarge the child's block-size to 300px in a vertical-rl scroll container",
|
||||
addStyle : function () {
|
||||
gTarget.style.writingMode = "vertical-rl";
|
||||
gTargetChild.style.blockSize = "300px";
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (let testcase of gTestCases) {
|
||||
test(function () {
|
||||
let numTestReflows = tweakStyleAndCountReflows(testcase.addStyle, true);
|
||||
let numReferenceReflows = tweakStyleAndCountReflows(testcase.addStyle, false);
|
||||
assert_less_than(numTestReflows, numReferenceReflows,
|
||||
"A scroll container with 'scrollbar-gutter:stable' should have less reflow counts:");
|
||||
}, testcase.name)
|
||||
}
|
||||
</script>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче