gecko-dev/dom/base/Timeout.cpp
Ehsan Akhgari 7326630b5d Bug 1312514 - Part 1: Split tracking and non-tracking timeouts into two separate lists; r=bkelly
This will allow us to schedule these timers differently in the future.
This patch only performs the refactoring, and is not intended to change
any behavior.  Specifically, this patch doesn't change the order in
which timeouts are fired -- they should still all be fired according to
the mWhen field.

The implementation works by splitting timeout storage per window into
two Timeouts objects, mNormalTimeouts and mTrackingTimeouts.  The ForEach
helper methods are extended to deal with both of these objects, and as a
result, most of the algorithms operating on the list of timeouts work
correctly without any modification, with the notable exception of
RunTimeout.

In RunTimeout(), the order in which Timeout objects are processed does
matter, so for that case we use the OrderedTimeoutIterator class to
iterate over both linked lists simultaneously in the mWhen order.  Also,
inserting the dummy timeout when running the timeouts is only necessary
for the linked list where the last expired timeout is coming from, so we
only inject the dummy timer into the corresponding list, therefore we
remember which list we picked the last expired timeout from when
looking for it.
2016-12-20 12:40:26 -05:00

113 lines
3.2 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "Timeout.h"
#include "nsGlobalWindow.h"
#include "nsITimeoutHandler.h"
#include "nsITimer.h"
#include "mozilla/dom/TimeoutManager.h"
namespace mozilla {
namespace dom {
Timeout::Timeout()
: mCleared(false),
mRunning(false),
mIsInterval(false),
mReason(Reason::eTimeoutOrInterval),
mTimeoutId(0),
mInterval(0),
mFiringDepth(0),
mNestingLevel(0),
mPopupState(openAllowed)
{
MOZ_COUNT_CTOR(Timeout);
}
Timeout::~Timeout()
{
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
MOZ_COUNT_DTOR(Timeout);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(Timeout)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Timeout)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptHandler)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Timeout)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptHandler)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Timeout, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Timeout, Release)
namespace {
void
TimerCallback(nsITimer*, void* aClosure)
{
RefPtr<Timeout> timeout = (Timeout*)aClosure;
timeout->mWindow->AsInner()->TimeoutManager().RunTimeout(timeout);
}
void
TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf, size_t aLen)
{
RefPtr<Timeout> timeout = (Timeout*)aClosure;
const char* filename;
uint32_t lineNum, column;
timeout->mScriptHandler->GetLocation(&filename, &lineNum, &column);
snprintf(aBuf, aLen, "[content] %s:%u:%u", filename, lineNum, column);
}
} // anonymous namespace
nsresult
Timeout::InitTimer(nsIEventTarget* aTarget, uint32_t aDelay)
{
// If the given target does not match the timer's current target
// then we need to override it before the Init. Note that GetTarget()
// will return the current thread after setting the target to nullptr.
// So we need to special case the nullptr target comparison.
nsCOMPtr<nsIEventTarget> currentTarget;
MOZ_ALWAYS_SUCCEEDS(mTimer->GetTarget(getter_AddRefs(currentTarget)));
if ((aTarget && currentTarget != aTarget) ||
(!aTarget && currentTarget != NS_GetCurrentThread())) {
// Always call Cancel() in case we are re-using a timer. Otherwise
// the subsequent SetTarget() may fail.
MOZ_ALWAYS_SUCCEEDS(mTimer->Cancel());
MOZ_ALWAYS_SUCCEEDS(mTimer->SetTarget(aTarget));
}
return mTimer->InitWithNameableFuncCallback(
TimerCallback, this, aDelay, nsITimer::TYPE_ONE_SHOT, TimerNameCallback);
}
// Return true if this timeout has a refcount of aCount. This is used to check
// that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.
#ifdef DEBUG
bool
Timeout::HasRefCnt(uint32_t aCount) const
{
return mRefCnt.get() == aCount;
}
#endif // DEBUG
} // namespace dom
} // namespace mozilla