mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 23:08:39 +09:00
moebius#73: DOM - window.requestIdleCallback - improvements (basic)
https://github.com/MoonchildProductions/moebius/pull/73
This commit is contained in:
parent
ec18bcca97
commit
da2320b2ef
11 changed files with 558 additions and 200 deletions
|
|
@ -23,6 +23,7 @@
|
|||
#include "mozilla/dom/StorageEvent.h"
|
||||
#include "mozilla/dom/StorageEventBinding.h"
|
||||
#include "mozilla/dom/Timeout.h"
|
||||
#include "mozilla/dom/TimeoutHandler.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
|
||||
#include "mozilla/dom/WindowOrientationObserver.h"
|
||||
|
|
@ -552,29 +553,283 @@ DialogValueHolder::Get(JSContext* aCx, JS::Handle<JSObject*> aScope,
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::PostThrottledIdleCallback()
|
||||
class IdleRequestExecutor final : public nsIRunnable
|
||||
, public nsICancelableRunnable
|
||||
, public nsIIncrementalRunnable
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
public:
|
||||
explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
|
||||
: mDispatched(false)
|
||||
, mDeadline(TimeStamp::Now())
|
||||
, mWindow(aWindow)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mWindow);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mWindow->IsInnerWindow());
|
||||
}
|
||||
|
||||
if (mThrottledIdleRequestCallbacks.isEmpty())
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequestExecutor, nsIRunnable)
|
||||
|
||||
NS_DECL_NSIRUNNABLE
|
||||
nsresult Cancel() override;
|
||||
void SetDeadline(TimeStamp aDeadline) override;
|
||||
|
||||
void MaybeDispatch();
|
||||
private:
|
||||
~IdleRequestExecutor() {}
|
||||
|
||||
bool mDispatched;
|
||||
TimeStamp mDeadline;
|
||||
RefPtr<nsGlobalWindow> mWindow;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequestExecutor)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutor)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutor)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequestExecutor)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequestExecutor)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutor)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMETHODIMP
|
||||
IdleRequestExecutor::Run()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mDispatched = false;
|
||||
if (mWindow) {
|
||||
return mWindow->ExecuteIdleRequest(mDeadline);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
IdleRequestExecutor::Cancel()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mWindow = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
|
||||
// ownership transferred from mThrottledIdleRequestCallbacks to
|
||||
// mIdleRequestCallbacks
|
||||
mIdleRequestCallbacks.insertBack(request);
|
||||
mDeadline = aDeadline;
|
||||
}
|
||||
|
||||
void
|
||||
IdleRequestExecutor::MaybeDispatch()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mWindow);
|
||||
|
||||
if (mDispatched) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDispatched = true;
|
||||
RefPtr<IdleRequestExecutor> request = this;
|
||||
NS_IdleDispatchToCurrentThread(request.forget());
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsGlobalWindow::InsertIdleCallbackIntoList(IdleRequest* aRequest,
|
||||
IdleRequests& aList)
|
||||
class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
|
||||
{
|
||||
aList.insertBack(aRequest);
|
||||
public:
|
||||
explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor* aExecutor)
|
||||
: mExecutor(aExecutor)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestExecutorTimeoutHandler,
|
||||
TimeoutHandler)
|
||||
|
||||
nsresult Call() override
|
||||
{
|
||||
mExecutor->MaybeDispatch();
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
~IdleRequestExecutorTimeoutHandler() {}
|
||||
RefPtr<IdleRequestExecutor> mExecutor;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler, mExecutor)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
|
||||
NS_IMPL_RELEASE_INHERITED(IdleRequestExecutorTimeoutHandler, TimeoutHandler)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
|
||||
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
|
||||
|
||||
void
|
||||
nsGlobalWindow::ScheduleIdleRequestDispatch()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mIdleRequestCallbacks.isEmpty()) {
|
||||
if (mIdleRequestExecutor) {
|
||||
mIdleRequestExecutor->Cancel();
|
||||
mIdleRequestExecutor = nullptr;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mIdleRequestExecutor) {
|
||||
mIdleRequestExecutor = new IdleRequestExecutor(this);
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* outer = GetOuterWindow();
|
||||
if (outer && outer->AsOuter()->IsBackground()) {
|
||||
nsCOMPtr<nsITimeoutHandler> handler = new IdleRequestExecutorTimeoutHandler(mIdleRequestExecutor);
|
||||
int32_t dummy;
|
||||
// Set a timeout handler with a timeout of 0 ms to throttle idle
|
||||
// callback requests coming from a backround window using
|
||||
// background timeout throttling.
|
||||
SetTimeoutOrInterval(handler, 0, false,
|
||||
Timeout::Reason::eIdleCallbackTimeout, &dummy);
|
||||
return;
|
||||
}
|
||||
|
||||
mIdleRequestExecutor->MaybeDispatch();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::SuspendIdleRequests()
|
||||
{
|
||||
if (mIdleRequestExecutor) {
|
||||
mIdleRequestExecutor->Cancel();
|
||||
mIdleRequestExecutor = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::ResumeIdleRequests()
|
||||
{
|
||||
MOZ_ASSERT(!mIdleRequestExecutor);
|
||||
|
||||
ScheduleIdleRequestDispatch();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::InsertIdleCallback(IdleRequest* aRequest)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mIdleRequestCallbacks.insertBack(aRequest);
|
||||
aRequest->AddRef();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindow::RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aRequest->HasTimeout()) {
|
||||
ClearTimeoutOrInterval(aRequest->GetTimeoutHandle(),
|
||||
Timeout::Reason::eIdleCallbackTimeout);
|
||||
}
|
||||
|
||||
aRequest->removeFrom(mIdleRequestCallbacks);
|
||||
aRequest->Release();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::RunIdleRequest(IdleRequest* aRequest,
|
||||
DOMHighResTimeStamp aDeadline,
|
||||
bool aDidTimeout)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RefPtr<IdleRequest> request(aRequest);
|
||||
nsresult result = request->IdleRun(AsInner(), aDeadline, aDidTimeout);
|
||||
RemoveIdleCallback(request);
|
||||
return result;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
|
||||
|
||||
if (!request) {
|
||||
// There are no more idle requests, so stop scheduling idle
|
||||
// request callbacks.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
DOMHighResTimeStamp deadline = 0.0;
|
||||
|
||||
if (Performance* perf = GetPerformance()) {
|
||||
deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
|
||||
}
|
||||
|
||||
nsresult result = RunIdleRequest(request, deadline, false);
|
||||
|
||||
ScheduleIdleRequestDispatch();
|
||||
return result;
|
||||
}
|
||||
|
||||
class IdleRequestTimeoutHandler final : public TimeoutHandler
|
||||
{
|
||||
public:
|
||||
IdleRequestTimeoutHandler(JSContext* aCx,
|
||||
IdleRequest* aIdleRequest,
|
||||
nsPIDOMWindowInner* aWindow)
|
||||
: TimeoutHandler(aCx)
|
||||
, mIdleRequest(aIdleRequest)
|
||||
, mWindow(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IdleRequestTimeoutHandler,
|
||||
TimeoutHandler)
|
||||
|
||||
nsresult Call() override
|
||||
{
|
||||
return nsGlobalWindow::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
|
||||
}
|
||||
|
||||
private:
|
||||
~IdleRequestTimeoutHandler() {}
|
||||
|
||||
RefPtr<IdleRequest> mIdleRequest;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(IdleRequestTimeoutHandler,
|
||||
TimeoutHandler,
|
||||
mIdleRequest,
|
||||
mWindow)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
|
||||
NS_IMPL_RELEASE_INHERITED(IdleRequestTimeoutHandler, TimeoutHandler)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
|
||||
NS_INTERFACE_MAP_END_INHERITING(TimeoutHandler)
|
||||
|
||||
uint32_t
|
||||
nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
|
||||
IdleRequestCallback& aCallback,
|
||||
|
|
@ -584,33 +839,36 @@ nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
|
|||
MOZ_RELEASE_ASSERT(IsInnerWindow());
|
||||
AssertIsOnMainThread();
|
||||
|
||||
uint32_t handle = ++mIdleRequestCallbackCounter;
|
||||
uint32_t handle = mIdleRequestCallbackCounter++;
|
||||
|
||||
RefPtr<IdleRequest> request =
|
||||
new IdleRequest(aCx, AsInner(), aCallback, handle);
|
||||
new IdleRequest(&aCallback, handle);
|
||||
|
||||
if (aOptions.mTimeout.WasPassed()) {
|
||||
aError = request->SetTimeout(aOptions.mTimeout.Value());
|
||||
if (NS_WARN_IF(aError.Failed())) {
|
||||
int32_t timeoutHandle;
|
||||
nsCOMPtr<nsITimeoutHandler> handler(new IdleRequestTimeoutHandler(aCx, request, AsInner()));
|
||||
|
||||
nsresult rv = SetTimeoutOrInterval(
|
||||
handler, aOptions.mTimeout.Value(), false,
|
||||
Timeout::Reason::eIdleCallbackTimeout, &timeoutHandle);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
request->SetTimeoutHandle(timeoutHandle);
|
||||
}
|
||||
|
||||
nsGlobalWindow* outer = GetOuterWindowInternal();
|
||||
if (outer && outer->AsOuter()->IsBackground()) {
|
||||
// mThrottledIdleRequestCallbacks now owns request
|
||||
InsertIdleCallbackIntoList(request, mThrottledIdleRequestCallbacks);
|
||||
// If the list of idle callback requests is not empty it means that
|
||||
// we've already dispatched the first idle request. If we're
|
||||
// suspended we should only queue the idle callback and not schedule
|
||||
// it to run, that will be done in ResumeIdleRequest.
|
||||
bool needsScheduling = !IsSuspended() && mIdleRequestCallbacks.isEmpty();
|
||||
// mIdleRequestCallbacks now owns request
|
||||
InsertIdleCallback(request);
|
||||
|
||||
NS_DelayedDispatchToCurrentThread(
|
||||
NewRunnableMethod(this, &nsGlobalWindow::PostThrottledIdleCallback),
|
||||
10000);
|
||||
} else {
|
||||
MOZ_ASSERT(mThrottledIdleRequestCallbacks.isEmpty());
|
||||
|
||||
// mIdleRequestCallbacks now owns request
|
||||
InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
|
||||
|
||||
NS_IdleDispatchToCurrentThread(request.forget());
|
||||
if (needsScheduling) {
|
||||
ScheduleIdleRequestDispatch();
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
|
@ -623,7 +881,7 @@ nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
|
|||
|
||||
for (IdleRequest* r : mIdleRequestCallbacks) {
|
||||
if (r->Handle() == aHandle) {
|
||||
r->Cancel();
|
||||
RemoveIdleCallback(r);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -632,29 +890,17 @@ nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
|
|||
void
|
||||
nsGlobalWindow::DisableIdleCallbackRequests()
|
||||
{
|
||||
if (mIdleRequestExecutor) {
|
||||
mIdleRequestExecutor->Cancel();
|
||||
mIdleRequestExecutor = nullptr;
|
||||
}
|
||||
|
||||
while (!mIdleRequestCallbacks.isEmpty()) {
|
||||
RefPtr<IdleRequest> request = mIdleRequestCallbacks.popFirst();
|
||||
request->Cancel();
|
||||
}
|
||||
|
||||
while (!mThrottledIdleRequestCallbacks.isEmpty()) {
|
||||
RefPtr<IdleRequest> request = mThrottledIdleRequestCallbacks.popFirst();
|
||||
request->Cancel();
|
||||
RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
|
||||
RemoveIdleCallback(request);
|
||||
}
|
||||
}
|
||||
|
||||
void nsGlobalWindow::UnthrottleIdleCallbackRequests()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
while (!mThrottledIdleRequestCallbacks.isEmpty()) {
|
||||
RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
|
||||
mIdleRequestCallbacks.insertBack(request);
|
||||
NS_IdleDispatchToCurrentThread(request.forget());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
extern uint64_t
|
||||
|
|
@ -1306,6 +1552,7 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
mSerial(0),
|
||||
mIdleCallbackTimeoutCounter(1),
|
||||
mIdleRequestCallbackCounter(1),
|
||||
mIdleRequestExecutor(nullptr),
|
||||
#ifdef DEBUG
|
||||
mSetOpenerWindowCalled(false),
|
||||
#endif
|
||||
|
|
@ -2033,14 +2280,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingStorageEvents)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleRequestExecutor)
|
||||
for (IdleRequest* request : tmp->mIdleRequestCallbacks) {
|
||||
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
|
||||
}
|
||||
|
||||
for (IdleRequest* request : tmp->mThrottledIdleRequestCallbacks) {
|
||||
cb.NoteNativeChild(request, NS_CYCLE_COLLECTION_PARTICIPANT(IdleRequest));
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleObservers)
|
||||
|
||||
#ifdef MOZ_GAMEPAD
|
||||
|
|
@ -2147,6 +2391,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
|
|||
|
||||
tmp->UnlinkHostObjectURIs();
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
|
||||
tmp->DisableIdleCallbackRequests();
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
|
|
@ -10237,12 +10482,6 @@ void nsGlobalWindow::SetIsBackground(bool aIsBackground)
|
|||
ResetTimersForThrottleReduction(gMinBackgroundTimeoutValue);
|
||||
}
|
||||
|
||||
if (!aIsBackground) {
|
||||
nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
|
||||
if (inner) {
|
||||
inner->UnthrottleIdleCallbackRequests();
|
||||
}
|
||||
}
|
||||
#ifdef MOZ_GAMEPAD
|
||||
if (!aIsBackground) {
|
||||
nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
|
||||
|
|
@ -12036,6 +12275,8 @@ nsGlobalWindow::Suspend()
|
|||
|
||||
mozilla::dom::workers::SuspendWorkersForWindow(AsInner());
|
||||
|
||||
SuspendIdleRequests();
|
||||
|
||||
for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
|
||||
// Leave the timers with the current time remaining. This will
|
||||
// cause the timers to potentially fire when the window is
|
||||
|
|
@ -12146,6 +12387,8 @@ nsGlobalWindow::Resume()
|
|||
t->AddRef();
|
||||
}
|
||||
|
||||
ResumeIdleRequests();
|
||||
|
||||
// Resume all of the workers for this window. We must do this
|
||||
// after timeouts since workers may have queued events that can trigger
|
||||
// a setTimeout().
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue