Bug 478536 Crash by removing a scroll target in MozMouseScrollFailed event handler r+sr=roc

This commit is contained in:
Masayuki Nakano 2009-02-18 00:55:53 +09:00
parent 7ae9acbaf5
commit bcf39be0ea
4 changed files with 254 additions and 0 deletions

View File

@ -377,6 +377,8 @@ public:
static void BeginTransaction(nsIFrame* aTargetFrame, static void BeginTransaction(nsIFrame* aTargetFrame,
PRInt32 aNumLines, PRInt32 aNumLines,
PRBool aScrollHorizontal); PRBool aScrollHorizontal);
// Be careful, UpdateTransaction may fire a DOM event, therefore, the target
// frame might be destroyed in the event handler.
static PRBool UpdateTransaction(PRInt32 aNumLines, static PRBool UpdateTransaction(PRInt32 aNumLines,
PRBool aScrollHorizontal); PRBool aScrollHorizontal);
static void EndTransaction(); static void EndTransaction();
@ -543,6 +545,10 @@ nsMouseWheelTransaction::OnFailToScrollTarget()
sTargetFrame->GetContent(), sTargetFrame->GetContent(),
NS_LITERAL_STRING("MozMouseScrollFailed"), NS_LITERAL_STRING("MozMouseScrollFailed"),
PR_TRUE, PR_TRUE); PR_TRUE, PR_TRUE);
// The target frame might be destroyed in the event handler, at that time,
// we need to finish the current transaction
if (!sTargetFrame)
EndTransaction();
} }
void void
@ -2734,6 +2740,12 @@ nsEventStateManager::DoScrollText(nsPresContext* aPresContext,
nsIScrollableViewProvider* svp = do_QueryFrame(lastScrollFrame); nsIScrollableViewProvider* svp = do_QueryFrame(lastScrollFrame);
if (svp && (scrollView = svp->GetScrollableView())) { if (svp && (scrollView = svp->GetScrollableView())) {
nsMouseWheelTransaction::UpdateTransaction(aNumLines, aScrollHorizontal); nsMouseWheelTransaction::UpdateTransaction(aNumLines, aScrollHorizontal);
// When the scroll event will not scroll any views, UpdateTransaction
// fired MozMouseScrollFailed event which is for automated testing.
// In the event handler, the target frame might be destroyed. Then,
// we should not keep handling this scroll event.
if (!nsMouseWheelTransaction::GetTargetFrame())
return NS_OK;
} else { } else {
nsMouseWheelTransaction::EndTransaction(); nsMouseWheelTransaction::EndTransaction();
lastScrollFrame = nsnull; lastScrollFrame = nsnull;

View File

@ -60,6 +60,8 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug343416.xul \ _TEST_FILES = test_bug343416.xul \
test_bug444800.xul \ test_bug444800.xul \
test_bug462106.xul \ test_bug462106.xul \
test_bug478536.xul \
window_bug478536.xul \
test_keycodes.xul \ test_keycodes.xul \
test_wheeltransaction.xul \ test_wheeltransaction.xul \
window_wheeltransaction.xul \ window_wheeltransaction.xul \

View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=478536
-->
<window title="Mozilla Bug 478536"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<title>Test for Bug 478536</title>
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<body xmlns="http://www.w3.org/1999/xhtml">
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
<script class="testbody" type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
window.open("window_bug478536.xul", "_blank",
"chrome,width=600,height=600");
]]>
</script>
</window>

View File

@ -0,0 +1,204 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window title="Mozilla Bug 478536"
width="600" height="600"
onload="onload();"
onunload="onunload();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js" />
<body xmlns="http://www.w3.org/1999/xhtml" id="body">
<style type="text/css">
#view {
overflow: auto;
width: 100px;
height: 100px;
border: 1px solid;
margin: 0;
}
</style>
<pre id="view" onscroll="onScrollView(event);">
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
Text. Text. Text. Text. Text. Text. Text. Text. Text. Text. Text.
</pre>
</body>
<script class="testbody" type="application/javascript">
<![CDATA[
function ok(aCondition, aMessage)
{
window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
}
function is(aLeft, aRight, aMessage)
{
window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
}
function isnot(aLeft, aRight, aMessage)
{
window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
}
var gBody = document.getElementById("body");
var gView = document.getElementById("view");
/**
* Description:
*
* First, lock the wheel scrolling target to "view" at first step.
* Next, scroll back to top most of the "view" at second step.
* Finally, scroll back again at third step. This fails to scroll the "view",
* then, |onMouseScrollFailed| event should be fired. And at that time, we
* can remove the "view". So, in post processing of the event firere, the
* "view" should not be referred.
*
* For suppressing random test failure, all tests will be retried if we handle
* unexpected timeout event.
*/
var gTests = [
{ scrollToForward: true, shouldScroll: true },
{ scrollToForward: false, shouldScroll: true },
{ scrollToForward: false, shouldScroll: false }
];
var gCurrentTestIndex = -1;
var gIgnoreScrollEvent = true;
var gPrefSvc = Components.classes["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch2);
const kPrefNameTimeout = "mousewheel.transaction.timeout";
const kDefaultTimeout = gPrefSvc.getIntPref(kPrefNameTimeout);
var gTimeout = kDefaultTimeout;
gBody.addEventListener("MozMouseScrollFailed", onMouseScrollFailed, false);
gBody.addEventListener("MozMouseScrollTransactionTimeout",
onTransactionTimeout, false);
function setTimeoutPrefs(aTimeout)
{
gPrefSvc.setIntPref(kPrefNameTimeout, aTimeout);
gTimeout = aTimeout;
}
function resetTimeoutPrefs()
{
if (gTimeout == kDefaultTimeout)
return;
setTimeoutPrefs(kDefaultTimeout);
}
function growUpTimeoutPrefs()
{
if (gTimeout != kDefaultTimeout)
return;
setTimeoutPrefs(5000);
}
function onload()
{
disableNonTestMouseEvents(true);
setTimeout(runNextTest, 0);
}
function onunload()
{
resetTimeoutPrefs();
disableNonTestMouseEvents(false);
window.opener.wrappedJSObject.SimpleTest.finish();
}
function finish()
{
window.close();
}
// testing code
var gTimer;
function clearTimer()
{
clearTimeout(gTimer);
gTimer = 0;
}
function runNextTest()
{
clearTimer();
if (++gCurrentTestIndex >= gTests.length) {
ok(true, "didn't crash, succeeded");
finish();
return;
}
fireWheelScrollEvent(gTests[gCurrentTestIndex].scrollToForward);
}
var gRetryCount = 5;
function retryAllTests()
{
clearTimer();
if (--gRetryCount >= 0) {
gView.scrollTop = 0;
gView.scrollLeft = 0;
gCurrentTestIndex = -1;
growUpTimeoutPrefs();
ok(true, "WARNING: retry current test-list...");
gTimer = setTimeout(runNextTest, 0);
} else {
ok(false, "Failed by unexpected timeout");
finish();
}
}
function fireWheelScrollEvent(aForward)
{
gIgnoreScrollEvent = false;
var event = { axis: "vertical", delta: aForward ? 4 : -4,
type: "DOMMouseScroll" };
synthesizeMouseScroll(gView, 5, 5, event, window);
}
function onScrollView(aEvent)
{
if (gIgnoreScrollEvent)
return;
gIgnoreScrollEvent = true;
clearTimer();
ok(gTests[gCurrentTestIndex].shouldScroll, "The view is scrolled");
gTimer = setTimeout(runNextTest, 0);
}
function onMouseScrollFailed(aEvent)
{
clearTimer();
gIgnoreScrollEvent = true;
ok(!gTests[gCurrentTestIndex].shouldScroll, "The view is not scrolled");
if (!gTests[gCurrentTestIndex].shouldScroll)
gBody.removeChild(gView);
runNextTest();
}
function onTransactionTimeout(aEvent)
{
if (!gTimer)
return;
gIgnoreScrollEvent = true;
retryAllTests();
}
]]>
</script>
</window>