Bug 830479. Account for multiple fixed elements acting as headers and footers when doing full page scrolling. r=roc

This commit is contained in:
Timothy Nikkel 2013-02-13 22:36:02 -06:00
parent f42b71d1b6
commit db9d763d93
2 changed files with 78 additions and 22 deletions

View File

@ -2350,34 +2350,73 @@ nsGfxScrollFrameInner::GetLineScrollAmount() const
* 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.
* viewport. We also want to consider fixed elements that stack or overlap
* to effectively create a larger header or footer. Headers and footers that
* cover more than a third 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.
*/
struct TopAndBottom
{
TopAndBottom(nscoord aTop, nscoord aBottom) : top(aTop), bottom(aBottom) {}
nscoord top, bottom;
};
struct TopComparator
{
bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
return A.top == B.top;
}
bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
return A.top < B.top;
}
};
struct ReverseBottomComparator
{
bool Equals(const TopAndBottom& A, const TopAndBottom& B) const {
return A.bottom == B.bottom;
}
bool LessThan(const TopAndBottom& A, const TopAndBottom& B) const {
return A.bottom > B.bottom;
}
};
static nsSize
GetScrollPortSizeExcludingHeadersAndFooters(nsIFrame* aViewportFrame,
const nsRect& aScrollPort)
{
nsTArray<TopAndBottom> list;
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 = std::max(headerBottom, r.height);
}
if (r.YMost() == aScrollPort.height) {
footerTop = std::min(footerTop, r.y);
}
list.AppendElement(TopAndBottom(r.y, r.YMost()));
}
}
list.Sort(TopComparator());
nscoord headerBottom = 0;
for (int32_t i = 0; i < list.Length(); ++i) {
if (list[i].top <= headerBottom) {
headerBottom = std::max(headerBottom, list[i].bottom);
}
}
list.Sort(ReverseBottomComparator());
nscoord footerTop = aScrollPort.height;
for (int32_t i = 0; i < list.Length(); ++i) {
if (list[i].bottom >= footerTop) {
footerTop = std::min(footerTop, list[i].top);
}
}
headerBottom = std::min(aScrollPort.height/3, headerBottom);
footerTop = std::max(aScrollPort.height - aScrollPort.height/3, footerTop);
return nsSize(aScrollPort.width, footerTop - headerBottom);
}

View File

@ -4,11 +4,14 @@
<title>Scrolling by pages with fixed-pos headers and footers</title>
<style>
.fp { position:fixed; left:0; width:100%; }
.fp2 { 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="fp2" id="top2" style="top:10px; height:11px; background:blue;"></div>
<div class="fp" style="top:50%; height:7px; background:cyan;"></div>
<div class="fp2" id="bottom2" style="bottom:9px; height:12px; background:red;"></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>
@ -18,12 +21,18 @@ var SimpleTest = window.opener.SimpleTest;
var SpecialPowers = window.opener.SpecialPowers;
var is = window.opener.is;
function showFixedPosElements(show) {
var elements = document.getElementsByClassName("fp");
function showElements(show, classname) {
var elements = document.getElementsByClassName(classname);
for (var i = 0; i < elements.length; ++i) {
elements[i].style.display = show ? '' : 'none';
}
}
function showFixedPosElements(show) {
showElements(show, "fp");
}
function showFixedPosElements2(show) {
showElements(show, "fp2");
}
var nextCont;
function didScroll() {
@ -45,6 +54,7 @@ function test() {
SpecialPowers.setBoolPref(smoothScrollPref, false);
showFixedPosElements(false);
showFixedPosElements2(false);
scrollDownOnePageWithContinuation(function() {
var fullPageScrollDown = document.documentElement.scrollTop;
@ -64,12 +74,19 @@ function test() {
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();
document.getElementById("top").style.width = "100%";
showFixedPosElements2(true);
scrollDownOnePageWithContinuation(function() {
is(document.documentElement.scrollTop, fullPageScrollDown - (10 + 11 + 9 + 12),
"Combine multiple overlapping elements");
// Scroll back up so test results are visible
document.documentElement.scrollTop = 0;
SpecialPowers.clearUserPref(smoothScrollPref);
SimpleTest.finish();
window.close();
});
});
});
});