1
0
mirror of https://github.com/roytam1/UXP.git synced 2026-05-26 13:58:49 +00:00

moebius#73: DOM - window.requestIdleCallback - improvements (basic)

https://github.com/MoonchildProductions/moebius/pull/73
This commit is contained in:
janekptacijarabaci
2018-04-18 15:15:49 +02:00
committed by Roy Tam
parent ec18bcca97
commit da2320b2ef
11 changed files with 562 additions and 204 deletions
+20 -103
View File
@@ -9,7 +9,6 @@
#include "mozilla/Function.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/IdleDeadline.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceTiming.h"
#include "mozilla/dom/WindowBinding.h"
#include "nsComponentManagerUtils.h"
@@ -20,138 +19,56 @@
namespace mozilla {
namespace dom {
IdleRequest::IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
IdleRequestCallback& aCallback, uint32_t aHandle)
: mWindow(aWindow)
, mCallback(&aCallback)
IdleRequest::IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle)
: mCallback(aCallback)
, mHandle(aHandle)
, mTimeoutHandle(Nothing())
{
MOZ_ASSERT(aWindow);
// Get the calling location.
nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
MOZ_DIAGNOSTIC_ASSERT(mCallback);
}
IdleRequest::~IdleRequest()
{
}
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
NS_IMPL_CYCLE_COLLECTION(IdleRequest, mCallback)
NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IdleRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequest)
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
NS_INTERFACE_MAP_ENTRY(nsIIncrementalRunnable)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimeoutHandler)
NS_INTERFACE_MAP_END
nsresult
IdleRequest::SetTimeout(uint32_t aTimeout)
{
int32_t handle;
nsresult rv = nsGlobalWindow::Cast(mWindow)->SetTimeoutOrInterval(
this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
mTimeoutHandle = Some(handle);
return rv;
}
nsresult
IdleRequest::Run()
{
if (mCallback) {
RunIdleRequestCallback(false);
}
return NS_OK;
}
nsresult
IdleRequest::Cancel()
{
mCallback = nullptr;
CancelTimeout();
if (isInList()) {
remove();
}
Release();
return NS_OK;
}
void
IdleRequest::SetDeadline(TimeStamp aDeadline)
IdleRequest::SetTimeoutHandle(int32_t aHandle)
{
mozilla::dom::Performance* perf = mWindow->GetPerformance();
mDeadline =
perf ? perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline) : 0.0;
mTimeoutHandle = Some(aHandle);
}
int32_t
IdleRequest::GetTimeoutHandle() const
{
MOZ_DIAGNOSTIC_ASSERT(mTimeoutHandle.isSome());
return mTimeoutHandle.value();
}
nsresult
IdleRequest::RunIdleRequestCallback(bool aDidTimeout)
IdleRequest::IdleRun(nsPIDOMWindowInner* aWindow,
DOMHighResTimeStamp aDeadline,
bool aDidTimeout)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_DIAGNOSTIC_ASSERT(mCallback);
if (!aDidTimeout) {
CancelTimeout();
}
remove();
ErrorResult error;
RefPtr<IdleDeadline> deadline =
new IdleDeadline(mWindow, aDidTimeout, mDeadline);
new IdleDeadline(aWindow, aDidTimeout, aDeadline);
mCallback->Call(*deadline, error, "requestIdleCallback handler");
mCallback = nullptr;
Release();
error.SuppressException();
return error.StealNSResult();
}
void
IdleRequest::CancelTimeout()
{
if (mTimeoutHandle.isSome()) {
nsGlobalWindow::Cast(mWindow)->ClearTimeoutOrInterval(
mTimeoutHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
}
}
nsresult
IdleRequest::Call()
{
SetDeadline(TimeStamp::Now());
return RunIdleRequestCallback(true);
}
void
IdleRequest::GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn)
{
*aFileName = mFileName.get();
*aLineNo = mLineNo;
*aColumn = mColumn;
}
void
IdleRequest::MarkForCC()
{
mCallback->MarkForCC();
}
} // namespace dom
} // namespace mozilla
+11 -29
View File
@@ -12,7 +12,6 @@
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMNavigationTiming.h"
#include "nsITimeoutHandler.h"
class nsPIDOMWindowInner;
@@ -21,28 +20,19 @@ namespace dom {
class IdleRequestCallback;
class IdleRequest final : public nsITimeoutHandler
, public nsIRunnable
, public nsICancelableRunnable
, public nsIIncrementalRunnable
, public LinkedListElement<IdleRequest>
class IdleRequest final : public nsISupports,
public LinkedListElement<IdleRequest>
{
public:
IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
IdleRequestCallback& aCallback, uint32_t aHandle);
IdleRequest(IdleRequestCallback* aCallback, uint32_t aHandle);
virtual nsresult Call() override;
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn) override;
virtual void MarkForCC() override;
nsresult IdleRun(nsPIDOMWindowInner* aWindow,
DOMHighResTimeStamp aDeadline,
bool aDidTimeout);
nsresult SetTimeout(uint32_t aTimout);
nsresult RunIdleRequestCallback(bool aDidTimeout);
void CancelTimeout();
NS_DECL_NSIRUNNABLE;
virtual nsresult Cancel() override;
virtual void SetDeadline(mozilla::TimeStamp aDeadline) override;
void SetTimeoutHandle(int32_t aHandle);
bool HasTimeout() const { return mTimeoutHandle.isSome(); }
uint32_t GetTimeoutHandle() const;
uint32_t Handle() const
{
@@ -50,22 +40,14 @@ public:
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IdleRequest, nsITimeoutHandler)
NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequest)
private:
~IdleRequest();
// filename, line number and JS language version string of the
// caller of setTimeout()
nsCString mFileName;
uint32_t mLineNo;
uint32_t mColumn;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
RefPtr<IdleRequestCallback> mCallback;
uint32_t mHandle;
const uint32_t mHandle;
mozilla::Maybe<int32_t> mTimeoutHandle;
DOMHighResTimeStamp mDeadline;
};
} // namespace dom
+7 -1
View File
@@ -41,7 +41,11 @@ public:
// default main thread being used.
nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
enum class Reason
{
eTimeoutOrInterval,
eIdleCallbackTimeout,
};
#ifdef DEBUG
bool HasRefCntOne() const;
@@ -62,6 +66,8 @@ public:
// True if this is a repeating/interval timer
bool mIsInterval;
// Used to allow several reasons for setting a timeout, where each
// 'Reason' value is using a possibly overlapping set of id:s.
Reason mReason;
// Returned as value of setTimeout()
+43
View File
@@ -0,0 +1,43 @@
/* -*- 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/. */
#include "TimeoutHandler.h"
namespace mozilla {
namespace dom {
TimeoutHandler::TimeoutHandler(JSContext* aCx)
: TimeoutHandler()
{
nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
}
nsresult
TimeoutHandler::Call()
{
return NS_OK;
}
void
TimeoutHandler::GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn)
{
*aFileName = mFileName.get();
*aLineNo = mLineNo;
*aColumn = mColumn;
}
NS_IMPL_CYCLE_COLLECTION_0(TimeoutHandler)
NS_IMPL_CYCLE_COLLECTING_ADDREF(TimeoutHandler)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TimeoutHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TimeoutHandler)
NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
NS_INTERFACE_MAP_END
} // namespace dom
} // namespace mozilla
+50
View File
@@ -0,0 +1,50 @@
/* -*- 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/. */
#ifndef mozilla_dom_timeout_handler_h
#define mozilla_dom_timeout_handler_h
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "nsITimeoutHandler.h"
namespace mozilla {
namespace dom {
/**
* Utility class for implementing nsITimeoutHandlers, designed to be subclassed.
*/
class TimeoutHandler : public nsITimeoutHandler
{
public:
// TimeoutHandler doesn't actually contain cycles, but subclasses
// probably will.
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(TimeoutHandler)
virtual nsresult Call() override;
virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
uint32_t* aColumn) override;
virtual void MarkForCC() override {}
protected:
TimeoutHandler() : mFileName(""), mLineNo(0), mColumn(0) {}
explicit TimeoutHandler(JSContext *aCx);
virtual ~TimeoutHandler() {}
private:
TimeoutHandler(const TimeoutHandler&) = delete;
TimeoutHandler& operator=(const TimeoutHandler&) = delete;
TimeoutHandler& operator=(const TimeoutHandler&&) = delete;
nsCString mFileName;
uint32_t mLineNo;
uint32_t mColumn;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_timeout_handler_h
+2
View File
@@ -217,6 +217,7 @@ EXPORTS.mozilla.dom += [
'TabGroup.h',
'Text.h',
'Timeout.h',
'TimeoutHandler.h',
'TreeWalker.h',
'WebKitCSSMatrix.h',
'WebSocket.h',
@@ -364,6 +365,7 @@ UNIFIED_SOURCES += [
'TextInputProcessor.cpp',
'ThirdPartyUtil.cpp',
'Timeout.cpp',
'TimeoutHandler.cpp',
'TimerClamping.cpp',
'TreeWalker.cpp',
'WebKitCSSMatrix.cpp',
+303 -60
View File
@@ -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().
+20 -11
View File
@@ -102,6 +102,8 @@ struct nsRect;
class nsWindowSizes;
class IdleRequestExecutor;
namespace mozilla {
class DOMEventTargetHelper;
class ThrottledEventQueue;
@@ -118,6 +120,7 @@ class Gamepad;
enum class ImageBitmapFormat : uint32_t;
class IdleRequest;
class IdleRequestCallback;
class IncrementalRunnable;
class Location;
class MediaQueryList;
class MozSelfSupport;
@@ -1097,7 +1100,6 @@ public:
mozilla::ErrorResult& aError);
void CancelIdleCallback(uint32_t aHandle);
#ifdef MOZ_WEBSPEECH
mozilla::dom::SpeechSynthesis*
GetSpeechSynthesis(mozilla::ErrorResult& aError);
@@ -1762,6 +1764,21 @@ private:
mozilla::dom::TabGroup* TabGroupInner();
mozilla::dom::TabGroup* TabGroupOuter();
public:
void DisableIdleCallbackRequests();
uint32_t IdleRequestHandle() const { return mIdleRequestCallbackCounter; }
nsresult RunIdleRequest(mozilla::dom::IdleRequest* aRequest,
DOMHighResTimeStamp aDeadline, bool aDidTimeout);
nsresult ExecuteIdleRequest(TimeStamp aDeadline);
void ScheduleIdleRequestDispatch();
void SuspendIdleRequests();
void ResumeIdleRequests();
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
void InsertIdleCallback(mozilla::dom::IdleRequest* aRequest);
void RemoveIdleCallback(mozilla::dom::IdleRequest* aRequest);
protected:
// These members are only used on outer window objects. Make sure
// you never set any of these on an inner object!
@@ -1912,21 +1929,12 @@ protected:
uint32_t mSerial;
void DisableIdleCallbackRequests();
void UnthrottleIdleCallbackRequests();
void PostThrottledIdleCallback();
typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
IdleRequests& aList);
// The current idle request callback timeout handle
uint32_t mIdleCallbackTimeoutCounter;
// The current idle request callback handle
uint32_t mIdleRequestCallbackCounter;
IdleRequests mIdleRequestCallbacks;
IdleRequests mThrottledIdleRequestCallbacks;
RefPtr<IdleRequestExecutor> mIdleRequestExecutor;
#ifdef DEBUG
bool mSetOpenerWindowCalled;
@@ -2002,6 +2010,7 @@ protected:
friend class nsDOMWindowUtils;
friend class mozilla::dom::PostMessageEvent;
friend class DesktopNotification;
friend class IdleRequestExecutor;
static WindowByIdTable* sWindowsById;
static bool sWarnedAboutWindowInternal;
+6
View File
@@ -38674,6 +38674,12 @@
"url": "/html/webappapis/idle-callbacks/callback-multiple-calls.html"
}
],
"html/webappapis/idle-callbacks/callback-suspended.html": [
{
"path": "html/webappapis/idle-callbacks/callback-suspended.html",
"url": "/html/webappapis/idle-callbacks/callback-suspended.html"
}
],
"html/webappapis/idle-callbacks/callback-timeout.html": [
{
"path": "html/webappapis/idle-callbacks/callback-timeout.html",
@@ -0,0 +1,93 @@
<!doctype html>
<meta charset=utf-8>
<title>Dispatching idle callbacks should be able to be suspended and then resumed</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div id="log"></div>
<script>
function withEventListener(target, event, handler) {
handler = handler || (e => e);
return new Promise(resolve => {
let wrapper = function(e) {
let result = handler(e);
if (!result) {
return;
}
resolve(result);
}
target.addEventListener(event, wrapper, { once: true });
});
}
function makePostBackUrl(name) {
return new URL('resources/post_name_on_load.html?name=' + name,
window.location).href;
}
function waitForMessage(message, handler) {
return withEventListener(window, 'message', e => (e.data === message) && handler(e));;
}
function withWindow(name) {
let win = window.open(makePostBackUrl(name))
return waitForMessage(name, _ => win);
}
function navigateWindow(win, name) {
win.location = makePostBackUrl(name);
return waitForMessage(name, _ => win);
}
function waitDuration(delay) {
return new Promise(resolve => {
setTimeout(resolve, delay);
})
}
function goBack(win) {
var p = withEventListener(win, 'pagehide');
win.history.back();
return p;
}
promise_test(t => {
let idleCalled = false;
let running = true;
return withWindow('foo')
.then(win => {
let callback = function(d) {
idleCalled = true;
if (running) {
win.requestIdleCallback(callback);
}
};
win.requestIdleCallback(callback);
return navigateWindow(win, 'bar')
.then(_ => idleCalled = false)
.then(_ => waitDuration(2000))
.then(_ => {
assert_false(idleCalled, "idle callback shouldn't have been called yet");
return goBack(win);
})
.then(_ => Promise.race([
// At this point it's a matter of having bfcache ...
waitDuration(2000)
.then(_ => {
assert_true(idleCalled, "idle callback should've been called by now");
running = false;
}),
// ... or not. If not, we expect a load event.
waitForMessage("foo")
]))
.then(_ => win.close())
.catch(e => {
win.close();
throw e;
})
});
});
</script>
@@ -0,0 +1,7 @@
<!doctype html>
<script>
addEventListener('load', _ => {
let params = new URLSearchParams(window.location.search);
window.opener.postMessage(params.get('name'), '*');
});
</script>