Bug 702739. When a element has an active transform and the element's content (before being transformed) is no larger than the window, prerender its entire contents into layers when any of it is visible, so we don't have to rerender it and/or resize its layers as it moves into or out of view. r=mats

This commit is contained in:
Robert O'Callahan 2011-12-18 21:46:44 +13:00
Родитель 092577b79e
Коммит 7a9dc0adaf
7 изменённых файлов: 138 добавлений и 62 удалений

Просмотреть файл

@ -2567,6 +2567,15 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
(newOrigin + toMozOrigin, result);
}
bool
nsDisplayTransform::ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame)
{
return aFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
aFrame->GetVisualOverflowRectRelativeToSelf().Size() <=
aBuilder->ReferenceFrame()->GetSize();
}
/* If the matrix is singular, or a hidden backface is shown, the frame won't be visible or hit. */
static bool IsFrameVisible(nsIFrame* aFrame, const gfx3DMatrix& aMatrix)
{
@ -2644,7 +2653,8 @@ bool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
* think that it's painting in its original rectangular coordinate space.
* If we can't untransform, take the entire overflow rect */
nsRect untransformedVisibleRect;
if (!UntransformRect(mVisibleRect,
if (ShouldPrerenderTransformedContent(aBuilder, mFrame) ||
!UntransformRect(mVisibleRect,
mFrame,
aBuilder->ToReferenceFrame(mFrame),
&untransformedVisibleRect))

Просмотреть файл

@ -2210,6 +2210,12 @@ public:
float aFactor,
const nsRect* aBoundsOverride = nsnull,
nsIFrame** aOutAncestor = nsnull);
/**
* Return true when we should try to prerender the entire contents of the
* transformed frame even when it's not completely visible (yet).
*/
static bool ShouldPrerenderTransformedContent(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame);
private:
nsDisplayWrapList mStoredList;

Просмотреть файл

@ -44,6 +44,7 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_CHROME_FILES = \
paint_listener.js \
test_bug370436.html \
test_bug396367-1.html \
test_bug396367-2.html \
@ -62,10 +63,11 @@ _CHROME_FILES = \
chrome_over_plugin_window.xul \
test_default_background.xul \
default_background_window.xul \
test_leaf_layers_partition_browser_window.xul \
test_no_clip_iframe.xul \
no_clip_iframe_window.xul \
no_clip_iframe_subdoc.html \
test_leaf_layers_partition_browser_window.xul \
test_no_clip_iframe.xul \
no_clip_iframe_window.xul \
no_clip_iframe_subdoc.html \
test_prerendered_transforms.html \
test_printpreview.xul \
printpreview_helper.xul \
test_printpreview_bug396024.xul \

Просмотреть файл

@ -8,6 +8,7 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
<script type="text/javascript" src="paint_listener.js"></script>
<div id="container" xmlns="http://www.w3.org/1999/xhtml" style="height:400px; overflow:auto; background:gray">
<div style="height:0">
@ -33,50 +34,6 @@
SimpleTest.waitForExplicitFinish();
var accumulatedRect = null;
var onpaint = function() {};
function paintListener(event) {
if (event.target != window)
return;
dump("got MozAfterPaint: " + event.boundingClientRect.left + "," + event.boundingClientRect.top + "," +
event.boundingClientRect.right + "," + event.boundingClientRect.bottom + "\n");
if (accumulatedRect) {
accumulatedRect[0] = Math.min(accumulatedRect[0], event.boundingClientRect.left);
accumulatedRect[1] = Math.min(accumulatedRect[1], event.boundingClientRect.top);
accumulatedRect[2] = Math.max(accumulatedRect[2], event.boundingClientRect.right);
accumulatedRect[3] = Math.max(accumulatedRect[3], event.boundingClientRect.bottom);
} else {
accumulatedRect = [event.boundingClientRect.left, event.boundingClientRect.top,
event.boundingClientRect.right, event.boundingClientRect.bottom];
}
onpaint();
}
window.addEventListener("MozAfterPaint", paintListener, false);
function waitForAllPaintsFlushed(callback, subdoc) {
document.documentElement.getBoundingClientRect();
if (subdoc) {
subdoc.documentElement.getBoundingClientRect();
}
var CI = Components.interfaces;
var utils = window.QueryInterface(CI.nsIInterfaceRequestor)
.getInterface(CI.nsIDOMWindowUtils);
if (!utils.isMozAfterPaintPending) {
dump("done...\n");
var result = accumulatedRect;
accumulatedRect = null;
onpaint = function() {};
if (!result) {
result = [0,0,0,0];
}
callback(result[0], result[1], result[2], result[3]);
return;
}
dump("waiting for paint...\n");
onpaint = function() { waitForAllPaintsFlushed(callback); };
}
var Ci = Components.interfaces;
var frame = document.getElementById("f");
var fl = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
@ -118,7 +75,6 @@
ref.style.visibility = "hidden";
document.getElementById("container").style.height = "400px";
waitForAllPaintsFlushed(function() {
dump("Scrolling\n");
frame.contentWindow.scrollTo(0,80);
waitForAllPaintsFlushed(function(x1, y1, x2, y2) {
ok(x1 <= 1 && x2 >= 151 && y1 <= 0 && y2 >= 400,

Просмотреть файл

@ -0,0 +1,50 @@
var accumulatedRect = null;
var onpaint = function() {};
var debug = false;
function paintListener(event) {
if (event.target != window)
return;
if (debug) {
dump("got MozAfterPaint: " + event.boundingClientRect.left + "," + event.boundingClientRect.top + "," +
event.boundingClientRect.right + "," + event.boundingClientRect.bottom + "\n");
}
if (accumulatedRect) {
accumulatedRect[0] = Math.min(accumulatedRect[0], event.boundingClientRect.left);
accumulatedRect[1] = Math.min(accumulatedRect[1], event.boundingClientRect.top);
accumulatedRect[2] = Math.max(accumulatedRect[2], event.boundingClientRect.right);
accumulatedRect[3] = Math.max(accumulatedRect[3], event.boundingClientRect.bottom);
} else {
accumulatedRect = [event.boundingClientRect.left, event.boundingClientRect.top,
event.boundingClientRect.right, event.boundingClientRect.bottom];
}
onpaint();
}
window.addEventListener("MozAfterPaint", paintListener, false);
function waitForAllPaintsFlushed(callback, subdoc) {
document.documentElement.getBoundingClientRect();
if (subdoc) {
subdoc.documentElement.getBoundingClientRect();
}
var CI = Components.interfaces;
var utils = window.QueryInterface(CI.nsIInterfaceRequestor)
.getInterface(CI.nsIDOMWindowUtils);
if (!utils.isMozAfterPaintPending) {
if (debug) {
dump("done...\n");
}
var result = accumulatedRect;
accumulatedRect = null;
onpaint = function() {};
if (!result) {
result = [0,0,0,0];
}
callback(result[0], result[1], result[2], result[3]);
return;
}
if (debug) {
dump("waiting for paint...\n");
}
onpaint = function() { waitForAllPaintsFlushed(callback, subdoc); };
}

Просмотреть файл

@ -0,0 +1,47 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test that active transformed elements coming into view are prerendered so we don't have to redraw constantly</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="paint_listener.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body onload="startTest()">
<div>
<div id="t" style="position:absolute; left:0; top:500px; -moz-transform: translatex(-100px); width:200px; height:100px; background:yellow;">
<div style="text-align:right">Hello</div>
<div style="text-align:left">Kitty</div>
</div>
</div>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
var t = document.getElementById("t");
var utils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
getInterface(Components.interfaces.nsIDOMWindowUtils);
function startTest() {
// Do a couple of transform changes to ensure we've triggered activity heuristics
waitForAllPaintsFlushed(function () {
t.style.MozTransform = "translatex(-75px)";
waitForAllPaintsFlushed(function () {
t.style.MozTransform = "translatex(-50px)";
waitForAllPaintsFlushed(function () {
// Clear paint state now and move again.
utils.checkAndClearPaintedState(t);
// Don't move to 0 since that might trigger some special case that turns off transforms.
t.style.MozTransform = "translatex(-1px)";
waitForAllPaintsFlushed(function () {
var painted = utils.checkAndClearPaintedState(t);
is(painted, false, "Transformed element should not have been painted");
SimpleTest.finish();
});
});
});
});
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -1680,19 +1680,24 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
bool inTransform = aBuilder->IsInTransform();
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
disp->HasTransform()) {
// Transform dirtyRect into our frame's local coordinate space. Note that
// the new value is the bounds of the old value's transformed vertices, so
// the area covered by dirtyRect may increase here.
//
// Although we don't bother to check for and maintain the 1x1 size of the
// magic rect indicating a hit test point, in reality this is extremely
// unlikely to matter. The rect starts off with dimensions of 1x1 *app*
// units, and it would require a very large number of elements with
// transforms along a parent chain to noticably expand this by an entire
// device pixel.
if (Preserves3DChildren() || !nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0), &dirtyRect)) {
// we have a singular transform - just grab the entire overflow rect
if (nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this) ||
Preserves3DChildren()) {
dirtyRect = GetVisualOverflowRectRelativeToSelf();
} else {
// Transform dirtyRect into our frame's local coordinate space. Note that
// the new value is the bounds of the old value's transformed vertices, so
// the area covered by dirtyRect may increase here.
//
// Although we don't bother to check for and maintain the 1x1 size of the
// magic rect indicating a hit test point, in reality this is extremely
// unlikely to matter. The rect starts off with dimensions of 1x1 *app*
// units, and it would require a very large number of elements with
// transforms along a parent chain to noticably expand this by an entire
// device pixel.
if (!nsDisplayTransform::UntransformRect(dirtyRect, this, nsPoint(0, 0), &dirtyRect)) {
// we have a singular transform - just grab the entire overflow rect
dirtyRect = GetVisualOverflowRectRelativeToSelf();
}
}
inTransform = true;
}