nsresult TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS, int32_t aMinTimeoutValueMS, SortBy aSortBy, nsIEventTarget* aQueue) { TimeStamp now = TimeStamp::Now(); // If insertion point is non-null, we're in the middle of firing timers and // the timers we're planning to fire all come before insertion point; // insertion point itself is a dummy timeout with an When() that may be // semi-bogus. In that case, we don't need to do anything with insertion // point or anything before it, so should start at the timer after insertion // point, if there is one. // Otherwise, start at the beginning of the list. for (Timeout* timeout = InsertionPoint() ? InsertionPoint()->getNext() : GetFirst(); timeout; ) { // It's important that this check be <= so that we guarantee that // taking std::max with |now| won't make a quantity equal to // timeout->When() below. if (timeout->When() <= now) { timeout = timeout->getNext(); continue; } if (timeout->When() - now > TimeDuration::FromMilliseconds(aPreviousThrottleDelayMS)) { // No need to loop further. Timeouts are sorted in When() order // and the ones after this point were all set up for at least // gMinBackgroundTimeoutValue ms and hence were not clamped. break; } // We reduced our throttled delay. Re-init the timer appropriately. // Compute the interval the timer should have had if it had not been set in a // background window TimeDuration interval = TimeDuration::FromMilliseconds(std::max(timeout->mInterval, uint32_t(aMinTimeoutValueMS))); uint32_t oldIntervalMillisecs = 0; timeout->mTimer->GetDelay(&oldIntervalMillisecs); TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs); if (oldInterval > interval) { // unclamp TimeStamp firingTime = std::max(timeout->When() - oldInterval + interval, now); NS_ASSERTION(firingTime < timeout->When(), "Our firing time should strictly decrease!"); TimeDuration delay = firingTime - now; timeout->SetWhenOrTimeRemaining(now, delay); MOZ_DIAGNOSTIC_ASSERT(timeout->When() == firingTime); // Since we reset When() we need to move |timeout| to the right // place in the list so that it remains sorted by When(). // Get the pointer to the next timeout now, before we move the // current timeout in the list. Timeout* nextTimeout = timeout->getNext(); // It is safe to remove and re-insert because When() is now // strictly smaller than it used to be, so we know we'll insert // |timeout| before nextTimeout. NS_ASSERTION(!nextTimeout || timeout->When() < nextTimeout->When(), "How did that happen?"); timeout->remove(); // Insert() will addref |timeout| and reset mFiringDepth. Make sure to // undo that after calling it. uint32_t firingDepth = timeout->mFiringDepth; Insert(timeout, aSortBy); timeout->mFiringDepth = firingDepth; timeout->Release(); nsresult rv = timeout->InitTimer(aQueue, delay.ToMilliseconds()); if (NS_FAILED(rv)) { NS_WARNING("Error resetting non background timer for DOM timeout!"); return rv; } timeout = nextTimeout; } else { timeout = timeout->getNext(); } } return NS_OK; }