Bug 748464 - refactor nsGlobalWindow::RunTimeout; r=bz

This commit is contained in:
Nathan Froyd 2012-04-24 14:16:29 -04:00
Родитель d22fa8806c
Коммит 8578a20583
2 изменённых файлов: 180 добавлений и 175 удалений

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

@ -8963,7 +8963,175 @@ nsGlobalWindow::SetTimeoutOrInterval(bool aIsInterval, PRInt32 *aReturn)
return SetTimeoutOrInterval(handler, interval, isInterval, aReturn);
}
// static
bool
nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
nsIScriptContext* aScx)
{
// Hold on to the timeout in case mExpr or mFunObj releases its
// doc.
nsRefPtr<nsTimeout> timeout = aTimeout;
nsTimeout* last_running_timeout = mRunningTimeout;
mRunningTimeout = timeout;
timeout->mRunning = true;
// Push this timeout's popup control state, which should only be
// eabled the first time a timeout fires that was created while
// popups were enabled and with a delay less than
// "dom.disable_open_click_delay".
nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState);
// Clear the timeout's popup state, if any, to prevent interval
// timeouts from repeatedly opening poups.
timeout->mPopupState = openAbused;
++gRunningTimeoutDepth;
++mTimeoutFiringDepth;
bool trackNestingLevel = !timeout->mIsInterval;
PRUint32 nestingLevel;
if (trackNestingLevel) {
nestingLevel = sNestingLevel;
sNestingLevel = timeout->mNestingLevel;
}
nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
JSObject* scriptObject = handler->GetScriptObject();
if (!scriptObject) {
// Evaluate the timeout expression.
const PRUnichar* script = handler->GetHandlerText();
NS_ASSERTION(script, "timeout has no script nor handler text!");
const char* filename = nsnull;
PRUint32 lineNo = 0;
handler->GetLocation(&filename, &lineNo);
NS_TIME_FUNCTION_MARK("(file: %s, line: %d)", filename, lineNo);
bool is_undefined;
aScx->EvaluateString(nsDependentString(script), FastGetGlobalJSObject(),
timeout->mPrincipal, timeout->mPrincipal,
filename, lineNo, JSVERSION_DEFAULT, nsnull,
&is_undefined);
} else {
nsCOMPtr<nsIVariant> dummy;
nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
aScx->CallEventHandler(me, FastGetGlobalJSObject(),
scriptObject, handler->GetArgv(),
// XXXmarkh - consider allowing CallEventHandler to
// accept nsnull?
getter_AddRefs(dummy));
}
// We ignore any failures from calling EvaluateString() or
// CallEventHandler() on the context here since we're in a loop
// where we're likely to be running timeouts whose OS timers
// didn't fire in time and we don't want to not fire those timers
// now just because execution of one timer failed. We can't
// propagate the error to anyone who cares about it from this
// point anyway, and the script context should have already reported
// the script error in the usual way - so we just drop it.
if (trackNestingLevel) {
sNestingLevel = nestingLevel;
}
--mTimeoutFiringDepth;
--gRunningTimeoutDepth;
mRunningTimeout = last_running_timeout;
timeout->mRunning = false;
return timeout->mCleared;
}
bool
nsGlobalWindow::RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
bool aRunningPendingTimeouts)
{
if (!aTimeout->mIsInterval) {
if (aTimeout->mTimer) {
// The timeout still has an OS timer, and it's not an interval,
// that means that the OS timer could still fire; cancel the OS
// timer and release its reference to the timeout.
aTimeout->mTimer->Cancel();
aTimeout->mTimer = nsnull;
aTimeout->Release();
}
return false;
}
// Compute time to next timeout for interval timer.
// Make sure nextInterval is at least DOMMinTimeoutValue().
TimeDuration nextInterval =
TimeDuration::FromMilliseconds(NS_MAX(aTimeout->mInterval,
PRUint32(DOMMinTimeoutValue())));
// If we're running pending timeouts, set the next interval to be
// relative to "now", and not to when the timeout that was pending
// should have fired.
TimeStamp firingTime;
if (aRunningPendingTimeouts) {
firingTime = now + nextInterval;
} else {
firingTime = aTimeout->mWhen + nextInterval;
}
TimeStamp currentNow = TimeStamp::Now();
TimeDuration delay = firingTime - currentNow;
// And make sure delay is nonnegative; that might happen if the timer
// thread is firing our timers somewhat early or if they're taking a long
// time to run the callback.
if (delay < TimeDuration(0)) {
delay = TimeDuration(0);
}
if (!aTimeout->mTimer) {
NS_ASSERTION(IsFrozen() || mTimeoutsSuspendDepth,
"How'd our timer end up null if we're not frozen or "
"suspended?");
aTimeout->mTimeRemaining = delay;
return true;
}
aTimeout->mWhen = currentNow + delay;
// Reschedule the OS timer. Don't bother returning any error codes if
// this fails since the callers of this method don't care about them.
// Make sure to cast the unsigned PR_USEC_PER_MSEC to signed
// PRTime to make the division do the right thing on 64-bit
// platforms whether delay is positive or negative (which we
// know is always positive here, but cast anyways for
// consistency).
nsresult rv = aTimeout->mTimer->
InitWithFuncCallback(TimerCallback, aTimeout,
delay.ToMilliseconds(),
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
NS_ERROR("Error initializing timer for DOM timeout!");
// We failed to initialize the new OS timer, this timer does
// us no good here so we just cancel it (just in case) and
// null out the pointer to the OS timer, this will release the
// OS timer. As we continue executing the code below we'll end
// up deleting the timeout since it's not an interval timeout
// any more (since timeout->mTimer == nsnull).
aTimeout->mTimer->Cancel();
aTimeout->mTimer = nsnull;
// Now that the OS timer no longer has a reference to the
// timeout we need to drop that reference.
aTimeout->Release();
return false;
}
return true;
}
void
nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
{
@ -9104,90 +9272,8 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
}
// This timeout is good to run
nsTimeout *last_running_timeout = mRunningTimeout;
mRunningTimeout = timeout;
timeout->mRunning = true;
++timeoutsRan;
// Push this timeout's popup control state, which should only be
// eabled the first time a timeout fires that was created while
// popups were enabled and with a delay less than
// "dom.disable_open_click_delay".
nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState);
// Clear the timeout's popup state, if any, to prevent interval
// timeouts from repeatedly opening poups.
timeout->mPopupState = openAbused;
// Hold on to the timeout in case mExpr or mFunObj releases its
// doc.
timeout->AddRef();
++gRunningTimeoutDepth;
++mTimeoutFiringDepth;
bool trackNestingLevel = !timeout->mIsInterval;
PRUint32 nestingLevel;
if (trackNestingLevel) {
nestingLevel = sNestingLevel;
sNestingLevel = timeout->mNestingLevel;
}
nsCOMPtr<nsIScriptTimeoutHandler> handler(timeout->mScriptHandler);
JSObject* scriptObject = handler->GetScriptObject();
if (!scriptObject) {
// Evaluate the timeout expression.
const PRUnichar *script = handler->GetHandlerText();
NS_ASSERTION(script, "timeout has no script nor handler text!");
const char *filename = nsnull;
PRUint32 lineNo = 0;
handler->GetLocation(&filename, &lineNo);
NS_TIME_FUNCTION_MARK("(file: %s, line: %d)", filename, lineNo);
bool is_undefined;
scx->EvaluateString(nsDependentString(script), FastGetGlobalJSObject(),
timeout->mPrincipal, timeout->mPrincipal,
filename, lineNo, JSVERSION_DEFAULT, nsnull,
&is_undefined);
} else {
nsCOMPtr<nsIVariant> dummy;
nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
scx->CallEventHandler(me, FastGetGlobalJSObject(),
scriptObject, handler->GetArgv(),
// XXXmarkh - consider allowing CallEventHandler to
// accept nsnull?
getter_AddRefs(dummy));
}
handler = nsnull; // drop reference before dropping timeout refs.
if (trackNestingLevel) {
sNestingLevel = nestingLevel;
}
--mTimeoutFiringDepth;
--gRunningTimeoutDepth;
mRunningTimeout = last_running_timeout;
timeout->mRunning = false;
// We ignore any failures from calling EvaluateString() or
// CallEventHandler() on the context here since we're in a loop
// where we're likely to be running timeouts whose OS timers
// didn't fire in time and we don't want to not fire those timers
// now just because execution of one timer failed. We can't
// propagate the error to anyone who cares about it from this
// point anyway, and the script context should have already reported
// the script error in the usual way - so we just drop it.
// If all timeouts were cleared and |timeout != aTimeout| then
// |timeout| may be the last reference to the timeout so check if
// it was cleared before releasing it.
bool timeout_was_cleared = timeout->mCleared;
timeout->Release();
bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
if (timeout_was_cleared) {
// The running timeout's window was cleared, this means that
@ -9200,96 +9286,9 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
return;
}
bool isInterval = false;
// If we have a regular interval timer, we re-schedule the
// timeout, accounting for clock drift.
if (timeout->mIsInterval) {
// Compute time to next timeout for interval timer.
// Make sure nextInterval is at least DOMMinTimeoutValue().
TimeDuration nextInterval =
TimeDuration::FromMilliseconds(NS_MAX(timeout->mInterval,
PRUint32(DOMMinTimeoutValue())));
// If we're running pending timeouts because they've been temporarily
// disabled (!aTimeout), set the next interval to be relative to "now",
// and not to when the timeout that was pending should have fired.
TimeStamp firingTime;
if (!aTimeout)
firingTime = now + nextInterval;
else
firingTime = timeout->mWhen + nextInterval;
TimeStamp currentNow = TimeStamp::Now();
TimeDuration delay = firingTime - currentNow;
// And make sure delay is nonnegative; that might happen if the timer
// thread is firing our timers somewhat early or if they're taking a long
// time to run the callback.
if (delay < TimeDuration(0)) {
delay = TimeDuration(0);
}
if (timeout->mTimer) {
timeout->mWhen = currentNow + delay; // firingTime unless delay got
// clamped, in which case it's
// currentNow.
// Reschedule the OS timer. Don't bother returning any error
// codes if this fails since the callers of this method
// doesn't care about them nobody who cares about them
// anyways.
// Make sure to cast the unsigned PR_USEC_PER_MSEC to signed
// PRTime to make the division do the right thing on 64-bit
// platforms whether delay is positive or negative (which we
// know is always positive here, but cast anyways for
// consistency).
nsresult rv = timeout->mTimer->
InitWithFuncCallback(TimerCallback, timeout,
delay.ToMilliseconds(),
nsITimer::TYPE_ONE_SHOT);
if (NS_FAILED(rv)) {
NS_ERROR("Error initializing timer for DOM timeout!");
// We failed to initialize the new OS timer, this timer does
// us no good here so we just cancel it (just in case) and
// null out the pointer to the OS timer, this will release the
// OS timer. As we continue executing the code below we'll end
// up deleting the timeout since it's not an interval timeout
// any more (since timeout->mTimer == nsnull).
timeout->mTimer->Cancel();
timeout->mTimer = nsnull;
// Now that the OS timer no longer has a reference to the
// timeout we need to drop that reference.
timeout->Release();
}
} else {
NS_ASSERTION(IsFrozen() || mTimeoutsSuspendDepth,
"How'd our timer end up null if we're not frozen or "
"suspended?");
timeout->mTimeRemaining = delay;
isInterval = true;
}
}
if (timeout->mTimer) {
if (timeout->mIsInterval) {
isInterval = true;
} else {
// The timeout still has an OS timer, and it's not an
// interval, that means that the OS timer could still fire (if
// it didn't already, i.e. aTimeout == timeout), cancel the OS
// timer and release its reference to the timeout.
timeout->mTimer->Cancel();
timeout->mTimer = nsnull;
timeout->Release();
}
}
bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
// Running a timeout can cause another timeout to be deleted, so
// we need to reset the pointer to the following timeout.
@ -9297,9 +9296,10 @@ nsGlobalWindow::RunTimeout(nsTimeout *aTimeout)
PR_REMOVE_LINK(timeout);
if (isInterval) {
// Reschedule an interval timeout. Insert interval timeout
// onto list sorted in deadline order.
if (needsReinsertion) {
NS_ASSERTION(timeout->mTimer,
"rescheduling interval timeout without a timer!");
// Insert interval timeout onto list sorted in deadline order.
// AddRefs timeout.
InsertTimeoutIntoList(timeout);
}

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

@ -659,6 +659,11 @@ protected:
// The timeout implementation functions.
void RunTimeout(nsTimeout *aTimeout);
void RunTimeout() { RunTimeout(nsnull); }
// Return true if |aTimeout| was cleared while its handler ran.
bool RunTimeoutHandler(nsTimeout* aTimeout, nsIScriptContext* aScx);
// Return true if |aTimeout| needs to be reinserted into the timeout list.
bool RescheduleTimeout(nsTimeout* aTimeout, const TimeStamp& now,
bool aRunningPendingTimeouts);
void ClearAllTimeouts();
// Insert aTimeout into the list, before all timeouts that would