зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1437064 - Remove tracking timeouts list from TimeoutManager. r=chutten,Ehsan
Differential Revision: https://phabricator.services.mozilla.com/D7878 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
50a1f611cf
Коммит
c9272ef398
|
@ -1,173 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_OrderedTimeoutIterator_h__
|
||||
#define mozilla_dom_OrderedTimeoutIterator_h__
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/dom/Timeout.h"
|
||||
#include "mozilla/dom/TimeoutManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// This class implements and iterator which iterates the normal and tracking
|
||||
// timeouts lists simultaneously in the mWhen order.
|
||||
class MOZ_STACK_CLASS OrderedTimeoutIterator final {
|
||||
public:
|
||||
typedef TimeoutManager::Timeouts Timeouts;
|
||||
typedef Timeouts::TimeoutList TimeoutList;
|
||||
|
||||
OrderedTimeoutIterator(Timeouts& aNormalTimeouts,
|
||||
Timeouts& aTrackingTimeouts)
|
||||
: mNormalTimeouts(aNormalTimeouts.mTimeoutList),
|
||||
mTrackingTimeouts(aTrackingTimeouts.mTimeoutList),
|
||||
mNormalIter(mNormalTimeouts.getFirst()),
|
||||
mTrackingIter(mTrackingTimeouts.getFirst()),
|
||||
mKind(Kind::None),
|
||||
mUpdateIteratorCalled(true)
|
||||
{
|
||||
}
|
||||
|
||||
// Return the current timeout and move to the next one.
|
||||
// Unless this is the first time calling Next(), you must call
|
||||
// UpdateIterator() before calling this method.
|
||||
Timeout* Next()
|
||||
{
|
||||
MOZ_ASSERT(mUpdateIteratorCalled);
|
||||
MOZ_ASSERT_IF(mNormalIter, mNormalIter->isInList());
|
||||
MOZ_ASSERT_IF(mTrackingIter, mTrackingIter->isInList());
|
||||
|
||||
mUpdateIteratorCalled = false;
|
||||
mKind = Kind::None;
|
||||
Timeout* timeout = nullptr;
|
||||
if (!mNormalIter) {
|
||||
if (!mTrackingIter) {
|
||||
// We have reached the end of both lists. Bail out!
|
||||
return nullptr;
|
||||
} else {
|
||||
// We have reached the end of the normal timeout list, select the next
|
||||
// tracking timeout.
|
||||
timeout = mTrackingIter;
|
||||
mKind = Kind::Tracking;
|
||||
}
|
||||
} else if (!mTrackingIter) {
|
||||
// We have reached the end of the tracking timeout list, select the next
|
||||
// normal timeout.
|
||||
timeout = mNormalIter;
|
||||
mKind = Kind::Normal;
|
||||
} else {
|
||||
// If we have a normal and a tracking timer, return the one with the
|
||||
// smaller mWhen (and prefer the timeout with a lower ID in case they are
|
||||
// equal.) Otherwise, return whichever iterator has an item left,
|
||||
// preferring a non-tracking timeout again. Note that in practice, even
|
||||
// if a web page calls setTimeout() twice in a row, it should get
|
||||
// different mWhen values, so in practice we shouldn't fall back to
|
||||
// comparing timeout IDs.
|
||||
if (mNormalIter && mTrackingIter &&
|
||||
(mTrackingIter->When() < mNormalIter->When() ||
|
||||
(mTrackingIter->When() == mNormalIter->When() &&
|
||||
mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) {
|
||||
timeout = mTrackingIter;
|
||||
mKind = Kind::Tracking;
|
||||
} else if (mNormalIter) {
|
||||
timeout = mNormalIter;
|
||||
mKind = Kind::Normal;
|
||||
} else if (mTrackingIter) {
|
||||
timeout = mTrackingIter;
|
||||
mKind = Kind::Tracking;
|
||||
}
|
||||
}
|
||||
if (!timeout) {
|
||||
// We didn't find any suitable iterator. This can happen for example
|
||||
// when getNext() in UpdateIterator() returns nullptr and then Next()
|
||||
// gets called. Bail out!
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mKind != Kind::None);
|
||||
|
||||
// Record the current timeout we just found.
|
||||
mCurrent = timeout;
|
||||
MOZ_ASSERT(mCurrent);
|
||||
|
||||
return mCurrent;
|
||||
}
|
||||
|
||||
// Prepare the iterator for the next call to Next().
|
||||
// This method can be called as many times as needed. Calling this more than
|
||||
// once is helpful in cases where we expect the timeouts list has been
|
||||
// modified before we got a chance to call Next().
|
||||
void UpdateIterator()
|
||||
{
|
||||
MOZ_ASSERT(mKind != Kind::None);
|
||||
// Update the winning iterator to point to the next element. Also check to
|
||||
// see if the other iterator is still valid, otherwise reset it to the
|
||||
// beginning of the list. This is needed in case a timeout handler removes
|
||||
// the timeout pointed to from one of our iterators.
|
||||
if (mKind == Kind::Normal) {
|
||||
mNormalIter = mCurrent->getNext();
|
||||
if (mTrackingIter && !mTrackingIter->isInList()) {
|
||||
mTrackingIter = mTrackingTimeouts.getFirst();
|
||||
}
|
||||
} else {
|
||||
mTrackingIter = mCurrent->getNext();
|
||||
if (mNormalIter && !mNormalIter->isInList()) {
|
||||
mNormalIter = mNormalTimeouts.getFirst();
|
||||
}
|
||||
}
|
||||
|
||||
mUpdateIteratorCalled = true;
|
||||
}
|
||||
|
||||
// This function resets the iterator to a defunct state. It should only be
|
||||
// used when we want to forcefully sever all of the strong references this
|
||||
// class holds.
|
||||
void Clear()
|
||||
{
|
||||
// Release all strong references.
|
||||
mNormalIter = nullptr;
|
||||
mTrackingIter = nullptr;
|
||||
mCurrent = nullptr;
|
||||
mKind = Kind::None;
|
||||
mUpdateIteratorCalled = true;
|
||||
}
|
||||
|
||||
// Returns true if the previous call to Next() picked a normal timeout.
|
||||
// Cannot be called before Next() has been called. Note that the result of
|
||||
// this method is only affected by Next() and not UpdateIterator(), so calling
|
||||
// UpdateIterator() before calling this is allowed.
|
||||
bool PickedNormalIter() const
|
||||
{
|
||||
MOZ_ASSERT(mKind != Kind::None);
|
||||
return mKind == Kind::Normal;
|
||||
}
|
||||
|
||||
// Returns true if the previous call to Next() picked a tracking timeout.
|
||||
// Cannot be called before Next() has been called. Note that the result of
|
||||
// this method is only affected by Next() and not UpdateIterator(), so calling
|
||||
// UpdateIterator() before calling this is allowed.
|
||||
bool PickedTrackingIter() const
|
||||
{
|
||||
MOZ_ASSERT(mKind != Kind::None);
|
||||
return mKind == Kind::Tracking;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeoutList& mNormalTimeouts; // The list of normal timeouts.
|
||||
TimeoutList& mTrackingTimeouts; // The list of tracking timeouts.
|
||||
RefPtr<Timeout> mNormalIter; // The iterator over the normal timeout list.
|
||||
RefPtr<Timeout> mTrackingIter; // The iterator over the tracking timeout list.
|
||||
RefPtr<Timeout> mCurrent; // The current timeout that Next() just found.
|
||||
enum class Kind { Normal, Tracking, None };
|
||||
Kind mKind; // The kind of iterator picked the last time.
|
||||
DebugOnly<bool> mUpdateIteratorCalled; // Whether we have called UpdateIterator() before calling Next().
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -19,8 +19,7 @@ Timeout::Timeout()
|
|||
mNestingLevel(0),
|
||||
mCleared(false),
|
||||
mRunning(false),
|
||||
mIsInterval(false),
|
||||
mIsTracking(false)
|
||||
mIsInterval(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -104,9 +104,6 @@ public:
|
|||
|
||||
// True if this is a repeating/interval timer
|
||||
bool mIsInterval;
|
||||
|
||||
// True if this is a timeout coming from a tracking script
|
||||
bool mIsTracking;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -11,9 +11,6 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Time between sampling timeout execution time.
|
||||
const uint32_t kTelemetryPeriodMS = 1000;
|
||||
|
||||
/* static */ TimeoutBudgetManager&
|
||||
TimeoutBudgetManager::Get()
|
||||
{
|
||||
|
@ -35,8 +32,7 @@ TimeoutBudgetManager::StopRecording()
|
|||
|
||||
TimeDuration
|
||||
TimeoutBudgetManager::RecordExecution(const TimeStamp& aNow,
|
||||
const Timeout* aTimeout,
|
||||
bool aIsBackground)
|
||||
const Timeout* aTimeout)
|
||||
{
|
||||
if (!mStart) {
|
||||
// If we've started a sync operation mStart might be null, in
|
||||
|
@ -44,53 +40,7 @@ TimeoutBudgetManager::RecordExecution(const TimeStamp& aNow,
|
|||
return TimeDuration();
|
||||
}
|
||||
|
||||
TimeDuration duration = aNow - mStart;
|
||||
|
||||
if (aIsBackground) {
|
||||
if (aTimeout->mIsTracking) {
|
||||
mTelemetryData.mBackgroundTracking += duration;
|
||||
} else {
|
||||
mTelemetryData.mBackgroundNonTracking += duration;
|
||||
}
|
||||
} else {
|
||||
if (aTimeout->mIsTracking) {
|
||||
mTelemetryData.mForegroundTracking += duration;
|
||||
} else {
|
||||
mTelemetryData.mForegroundNonTracking += duration;
|
||||
}
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
void
|
||||
TimeoutBudgetManager::Accumulate(Telemetry::HistogramID aId,
|
||||
const TimeDuration& aSample)
|
||||
{
|
||||
uint32_t sample = std::round(aSample.ToMilliseconds());
|
||||
if (sample) {
|
||||
Telemetry::Accumulate(aId, sample);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimeoutBudgetManager::MaybeCollectTelemetry(const TimeStamp& aNow)
|
||||
{
|
||||
if ((aNow - mLastCollection).ToMilliseconds() < kTelemetryPeriodMS) {
|
||||
return;
|
||||
}
|
||||
|
||||
Accumulate(Telemetry::TIMEOUT_EXECUTION_FG_TRACKING_MS,
|
||||
mTelemetryData.mForegroundTracking);
|
||||
Accumulate(Telemetry::TIMEOUT_EXECUTION_FG_MS,
|
||||
mTelemetryData.mForegroundNonTracking);
|
||||
Accumulate(Telemetry::TIMEOUT_EXECUTION_BG_TRACKING_MS,
|
||||
mTelemetryData.mBackgroundTracking);
|
||||
Accumulate(Telemetry::TIMEOUT_EXECUTION_BG_MS,
|
||||
mTelemetryData.mBackgroundNonTracking);
|
||||
|
||||
mTelemetryData = TelemetryData();
|
||||
mLastCollection = aNow;
|
||||
return aNow - mStart;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#ifndef mozilla_dom_timeoutbudgetmanager_h
|
||||
#define mozilla_dom_timeoutbudgetmanager_h
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -22,24 +21,11 @@ public:
|
|||
void StartRecording(const TimeStamp& aNow);
|
||||
void StopRecording();
|
||||
TimeDuration RecordExecution(const TimeStamp& aNow,
|
||||
const Timeout* aTimeout,
|
||||
bool aIsBackground);
|
||||
void MaybeCollectTelemetry(const TimeStamp& aNow);
|
||||
const Timeout* aTimeout);
|
||||
private:
|
||||
TimeoutBudgetManager() : mLastCollection(TimeStamp::Now()) {}
|
||||
struct TelemetryData
|
||||
{
|
||||
TimeDuration mForegroundTracking;
|
||||
TimeDuration mForegroundNonTracking;
|
||||
TimeDuration mBackgroundTracking;
|
||||
TimeDuration mBackgroundNonTracking;
|
||||
};
|
||||
TimeoutBudgetManager() = default;
|
||||
|
||||
void Accumulate(Telemetry::HistogramID aId, const TimeDuration& aSample);
|
||||
|
||||
TelemetryData mTelemetryData;
|
||||
TimeStamp mStart;
|
||||
TimeStamp mLastCollection;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "nsITimeoutHandler.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "OrderedTimeoutIterator.h"
|
||||
#include "TimeoutExecutor.h"
|
||||
#include "TimeoutBudgetManager.h"
|
||||
#include "mozilla/net/WebSocketEventService.h"
|
||||
|
@ -293,11 +292,6 @@ TimeoutManager::CalculateDelay(Timeout* aTimeout) const {
|
|||
result, TimeDuration::FromMilliseconds(gMinClampTimeoutValue));
|
||||
}
|
||||
|
||||
if (aTimeout->mIsTracking && mThrottleTrackingTimeouts) {
|
||||
result = TimeDuration::Max(
|
||||
result, TimeDuration::FromMilliseconds(gMinTrackingTimeoutValue));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -332,9 +326,7 @@ TimeoutManager::RecordExecution(Timeout* aRunningTimeout,
|
|||
if (aRunningTimeout) {
|
||||
// If we're running a timeout callback, record any execution until
|
||||
// now.
|
||||
TimeDuration duration = budgetManager.RecordExecution(
|
||||
now, aRunningTimeout, mWindow.IsBackgroundInternal());
|
||||
budgetManager.MaybeCollectTelemetry(now);
|
||||
TimeDuration duration = budgetManager.RecordExecution(now, aRunningTimeout);
|
||||
|
||||
UpdateBudget(now, duration);
|
||||
|
||||
|
@ -396,13 +388,6 @@ TimeoutManager::UpdateBudget(const TimeStamp& aNow, const TimeDuration& aDuratio
|
|||
mLastBudgetUpdate = aNow;
|
||||
}
|
||||
|
||||
#define TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY 0 // Consider all timeouts coming from tracking scripts as tracking
|
||||
// These strategies are useful for testing.
|
||||
#define ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY 1 // Consider all timeouts as normal
|
||||
#define ALTERNATE_TIMEOUT_BUCKETING_STRATEGY 2 // Put every other timeout in the list of tracking timeouts
|
||||
#define RANDOM_TIMEOUT_BUCKETING_STRATEGY 3 // Put timeouts into either the normal or tracking timeouts list randomly
|
||||
static int32_t gTimeoutBucketingStrategy = 0;
|
||||
|
||||
#define DEFAULT_TIMEOUT_THROTTLING_DELAY -1 // Only positive integers cause us to introduce a delay for
|
||||
// timeout throttling.
|
||||
|
||||
|
@ -430,8 +415,7 @@ int32_t gDisableOpenClickDelay;
|
|||
TimeoutManager::TimeoutManager(nsGlobalWindowInner& aWindow)
|
||||
: mWindow(aWindow),
|
||||
mExecutor(new TimeoutExecutor(this)),
|
||||
mNormalTimeouts(*this),
|
||||
mTrackingTimeouts(*this),
|
||||
mTimeouts(*this),
|
||||
mTimeoutIdCounter(1),
|
||||
mNextFiringId(InvalidFiringId + 1),
|
||||
mRunningTimeout(nullptr),
|
||||
|
@ -474,9 +458,6 @@ TimeoutManager::Initialize()
|
|||
Preferences::AddIntVarCache(&gMinTrackingBackgroundTimeoutValue,
|
||||
"dom.min_tracking_background_timeout_value",
|
||||
DEFAULT_MIN_TRACKING_BACKGROUND_TIMEOUT_VALUE);
|
||||
Preferences::AddIntVarCache(&gTimeoutBucketingStrategy,
|
||||
"dom.timeout_bucketing_strategy",
|
||||
TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY);
|
||||
Preferences::AddIntVarCache(&gTimeoutThrottlingDelay,
|
||||
"dom.timeout.throttling_delay",
|
||||
DEFAULT_TIMEOUT_THROTTLING_DELAY);
|
||||
|
@ -563,43 +544,6 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
// No popups from timeouts by default
|
||||
timeout->mPopupState = openAbused;
|
||||
|
||||
switch (gTimeoutBucketingStrategy) {
|
||||
default:
|
||||
case TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY: {
|
||||
const char* filename = nullptr;
|
||||
uint32_t dummyLine = 0, dummyColumn = 0;
|
||||
aHandler->GetLocation(&filename, &dummyLine, &dummyColumn);
|
||||
timeout->mIsTracking = doc->IsScriptTracking(nsDependentCString(filename));
|
||||
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Classified timeout %p set from %s as %stracking\n",
|
||||
timeout.get(), filename, timeout->mIsTracking ? "" : "non-"));
|
||||
break;
|
||||
}
|
||||
case ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY:
|
||||
// timeout->mIsTracking is already false!
|
||||
MOZ_DIAGNOSTIC_ASSERT(!timeout->mIsTracking);
|
||||
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Classified timeout %p unconditionally as normal\n",
|
||||
timeout.get()));
|
||||
break;
|
||||
case ALTERNATE_TIMEOUT_BUCKETING_STRATEGY:
|
||||
timeout->mIsTracking = (mTimeoutIdCounter % 2) == 0;
|
||||
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Classified timeout %p as %stracking (alternating mode)\n",
|
||||
timeout.get(), timeout->mIsTracking ? "" : "non-"));
|
||||
break;
|
||||
case RANDOM_TIMEOUT_BUCKETING_STRATEGY:
|
||||
timeout->mIsTracking = (rand() % 2) == 0;
|
||||
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Classified timeout %p as %stracking (random mode)\n",
|
||||
timeout.get(), timeout->mIsTracking ? "" : "non-"));
|
||||
break;
|
||||
}
|
||||
|
||||
timeout->mNestingLevel = sNestingLevel < DOM_CLAMP_TIMEOUT_NESTING_LEVEL
|
||||
? sNestingLevel + 1 : sNestingLevel;
|
||||
|
||||
|
@ -633,11 +577,7 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
|
||||
Timeouts::SortBy sort(mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
||||
: Timeouts::SortBy::TimeWhen);
|
||||
if (timeout->mIsTracking) {
|
||||
mTrackingTimeouts.Insert(timeout, sort);
|
||||
} else {
|
||||
mNormalTimeouts.Insert(timeout, sort);
|
||||
}
|
||||
mTimeouts.Insert(timeout, sort);
|
||||
|
||||
timeout->mTimeoutId = GetTimeoutId(aReason);
|
||||
*aReturn = timeout->mTimeoutId;
|
||||
|
@ -646,7 +586,7 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
LogLevel::Debug,
|
||||
("Set%s(TimeoutManager=%p, timeout=%p, delay=%i, "
|
||||
"minimum=%f, throttling=%s, state=%s(%s), realInterval=%f) "
|
||||
"returned %stracking timeout ID %u, budget=%d\n",
|
||||
"returned timeout ID %u, budget=%d\n",
|
||||
aIsInterval ? "Interval" : "Timeout",
|
||||
this, timeout.get(), interval,
|
||||
(CalculateDelay(timeout) - timeout->mInterval).ToMilliseconds(),
|
||||
|
@ -656,7 +596,6 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
|||
IsActive() ? "active" : "inactive",
|
||||
mWindow.IsBackgroundInternal() ? "background" : "foreground",
|
||||
realInterval.ToMilliseconds(),
|
||||
timeout->mIsTracking ? "" : "non-",
|
||||
timeout->mTimeoutId,
|
||||
int(mExecutionBudget.ToMilliseconds())));
|
||||
|
||||
|
@ -671,11 +610,10 @@ TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
|
|||
bool firstTimeout = true;
|
||||
bool deferredDeletion = false;
|
||||
|
||||
ForEachUnorderedTimeoutAbortable([&](Timeout* aTimeout) {
|
||||
mTimeouts.ForEachAbortable([&](Timeout* aTimeout) {
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Clear%s(TimeoutManager=%p, timeout=%p, aTimerId=%u, ID=%u, tracking=%d)\n", aTimeout->mIsInterval ? "Interval" : "Timeout",
|
||||
this, aTimeout, timerId, aTimeout->mTimeoutId,
|
||||
int(aTimeout->mIsTracking)));
|
||||
("Clear%s(TimeoutManager=%p, timeout=%p, aTimerId=%u, ID=%u)\n", aTimeout->mIsInterval ? "Interval" : "Timeout",
|
||||
this, aTimeout, timerId, aTimeout->mTimeoutId));
|
||||
|
||||
if (aTimeout->mTimeoutId == timerId && aTimeout->mReason == aReason) {
|
||||
if (aTimeout->mRunning) {
|
||||
|
@ -712,8 +650,7 @@ TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
|
|||
// Stop the executor and restart it at the next soonest deadline.
|
||||
mExecutor->Cancel();
|
||||
|
||||
OrderedTimeoutIterator iter(mNormalTimeouts, mTrackingTimeouts);
|
||||
Timeout* nextTimeout = iter.Next();
|
||||
Timeout* nextTimeout = mTimeouts.GetFirst();
|
||||
if (nextTimeout) {
|
||||
MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(nextTimeout->When()));
|
||||
}
|
||||
|
@ -787,39 +724,30 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
|
|||
// if the timer fired early. So we can stop walking if we get to timeouts
|
||||
// whose When() is greater than deadline, since once that happens we know
|
||||
// nothing past that point is expired.
|
||||
{
|
||||
// Use a nested scope in order to make sure the strong references held by
|
||||
// the iterator are freed after the loop.
|
||||
OrderedTimeoutIterator expiredIter(mNormalTimeouts, mTrackingTimeouts);
|
||||
for (Timeout* timeout = mTimeouts.GetFirst();
|
||||
timeout != nullptr;
|
||||
timeout = timeout->getNext()) {
|
||||
if (totalTimeLimit.IsZero() || timeout->When() > deadline) {
|
||||
nextDeadline = timeout->When();
|
||||
break;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
Timeout* timeout = expiredIter.Next();
|
||||
if (!timeout || totalTimeLimit.IsZero() || timeout->When() > deadline) {
|
||||
if (timeout) {
|
||||
if (IsInvalidFiringId(timeout->mFiringId)) {
|
||||
// Mark any timeouts that are on the list to be fired with the
|
||||
// firing depth so that we can reentrantly run timeouts
|
||||
timeout->mFiringId = firingId;
|
||||
|
||||
numTimersToRun += 1;
|
||||
|
||||
// Run only a limited number of timers based on the configured maximum.
|
||||
if (numTimersToRun % kNumTimersPerInitialElapsedCheck == 0) {
|
||||
now = TimeStamp::Now();
|
||||
TimeDuration elapsed(now - start);
|
||||
if (elapsed >= initialTimeLimit) {
|
||||
nextDeadline = timeout->When();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsInvalidFiringId(timeout->mFiringId)) {
|
||||
// Mark any timeouts that are on the list to be fired with the
|
||||
// firing depth so that we can reentrantly run timeouts
|
||||
timeout->mFiringId = firingId;
|
||||
|
||||
numTimersToRun += 1;
|
||||
|
||||
// Run only a limited number of timers based on the configured maximum.
|
||||
if (numTimersToRun % kNumTimersPerInitialElapsedCheck == 0) {
|
||||
now = TimeStamp::Now();
|
||||
TimeDuration elapsed(now - start);
|
||||
if (elapsed >= initialTimeLimit) {
|
||||
nextDeadline = timeout->When();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
expiredIter.UpdateIterator();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -853,17 +781,18 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
|
|||
// next item after the last timeout we looked at or nullptr if we have
|
||||
// exhausted the entire list while looking for the last expired timeout.
|
||||
{
|
||||
// Use a nested scope in order to make sure the strong references held by
|
||||
// the iterator are freed after the loop.
|
||||
OrderedTimeoutIterator runIter(mNormalTimeouts, mTrackingTimeouts);
|
||||
while (true) {
|
||||
RefPtr<Timeout> timeout = runIter.Next();
|
||||
if (!timeout) {
|
||||
// We have run out of timeouts!
|
||||
break;
|
||||
}
|
||||
runIter.UpdateIterator();
|
||||
// Use a nested scope in order to make sure the strong references held while
|
||||
// iterating are freed after the loop.
|
||||
|
||||
// The next timeout to run. This is used to advance the loop, but
|
||||
// we cannot set it until we've run the current timeout, since
|
||||
// running the current timeout might remove the immediate next
|
||||
// timeout.
|
||||
RefPtr<Timeout> next;
|
||||
|
||||
for (RefPtr<Timeout> timeout = mTimeouts.GetFirst();
|
||||
timeout != nullptr;
|
||||
timeout = next) {
|
||||
// We should only execute callbacks for the set of expired Timeout
|
||||
// objects we computed above.
|
||||
if (timeout->mFiringId != firingId) {
|
||||
|
@ -907,15 +836,18 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
|
|||
// This timeout is good to run
|
||||
bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
|
||||
|
||||
MOZ_LOG(gLog, LogLevel::Debug,
|
||||
("Run%s(TimeoutManager=%p, timeout=%p, tracking=%d) returned %d\n", timeout->mIsInterval ? "Interval" : "Timeout",
|
||||
this, timeout.get(),
|
||||
int(timeout->mIsTracking),
|
||||
!!timeout_was_cleared));
|
||||
MOZ_LOG(
|
||||
gLog,
|
||||
LogLevel::Debug,
|
||||
("Run%s(TimeoutManager=%p, timeout=%p) returned %d\n",
|
||||
timeout->mIsInterval ? "Interval" : "Timeout",
|
||||
this,
|
||||
timeout.get(),
|
||||
!!timeout_was_cleared));
|
||||
|
||||
if (timeout_was_cleared) {
|
||||
// Make sure the iterator isn't holding any Timeout objects alive.
|
||||
runIter.Clear();
|
||||
// Make sure we're not holding any Timeout objects alive.
|
||||
next = nullptr;
|
||||
|
||||
// Since ClearAllTimeouts() was called the lists should be empty.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!HasTimeouts());
|
||||
|
@ -936,22 +868,16 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
|
|||
|
||||
// Running a timeout can cause another timeout to be deleted, so
|
||||
// we need to reset the pointer to the following timeout.
|
||||
runIter.UpdateIterator();
|
||||
next = timeout->getNext();
|
||||
|
||||
timeout->remove();
|
||||
|
||||
if (needsReinsertion) {
|
||||
// Insert interval timeout onto the corresponding list sorted in
|
||||
// deadline order. AddRefs timeout.
|
||||
if (runIter.PickedTrackingIter()) {
|
||||
mTrackingTimeouts.Insert(timeout,
|
||||
mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
||||
: Timeouts::SortBy::TimeWhen);
|
||||
} else {
|
||||
mNormalTimeouts.Insert(timeout,
|
||||
mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
||||
: Timeouts::SortBy::TimeWhen);
|
||||
}
|
||||
mTimeouts.Insert(timeout,
|
||||
mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
|
||||
: Timeouts::SortBy::TimeWhen);
|
||||
}
|
||||
|
||||
// Check to see if we have run out of time to execute timeout handlers.
|
||||
|
@ -963,8 +889,7 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
|
|||
// however, that the last timeout handler suspended the window. If
|
||||
// that happened then we must skip this step.
|
||||
if (!mWindow.IsSuspended()) {
|
||||
RefPtr<Timeout> timeout = runIter.Next();
|
||||
if (timeout) {
|
||||
if (next) {
|
||||
// If we ran out of execution budget we need to force a
|
||||
// reschedule. By cancelling the executor we will not run
|
||||
// immediately, but instead reschedule to the minimum
|
||||
|
@ -973,7 +898,7 @@ TimeoutManager::RunTimeout(const TimeStamp& aNow, const TimeStamp& aTargetDeadli
|
|||
mExecutor->Cancel();
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(timeout->When(), now));
|
||||
MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(next->When(), now));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1056,8 +981,7 @@ TimeoutManager::ClearAllTimeouts()
|
|||
});
|
||||
|
||||
// Clear out our list
|
||||
mNormalTimeouts.Clear();
|
||||
mTrackingTimeouts.Clear();
|
||||
mTimeouts.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1149,8 +1073,7 @@ TimeoutManager::Resume()
|
|||
MaybeStartThrottleTimeout();
|
||||
}
|
||||
|
||||
OrderedTimeoutIterator iter(mNormalTimeouts, mTrackingTimeouts);
|
||||
Timeout* nextTimeout = iter.Next();
|
||||
Timeout* nextTimeout = mTimeouts.GetFirst();
|
||||
if (nextTimeout) {
|
||||
MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(nextTimeout->When()));
|
||||
}
|
||||
|
@ -1202,8 +1125,7 @@ TimeoutManager::UpdateBackgroundState()
|
|||
// changed. Only do this if the window is not suspended and we
|
||||
// actually have a timeout.
|
||||
if (!mWindow.IsSuspended()) {
|
||||
OrderedTimeoutIterator iter(mNormalTimeouts, mTrackingTimeouts);
|
||||
Timeout* nextTimeout = iter.Next();
|
||||
Timeout* nextTimeout = mTimeouts.GetFirst();
|
||||
if (nextTimeout) {
|
||||
mExecutor->Cancel();
|
||||
MOZ_ALWAYS_SUCCEEDS(MaybeSchedule(nextTimeout->When()));
|
||||
|
@ -1211,14 +1133,6 @@ TimeoutManager::UpdateBackgroundState()
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TimeoutManager::IsTimeoutTracking(uint32_t aTimeoutId)
|
||||
{
|
||||
return mTrackingTimeouts.ForEachAbortable([&](Timeout* aTimeout) {
|
||||
return aTimeout->mTimeoutId == aTimeoutId;
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ThrottleTimeoutsCallback final : public nsITimerCallback
|
||||
|
|
|
@ -21,7 +21,6 @@ class PerformanceCounter;
|
|||
|
||||
namespace dom {
|
||||
|
||||
class OrderedTimeoutIterator;
|
||||
class TimeoutExecutor;
|
||||
|
||||
// This class manages the timeouts in a Window's setTimeout/setInterval pool.
|
||||
|
@ -40,8 +39,7 @@ public:
|
|||
|
||||
bool HasTimeouts() const
|
||||
{
|
||||
return !mNormalTimeouts.IsEmpty() ||
|
||||
!mTrackingTimeouts.IsEmpty();
|
||||
return !mTimeouts.IsEmpty();
|
||||
}
|
||||
|
||||
nsresult SetTimeout(nsITimeoutHandler* aHandler,
|
||||
|
@ -81,9 +79,6 @@ public:
|
|||
// Initialize TimeoutManager before the first time it is accessed.
|
||||
static void Initialize();
|
||||
|
||||
// Exposed only for testing
|
||||
bool IsTimeoutTracking(uint32_t aTimeoutId);
|
||||
|
||||
// The document finished loading
|
||||
void OnDocumentLoaded();
|
||||
void StartThrottlingTimeouts();
|
||||
|
@ -93,19 +88,7 @@ public:
|
|||
template <class Callable>
|
||||
void ForEachUnorderedTimeout(Callable c)
|
||||
{
|
||||
mNormalTimeouts.ForEach(c);
|
||||
mTrackingTimeouts.ForEach(c);
|
||||
}
|
||||
|
||||
// Run some code for each Timeout in our list, but let the callback cancel the
|
||||
// iteration by returning true. Note that this function doesn't guarantee
|
||||
// that Timeouts are iterated in any particular order.
|
||||
template <class Callable>
|
||||
void ForEachUnorderedTimeoutAbortable(Callable c)
|
||||
{
|
||||
if (!mNormalTimeouts.ForEachAbortable(c)) {
|
||||
mTrackingTimeouts.ForEachAbortable(c);
|
||||
}
|
||||
mTimeouts.ForEach(c);
|
||||
}
|
||||
|
||||
void BeginSyncOperation();
|
||||
|
@ -204,8 +187,6 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
friend class OrderedTimeoutIterator;
|
||||
|
||||
private:
|
||||
// The TimeoutManager that owns this Timeouts structure. This is
|
||||
// mainly used to call state inspecting methods like IsValidFiringId().
|
||||
|
@ -218,8 +199,6 @@ private:
|
|||
TimeoutList mTimeoutList;
|
||||
};
|
||||
|
||||
friend class OrderedTimeoutIterator;
|
||||
|
||||
// Each nsGlobalWindowInner object has a TimeoutManager member. This reference
|
||||
// points to that holder object.
|
||||
nsGlobalWindowInner& mWindow;
|
||||
|
@ -228,9 +207,7 @@ private:
|
|||
// it must be a separate ref-counted object.
|
||||
RefPtr<TimeoutExecutor> mExecutor;
|
||||
// The list of timeouts coming from non-tracking scripts.
|
||||
Timeouts mNormalTimeouts;
|
||||
// The list of timeouts coming from scripts on the tracking protection list.
|
||||
Timeouts mTrackingTimeouts;
|
||||
Timeouts mTimeouts;
|
||||
uint32_t mTimeoutIdCounter;
|
||||
uint32_t mNextFiringId;
|
||||
AutoTArray<uint32_t, 2> mFiringIdStack;
|
||||
|
|
|
@ -4250,21 +4250,6 @@ nsDOMWindowUtils::GetGpuProcessPid(int32_t* aPid)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::IsTimeoutTracking(uint32_t aTimeoutId, bool* aResult)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
*aResult = false;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_STATE(window);
|
||||
nsCOMPtr<nsPIDOMWindowInner> innerWindow = window->GetCurrentInnerWindow();
|
||||
NS_ENSURE_STATE(innerWindow);
|
||||
|
||||
*aResult = innerWindow->TimeoutManager().IsTimeoutTracking(aTimeoutId);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
struct StateTableEntry
|
||||
{
|
||||
const char* mStateString;
|
||||
|
@ -4493,4 +4478,3 @@ nsDOMWindowUtils::IsCssPropertyRecordedInUseCounter(const nsACString& aPropName,
|
|||
&knownProp);
|
||||
return knownProp ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,25 +3,15 @@
|
|||
<body>
|
||||
<script>
|
||||
let count = 0;
|
||||
let last_timer_set = 0;
|
||||
let last_timer_observed = 0;
|
||||
function cb(timer_observed) {
|
||||
if (timer_observed > last_timer_observed) {
|
||||
// In order to make the test more efficient, we don't use the SimpleTest
|
||||
// ok() function to avoid generating one test assertion per one of these
|
||||
// checks. We only send a message to the parent which fails the test if
|
||||
// we detect out of order firing of timeouts.
|
||||
window.parent.postMessage('OUT_OF_ORDER', '*');
|
||||
}
|
||||
last_timer_observed = timer_observed;
|
||||
function cb() {
|
||||
count += 1;
|
||||
// Notify our parent that we are ready once the timer flood has
|
||||
// warmed up.
|
||||
if (count === 10000) {
|
||||
window.parent.postMessage('STARTED', '*');
|
||||
}
|
||||
last_timer_set = setTimeout(cb.bind(last_timer_set), 0);
|
||||
last_timer_set = setTimeout(cb.bind(last_timer_set), 0);
|
||||
setTimeout(cb, 0);
|
||||
setTimeout(cb, 0);
|
||||
}
|
||||
addEventListener('load', cb);
|
||||
</script>
|
||||
|
|
|
@ -22,24 +22,15 @@ function onLoad() {
|
|||
});
|
||||
}
|
||||
|
||||
function setPrefs() {
|
||||
// Put timeouts randomly in the tracking or normal buffer. We do this in order to
|
||||
// test to ensure that by default, this will not change the scheduling of timeouts.
|
||||
return SpecialPowers.pushPrefEnv({"set": [["dom.timeout_bucketing_strategy", 3]]});
|
||||
}
|
||||
|
||||
// Create a frame that executes a timer flood. The frame signals
|
||||
// that is ready once the flood has had a chance to warm up.
|
||||
function withFloodFrame() {
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise(resolve => {
|
||||
let frame = document.createElement('iframe');
|
||||
addEventListener('message', function onMsg(evt) {
|
||||
if (evt.data === 'STARTED') {
|
||||
removeEventListener('message', onMsg);
|
||||
resolve(frame);
|
||||
} else if (evt.data == 'OUT_OF_ORDER') {
|
||||
ok(false, "Out of order timeout observed");
|
||||
reject();
|
||||
}
|
||||
});
|
||||
frame.src = 'file_timer_flood.html';
|
||||
|
@ -86,9 +77,7 @@ function testRequestAnimationFrame() {
|
|||
|
||||
let floodFrame;
|
||||
|
||||
onLoad()
|
||||
.then(setPrefs)
|
||||
.then(_ => {
|
||||
onLoad().then(_ => {
|
||||
// Start a timer flood in a frame.
|
||||
return withFloodFrame();
|
||||
}).then(frame => {
|
||||
|
@ -122,8 +111,6 @@ onLoad()
|
|||
ok(true, 'completed tests without timing out');
|
||||
floodFrame.remove();
|
||||
SimpleTest.finish();
|
||||
}).catch(_ => {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
|
|
|
@ -1876,12 +1876,6 @@ interface nsIDOMWindowUtils : nsISupports {
|
|||
*/
|
||||
readonly attribute int32_t gpuProcessPid;
|
||||
|
||||
/**
|
||||
* Returns true if the given timeout ID is in the list of tracking
|
||||
* timeouts.
|
||||
*/
|
||||
boolean isTimeoutTracking(in unsigned long timeoutId);
|
||||
|
||||
/**
|
||||
* Adds an EventStates bit to the element.
|
||||
*
|
||||
|
|
|
@ -13179,51 +13179,6 @@
|
|||
"keyed": true,
|
||||
"description": "Measures the number of milliseconds we spend waiting for sync message manager IPC messages to finish sending, keyed by message name. Note: only messages that wait for more than 500 microseconds are included in this probe."
|
||||
},
|
||||
"TIMEOUT_EXECUTION_FG_MS":
|
||||
{
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["farre@mozilla.com"],
|
||||
"bug_numbers": [1355480],
|
||||
"expires_in_version": "61",
|
||||
"kind": "exponential",
|
||||
"high": 1000,
|
||||
"n_buckets": 20,
|
||||
"description": "Time in ms used to execute callbacks from setTimeout/setInterval, when the script belongs to a tab in the foreground and the script is not on the tracking list. Multiple events are aggregated over a 1s interval."
|
||||
},
|
||||
"TIMEOUT_EXECUTION_FG_TRACKING_MS":
|
||||
{
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["farre@mozilla.com"],
|
||||
"bug_numbers": [1355480],
|
||||
"expires_in_version": "61",
|
||||
"kind": "exponential",
|
||||
"high": 1000,
|
||||
"n_buckets": 20,
|
||||
"description": "Time in ms used to execute callbacks from setTimeout/setInterval, when the script belongs to a tab in the foreground and the script is on the tracking list. Multiple events are aggregated over a 1s interval."
|
||||
},
|
||||
"TIMEOUT_EXECUTION_BG_MS":
|
||||
{
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["farre@mozilla.com"],
|
||||
"bug_numbers": [1355480],
|
||||
"expires_in_version": "61",
|
||||
"kind": "exponential",
|
||||
"high": 1000,
|
||||
"n_buckets": 20,
|
||||
"description": "Time in ms used to execute callbacks from setTimeout/setInterval, when the script belongs to a tab in the background and the script is not on the tracking list. Multiple events are aggregated over a 1s interval."
|
||||
},
|
||||
"TIMEOUT_EXECUTION_BG_TRACKING_MS":
|
||||
{
|
||||
"record_in_processes": ["main", "content"],
|
||||
"alert_emails": ["farre@mozilla.com"],
|
||||
"bug_numbers": [1355480],
|
||||
"expires_in_version": "61",
|
||||
"kind": "exponential",
|
||||
"low": 1,
|
||||
"high": 1000,
|
||||
"n_buckets": 10,
|
||||
"description": "Time in ms used to execute callbacks from setTimeout/setInterval, when the script belongs to a tab in the background and the script is on the tracking list. Multiple events are aggregated over a 1s interval."
|
||||
},
|
||||
"TIME_TO_DOM_LOADING_MS": {
|
||||
"record_in_processes": ["content"],
|
||||
"alert_emails": ["hbambas@mozilla.com", "vgosu@mozilla.com", "jduell@mozilla.com"],
|
||||
|
|
|
@ -42,7 +42,6 @@ support-files =
|
|||
!/toolkit/components/url-classifier/tests/mochitest/bad.css
|
||||
!/toolkit/components/url-classifier/tests/mochitest/bad.css^headers^
|
||||
!/toolkit/components/url-classifier/tests/mochitest/gethashFrame.html
|
||||
!/toolkit/components/url-classifier/tests/mochitest/tracker.js
|
||||
!/toolkit/components/url-classifier/tests/mochitest/seek.webm
|
||||
!/toolkit/components/url-classifier/tests/mochitest/cache.sjs
|
||||
|
||||
|
|
|
@ -32,17 +32,6 @@ function checkLoads() {
|
|||
return;
|
||||
}
|
||||
|
||||
let dwu = window.parent.SpecialPowers.getDOMWindowUtils(window);
|
||||
let timer1 = window.setTimeout(function() {}, 0);
|
||||
window.parent.ok(!dwu.isTimeoutTracking(timer1),
|
||||
"Timeout set from main script should not be considered as tracking");
|
||||
/* global getTrackerTimeout */
|
||||
let timer2 = getTrackerTimeout();
|
||||
window.parent.ok(dwu.isTimeoutTracking(timer2),
|
||||
"Timeout set from included script should be considered as tracking");
|
||||
window.clearTimeout(timer1);
|
||||
window.clearTimeout(timer2);
|
||||
|
||||
// End (parent) test.
|
||||
window.parent.SimpleTest.finish();
|
||||
}
|
||||
|
@ -52,9 +41,6 @@ function checkLoads() {
|
|||
<!-- Try loading from a malware javascript URI -->
|
||||
<script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
|
||||
|
||||
<!-- Try loading from a tracker javascript URI -->
|
||||
<script type="text/javascript" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/tracker.js"></script>
|
||||
|
||||
<!-- Try loading from an uwanted software css URI -->
|
||||
<link rel="stylesheet" type="text/css" href="http://unwanted.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
|
||||
|
||||
|
|
|
@ -30,7 +30,6 @@ support-files =
|
|||
bad.css^headers^
|
||||
gethash.sjs
|
||||
gethashFrame.html
|
||||
tracker.js
|
||||
seek.webm
|
||||
cache.sjs
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
function getTrackerTimeout() {
|
||||
return window.setTimeout(function() {}, 0);
|
||||
}
|
Загрузка…
Ссылка в новой задаче