Bug 509693. Don't use SW_SCROLLCHILDREN if some child has a descendant window that's outside the scrolled rectangle. r=jmathies

--HG--
extra : rebase_source : d6762b4e570d90e7a2561597ddc071383a923092
This commit is contained in:
Robert O'Callahan 2009-09-24 21:37:01 +12:00
parent 69525d5240
commit 56caf25e1b
3 changed files with 110 additions and 0 deletions

View File

@ -2105,6 +2105,37 @@ ClipRegionContainedInRect(const nsTArray<nsIntRect>& aClipRects,
return PR_TRUE;
}
// This function determines whether the given window has a descendant that
// does not intersect the given aScreenRect. If we encounter a window owned
// by another thread (which includes another process, since thread IDs
// are unique system-wide), then we give up and conservatively return true.
static PRBool
HasDescendantWindowOutsideRect(DWORD aThisThreadID, HWND aWnd,
const RECT& aScreenRect)
{
// If the window is owned by another thread, give up now, don't try to
// look at its children since they could change asynchronously.
// XXX should we try harder here for out-of-process plugins?
if (GetWindowThreadProcessId(aWnd, NULL) != aThisThreadID) {
return PR_TRUE;
}
for (HWND child = ::GetWindow(aWnd, GW_CHILD); child;
child = ::GetWindow(child, GW_HWNDNEXT)) {
RECT childScreenRect;
::GetWindowRect(child, &childScreenRect);
RECT result;
if (!::IntersectRect(&result, &childScreenRect, &aScreenRect)) {
return PR_TRUE;
}
if (HasDescendantWindowOutsideRect(aThisThreadID, child, aScreenRect)) {
return PR_TRUE;
}
}
return PR_FALSE;
}
void
nsWindow::Scroll(const nsIntPoint& aDelta,
const nsTArray<nsIntRect>& aDestRects,
@ -2129,6 +2160,8 @@ nsWindow::Scroll(const nsIntPoint& aDelta,
w->SetWindowClipRegion(configuration.mClipRegion, PR_TRUE);
}
DWORD ourThreadID = GetWindowThreadProcessId(mWnd, NULL);
for (PRUint32 i = 0; i < aDestRects.Length(); ++i) {
nsIntRect affectedRect;
affectedRect.UnionRect(aDestRects[i], aDestRects[i] - aDelta);
@ -2148,6 +2181,24 @@ nsWindow::Scroll(const nsIntPoint& aDelta,
// used on it again by a later rectangle in aDestRects, we
// don't want it to move twice!
scrolledWidgets.RawRemoveEntry(entry);
nsIntPoint screenOffset = WidgetToScreenOffset();
RECT screenAffectedRect = {
screenOffset.x + affectedRect.x,
screenOffset.y + affectedRect.y,
screenOffset.x + affectedRect.XMost(),
screenOffset.y + affectedRect.YMost()
};
if (HasDescendantWindowOutsideRect(ourThreadID, w->mWnd,
screenAffectedRect)) {
// SW_SCROLLCHILDREN seems to not move descendant windows
// that don't intersect the scrolled rectangle, *even if* the
// immediate child window of the scrolled window *does* intersect
// the scrolled window. So if w has a descendant window
// that would not be moved, SW_SCROLLCHILDREN will hopelessly mess
// things up and we must not use it.
flags &= ~SW_SCROLLCHILDREN;
}
} else {
flags &= ~SW_SCROLLCHILDREN;
// We may have removed some children from scrolledWidgets even

View File

@ -61,6 +61,7 @@ _TEST_FILES = test_bug343416.xul \
test_wheeltransaction.xul \
window_wheeltransaction.xul \
test_imestate.html \
test_plugin_scroll_consistency.html \
$(NULL)
ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)

View File

@ -0,0 +1,58 @@
<html>
<head>
<title>Test for plugin child widgets not being messed up by scrolling</title>
<script type="text/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body onload="setTimeout(runTests, 0)">
<p id="display">
<div style="overflow:hidden; height:100px;" id="scroll">
<embed type="application/x-test" wmode="window" width="100" height="800" id="plugin"></object>
<div style="height:1000px;"></div>
</div>
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
var plugin = document.getElementById("plugin");
function consistencyCheck(state) {
var s = plugin.doInternalConsistencyCheck();
ok(s == "", "Consistency check: " + state + ": " + s);
}
consistencyCheck("Initial state");
function runTests()
{
var scroll = document.getElementById("scroll");
scroll.scrollTop = 10;
consistencyCheck("Scrolled down a bit");
setTimeout(function() {
consistencyCheck("Before scrolling back to top");
scroll.scrollTop = 0;
consistencyCheck("Scrolled to top");
setTimeout(function() {
consistencyCheck("After scrolling to top");
SimpleTest.finish();
}, 0);
}, 0);
}
</script>
</body>
</html>