зеркало из https://github.com/mozilla/gecko-dev.git
Bug 748464 - refactor nsGlobalWindow::RunTimeout; r=bz
This commit is contained in:
Родитель
d22fa8806c
Коммит
8578a20583
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче