Bug 780345. Use a heuristic to reduce the scroll-port height by the height of fixed-position headers and footers when scrolling the viewport vertically by pages. r=tnikkel

--HG--
extra : rebase_source : 0f6d4a64b2f3832deb2af1f9ae0d332db042e127
This commit is contained in:
Robert O'Callahan 2012-08-09 23:17:40 +12:00
parent d2d218e577
commit 9adec2a25b
4 changed files with 148 additions and 2 deletions

View File

@ -2556,15 +2556,62 @@ nsGfxScrollFrameInner::GetLineScrollAmount() const
return nsSize(fontHeight, fontHeight);
}
/**
* Compute the scrollport size excluding any fixed-pos headers and
* footers. A header or footer is an box that spans that entire width
* of the viewport and touches the top (or bottom, respectively) of the
* viewport. Headers and footers that cover more than a quarter of the
* the viewport are ignored since they probably aren't true headers and
* footers and we don't want to restrict scrolling too much in such cases.
* This is a bit conservative --- some pages use elements as headers or
* footers that don't span the entire width of the viewport --- but it
* should be a good start.
*/
static nsSize
GetScrollPortSizeExcludingHeadersAndFooters(nsIFrame* aViewportFrame,
const nsRect& aScrollPort)
{
nsFrameList fixedFrames = aViewportFrame->GetChildList(nsIFrame::kFixedList);
nscoord headerBottom = 0;
nscoord footerTop = aScrollPort.height;
for (nsFrameList::Enumerator iterator(fixedFrames); !iterator.AtEnd();
iterator.Next()) {
nsIFrame* f = iterator.get();
nsRect r = f->GetRect().Intersect(aScrollPort);
if (r.x == 0 && r.width == aScrollPort.width &&
r.height <= aScrollPort.height/3) {
if (r.y == 0) {
headerBottom = NS_MAX(headerBottom, r.height);
}
if (r.YMost() == aScrollPort.height) {
footerTop = NS_MIN(footerTop, r.y);
}
}
}
return nsSize(aScrollPort.width, footerTop - headerBottom);
}
nsSize
nsGfxScrollFrameInner::GetPageScrollAmount() const
{
nsSize lineScrollAmount = GetLineScrollAmount();
nsSize effectiveScrollPortSize;
if (mIsRoot) {
// Reduce effective scrollport height by the height of any fixed-pos
// headers or footers
nsIFrame* root = mOuter->PresContext()->PresShell()->GetRootFrame();
effectiveScrollPortSize =
GetScrollPortSizeExcludingHeadersAndFooters(root, mScrollPort);
} else {
effectiveScrollPortSize = mScrollPort.Size();
}
// The page increment is the size of the page, minus the smaller of
// 10% of the size or 2 lines.
return nsSize(
mScrollPort.width - NS_MIN(mScrollPort.width/10, 2*lineScrollAmount.width),
mScrollPort.height - NS_MIN(mScrollPort.height/10, 2*lineScrollAmount.height));
effectiveScrollPortSize.width -
NS_MIN(effectiveScrollPortSize.width/10, 2*lineScrollAmount.width),
effectiveScrollPortSize.height -
NS_MIN(effectiveScrollPortSize.height/10, 2*lineScrollAmount.height));
}
/**

View File

@ -65,6 +65,8 @@ MOCHITEST_FILES = \
test_invalidate_during_plugin_paint.html \
test_movement_by_characters.html \
test_movement_by_words.html \
test_page_scroll_with_fixed_pos.html \
page_scroll_with_fixed_pos_window.html \
test_plugin_clipping.xhtml \
test_plugin_clipping2.xhtml \
test_plugin_clipping_transformed.xhtml \

View File

@ -0,0 +1,81 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Scrolling by pages with fixed-pos headers and footers</title>
<style>
.fp { position:fixed; left:0; width:100%; }
</style>
</head>
<body onscroll="didScroll()" onload="test()">
<div class="fp" id="top" style="top:0; height:10px; background:yellow;"></div>
<div class="fp" style="top:50%; height:7px; background:cyan;"></div>
<div class="fp" id="bottom" style="bottom:0; height:13px; background:yellow;"></div>
<p id="target">Something to click on to get focus
<div style="height:3000px;"></div>
<pre id="test">
<script class="testbody">
var SimpleTest = window.opener.SimpleTest;
var SpecialPowers = window.opener.SpecialPowers;
var is = window.opener.is;
function showFixedPosElements(show) {
var elements = document.getElementsByClassName("fp");
for (var i = 0; i < elements.length; ++i) {
elements[i].style.display = show ? '' : 'none';
}
}
var nextCont;
function didScroll() {
var c = nextCont;
nextCont = null;
if (c) {
c();
}
}
function scrollDownOnePageWithContinuation(cont) {
document.documentElement.scrollTop = 0;
nextCont = cont;
window.scrollByPages(1);
}
function test() {
var smoothScrollPref = "general.smoothScroll";
SpecialPowers.setBoolPref(smoothScrollPref, false);
showFixedPosElements(false);
scrollDownOnePageWithContinuation(function() {
var fullPageScrollDown = document.documentElement.scrollTop;
showFixedPosElements(true);
scrollDownOnePageWithContinuation(function() {
var fullPageScrollDownWithHeaderAndFooter = document.documentElement.scrollTop;
is(fullPageScrollDownWithHeaderAndFooter, fullPageScrollDown - (10 + 13),
"Reduce scroll distance by size of small header and footer");
document.getElementById("bottom").style.height = (window.innerHeight - 20) + "px";
scrollDownOnePageWithContinuation(function() {
is(document.documentElement.scrollTop, fullPageScrollDown - 10,
"Ignore really big elements when reducing scroll size");
document.getElementById("bottom").style.height = "13px";
document.getElementById("top").style.width = "100px";
scrollDownOnePageWithContinuation(function() {
is(document.documentElement.scrollTop, fullPageScrollDown - 13,
"Ignore elements that don't span the entire viewport side");
// Scroll back up so test results are visible
document.documentElement.scrollTop = 0;
SpecialPowers.clearUserPref(smoothScrollPref);
SimpleTest.finish();
window.close();
});
});
});
});
}
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Scrolling by pages with fixed-pos headers and footers</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<script class="testbody">
SimpleTest.waitForExplicitFinish();
window.open("./page_scroll_with_fixed_pos_window.html", "", "width=600,height=600");
</script>
</pre>
</body>
</html>