gecko/layout/base/tests/scrolling_helper.html

455 lines
18 KiB
HTML

<!DOCTYPE HTML>
<html>
<head>
<style>
.testcase {
height:200px;
width:200px;
position:absolute;
overflow:hidden;
left:0;
top:0;
}
iframe {
border:none;
}
</style>
<script src="region_lib.js"></script>
</head>
<body style="margin:0">
<!-- We use gradients here to fill elements with an unpredictable image. Since
our scrolling optimizations take account of areas of an element which
are blank or filled with a solid color, it's fragile to use those in
tests which expect to observe scrolling happening. -->
<!-- Each of the "testcase" elements is one test. We scroll that element's
scrollTop (or scrollLeft, if the element's class is 'horizontal')
from 0 to 20 and then call the function given by the element's id,
passing the blit region and the paint region as a parameters. -->
<div id="testSimpleScroll" class="testcase">
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
</div>
<div id="testFixedBackground" class="testcase">
<div style="height:300px; margin-bottom:-300px; background:-moz-linear-gradient(left, red, black) fixed;"></div>
<div style="height:300px; background:-moz-linear-gradient(top, rgba(0,0,0,0.7), rgba(255,0,0,0.7));"></div>
</div>
<div id="testFixedPosOverlay" class="testcase">
<div style="position:fixed; left:0; top:50px; width:100px; height:45px; background:yellow;"></div>
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
<p>Hello
</div>
<div style="border:1px solid black;" id="testBorder" class="testcase">
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
</div>
<div id="testScrollOutOfView" class="testcase">
<div style="height:10px; margin-bottom:210px; background:-moz-linear-gradient(top, rgba(0,0,0,0.7), rgba(255,0,0,0.7));"></div>
</div>
<div id="testScrollIntoView" class="testcase">
<div style="height:10px; margin-top:210px; background:-moz-linear-gradient(top, rgba(0,0,0,0.7), rgba(255,0,0,0.7));"></div>
</div>
<div id="testSimpleScrollWithSubpixelOffset1" style="top:0.2px" class="testcase">
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
</div>
<div id="testSimpleScrollWithSubpixelOffset2" style="top:0.8px" class="testcase">
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
</div>
<div id="testSimpleScrollWithSubpixelOffset3" style="left:0.2px" class="horizontal testcase">
<div style="width:300px; height:200px; background:-moz-linear-gradient(left, red, black);"></div>
</div>
<div id="testSimpleScrollWithSubpixelOffset4" style="left:0.8px" class="horizontal testcase">
<div style="width:300px; height:200px; background:-moz-linear-gradient(left, red, black);"></div>
</div>
<div id="testMovingClipArea" class="testcase">
<div style="margin-top:20px; height:20px; margin-bottom:300px; background-color:blue; overflow:hidden;"></div>
</div>
<div id="testNonmovingClipArea" class="testcase">
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
<div style="position:fixed; left:0; top:150px; width:200px; height:50px; background:yellow; overflow:hidden; opacity:0.5;">
<div style="height:50px; background:-moz-linear-gradient(top, green, yellow); opacity:0.3;"></div>
</div>
</div>
<div id="testFixedOverUniform" class="testcase">
<div style="height:300px; background:blue;"></div>
<div style="position:fixed; left:0; top:50px; width:100px; height:45px; background:yellow; opacity:0.5;"></div>
</div>
<iframe class="testcase" id="testClipIFRAME"
src="data:text/html,<body class='testcase' style='margin:0; height:300px; background:-moz-linear-gradient(top, red, black);'>">
</iframe>
<iframe class="testcase" id="testClipIFRAME2" style="top:-50px"
src="data:text/html,<body class='testcase' style='margin:0; height:300px; background:-moz-linear-gradient(top, red, black);'>">
</iframe>
<div id="testHiddenTable" class="testcase">
<table style="position:fixed; visibility:hidden; width:200px; height:200px; background:blue;">
<tr><td>Hidden stuff</td></tr>
</table>
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
</div>
<div id="testTableNoBackground" class="testcase">
<table style="position:fixed; width:200px; height:200px;">
<tr><td></td></tr>
</table>
<div style="height:300px; background:-moz-linear-gradient(top, red, black);"></div>
</div>
<iframe class="testcase" id="testNoBlitInSVG" height="200" width="200"
src="data:text/html,<body class='testcase' style='margin:0; height:300px; background:-moz-linear-gradient(top, red, black);'>">
</iframe>
<script>
// We're not in XHTML, so we have to make our SVG elements with script.
var SVG_NS = "http://www.w3.org/2000/svg";
var svg = document.createElementNS(SVG_NS, "svg");
svg.setAttribute("style", "width: 300px; height: 300px");
var g = document.createElementNS(SVG_NS, "g");
g.setAttribute("transform", "translate(100,0) rotate(30)");
var fo = document.createElementNS(SVG_NS, "foreignObject");
fo.setAttribute("x", "0");
fo.setAttribute("y", "0");
fo.setAttribute("width", "200");
fo.setAttribute("height", "200");
var iframe = document.getElementById("testNoBlitInSVG");
iframe.parentNode.replaceChild(svg, iframe);
fo.appendChild(iframe);
g.appendChild(fo);
svg.appendChild(g);
</script>
<iframe class="testcase" id="testNoBlitInTransform" height="200" width="200" style="-moz-transform-origin: 0 0; -moz-transform: translateX(100px) rotate(30deg)"
src="data:text/html,<body class='testcase' style='margin:0; height:300px; background:-moz-linear-gradient(top, red, black);'>">
</iframe>
<script>
var testcases = document.querySelectorAll("div.testcase");
var tests = [];
var iframes = document.querySelectorAll("iframe.testcase");
var currentTest = -1;
function ok(a, msg) {
window.opener.ok(a, tests[currentTest].container.id + ": " + msg);
};
function todo(a, msg) {
window.opener.todo(a, tests[currentTest].container.id + ": " + msg);
};
function finish() {
window.opener.SimpleTest.finish();
window.close();
}
window.onerror = function (event) { window.opener.onerror(event); window.close(); };
// Simple sanity check that scrolling a window with moving content in it does
// the obvious thing: blit the content that was already visible, and repaint
// the content that has scrolled into view.
function testSimpleScroll(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,180]])),
"Should blit everything that was already visible: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[0,180,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
}
// Check that scrolling visible content over background-attachment:fixed
// content repaints everything
function testFixedBackground(blitRegion, paintRegion) {
ok(blitRegion.isEmpty(),
"Shouldn't blit anything: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[0,0,200,200]])),
"Should repaint everything: " + paintRegion.toString());
}
// Check that we optimize scrolling in a reasonable way in the presence of a
// non-moving opaque overlay
function testFixedPosOverlay(blitRegion, paintRegion) {
// The area of the fixed element should not be repainted or blitted at all
var fixedElemRect = [0,50,100,95]
ok(!blitRegion.intersectsRect(fixedElemRect),
"blit region should not intersect fixed-pos element area: " + blitRegion.toString());
ok(!paintRegion.intersectsRect(fixedElemRect),
"paint region should not intersect fixed-pos element area: " + paintRegion.toString());
// The area to the right of the fixed element that we can fill with blitting
// existing content should not be repainted (but may be blitted, although
// it doesn't all need to be blitted either, since most of it is blank)
var noPaintRect = [100,0,200,180];
ok(!paintRegion.intersectsRect(noPaintRect),
"paint region should not intersect blittable area: " + paintRegion.toString());
}
// Check that scrolling an element with moving content in it does
// the obvious thing, even if the element has a border.
function testBorder(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[1,1,201,181]])),
"Should blit everything that was already visible: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[1,181,201,201]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
}
// Check that scrolling some content out of view updates the area that
// was displaying the content
function testScrollOutOfView(blitRegion, paintRegion) {
var all = blitRegion.unionRegion(paintRegion);
ok(all.containsRect([0,0,200,10]),
"Should update everything that was previously visible but scrolled out of view: " + all.toString());
}
// Check that scrolling some content into view updates the area that
// is displaying the content
function testScrollIntoView(blitRegion, paintRegion) {
var all = blitRegion.unionRegion(paintRegion);
ok(all.containsRect([0,190,200,200]),
"Should update everything that was previously visible: " + all.toString());
}
// When we scroll an area which has a very small subpixel offset, we should
// still be doing the obvious thing --- we shouldn't end up painting an extra
// row of pixels anywhere
function testSimpleScrollWithSubpixelOffset1(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,180]])),
"Should blit everything that was already visible: " + blitRegion.toString());
// The next test is contains, not equals, since we repaint down to 200.2
// which is OK
ok(paintRegion.containsRegion(new Region([[0,180,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
// Check that we're not repainting anything in the area that's blitted
ok(!paintRegion.intersectsRect([0,0,200,180]),
"Should not repaint area that was blitted: " + paintRegion.toString());
}
function testSimpleScrollWithSubpixelOffset2(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,1,200,181]])),
"Should blit everything that was already visible: " + blitRegion.toString());
// The next test is contains, not equals, since we repaint down to 200.8
// which is OK
ok(paintRegion.containsRegion(new Region([[0,181,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
// Check that we're not repainting anything in the area that's blitted
ok(!paintRegion.intersectsRect([0,0,200,180]),
"Should not repaint area that was blitted: " + paintRegion.toString());
}
function testSimpleScrollWithSubpixelOffset3(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,180,200]])),
"Should blit everything that was already visible: " + blitRegion.toString());
// The next test is contains, not equals, since we repaint across to 200.2
// which is OK
ok(paintRegion.containsRegion(new Region([[180,0,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
// Check that we're not repainting anything in the area that's blitted
ok(!paintRegion.intersectsRect([0,0,180,200]),
"Should not repaint area that was blitted: " + paintRegion.toString());
}
function testSimpleScrollWithSubpixelOffset4(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[1,0,181,200]])),
"Should blit everything that was already visible: " + blitRegion.toString());
// The next test is contains, not equals, since we repaint down to 200.8
// which is OK
ok(paintRegion.containsRegion(new Region([[181,0,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
// Check that we're not repainting anything in the area that's blitted
ok(!paintRegion.intersectsRect([0,0,180,200]),
"Should not repaint area that was blitted: " + paintRegion.toString());
}
function testMovingClipArea(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,40]])),
"Should blit everything that's affected: " + blitRegion.toString());
ok(paintRegion.isEmpty(),
"Shouldn't repaint anything: " + paintRegion.toString());
}
function testNonmovingClipArea(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,130]])),
"Should only blit necessary strip: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[0,130,200,200]])),
"Should only repaint necessary strip: " + paintRegion.toString());
}
function testFixedOverUniform(blitRegion, paintRegion) {
ok(!blitRegion.intersectsRect([0,20,200,200]),
"Shouldn't blit anything except possibly to cover the area that has moved offscreen: " + blitRegion.toString());
ok(paintRegion.isEmpty(),
"Shouldn't repaint anything: " + paintRegion.toString());
}
function testClipIFRAME(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,180]])),
"Should blit everything that was already visible: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[0,180,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
}
function testClipIFRAME2(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,50,200,180]])),
"Should blit everything that was already visible: " + blitRegion.toString());
// ok(paintRegion.equalsRegion(new Region([[0,180,200,200]])),
// "Should repaint area that was scrolled into view: " + paintRegion.toString());
}
function testHiddenTable(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,180]])),
"Should blit everything that was already visible: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[0,180,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
}
function testTableNoBackground(blitRegion, paintRegion) {
ok(blitRegion.equalsRegion(new Region([[0,0,200,180]])),
"Should blit everything that was already visible: " + blitRegion.toString());
ok(paintRegion.equalsRegion(new Region([[0,180,200,200]])),
"Should repaint area that was scrolled into view: " + paintRegion.toString());
}
function testNoBlitInSVG(blitRegion, paintRegion) {
ok(blitRegion.isEmpty(), "should not blit when in transformed SVG");
// We're looking at regions in the coordinates of the inner iframe.
// (Not the most useful test, but it does test the particular bug that we
// should be repainting rather than blitting.)
ok(paintRegion.equalsRegion(new Region([[0,0,200,200]])),
"repaint rect must contain area completely inside scrolled region");
}
function testNoBlitInTransform(blitRegion, paintRegion) {
ok(blitRegion.isEmpty(), "should not blit when in CSS Transform");
// We're looking at regions in the coordinates of the inner iframe.
// (Not the most useful test, but it does test the particular bug that we
// should be repainting rather than blitting.)
ok(paintRegion.equalsRegion(new Region([[0,0,200,200]])),
"repaint rect must contain area completely inside scrolled region");
}
function clientRectToRect(cr)
{
return [cr.left, cr.top, cr.right, cr.bottom];
}
// Return the ancestor-or-self of |container| that is a child of body.
function bodyChild(container)
{
var prev;
var next = container;
do {
prev = next;
next = prev.parentNode;
} while (next != document.body);
return prev;
}
function regionForReason(requests, reason)
{
var rects = [];
for (var i = 0; i < requests.length; ++i) {
var r = requests[i];
if (r.reason == reason) {
rects.push(clientRectToRect(r.clientRect));
}
}
return new Region(rects);
}
function afterPaint(event) {
var requests = event.paintRequests;
if (!requests) {
todo(false, "cannot run tests since paintRequests not supported");
finish();
return;
}
var blitRegion = regionForReason(requests, "scroll copy");
var paintRegion = regionForReason(requests, "scroll repaint");
if (blitRegion.isEmpty() && paintRegion.isEmpty())
return;
var testFunc = window[tests[currentTest].container.id];
if (!testFunc) {
ok(false, "Cannot find test function for " + tests[currentTest].container.id);
} else {
testFunc(blitRegion, paintRegion);
}
bodyChild(tests[currentTest].container).style.display = 'none';
nextTest();
}
function nextTest() {
++currentTest;
if (currentTest >= tests.length) {
finish();
return;
}
var t = tests[currentTest];
bodyChild(t.container).style.display = "";
setTimeout(function() {
if (t.scrollable.getAttribute("class").match(/horizontal/)) {
t.scrollable.scrollLeft = 20;
} else {
t.scrollable.scrollTop = 20;
}
}, 0);
}
function runTests() {
for (var i = 0; i < testcases.length; ++i) {
tests.push({ scrollable:testcases[i], container:testcases[i] });
}
for (var i = 0; i < iframes.length; ++i) {
tests.push({ scrollable:iframes[i].contentDocument.body, container:iframes[i] });
}
for (var i = 0; i < tests.length; ++i) {
var t = tests[i];
bodyChild(t.container).style.display = "none";
// Make sure we don't remember a scroll position from history
t.scrollable.scrollTop = 0;
t.scrollable.scrollLeft = 0;
}
window.addEventListener("MozAfterPaint", afterPaint, false);
for (var i = 0; i < iframes.length; ++i) {
iframes[i].contentWindow.addEventListener("MozAfterPaint", afterPaint, false);
}
nextTest();
}
window.onload = function() { setTimeout(runTests, 0); };
</script>
</body>
</html>