--- a/dom/base/IdleRequest.cpp
+++ b/dom/base/IdleRequest.cpp
@@ -3,154 +3,66 @@
/* 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 "IdleRequest.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"
#include "nsGlobalWindow.h"
#include "nsISupportsPrimitives.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
-IdleRequest::IdleRequest(JSContext* aCx, nsPIDOMWindowInner* aWindow,
+IdleRequest::IdleRequest(nsPIDOMWindowInner* aWindow,
IdleRequestCallback& aCallback, uint32_t aHandle)
- : mWindow(aWindow)
- , mCallback(&aCallback)
- , mHandle(aHandle)
- , mTimeoutHandle(Nothing())
-{
- MOZ_ASSERT(aWindow);
-
- // Get the calling location.
- nsJSUtils::GetCallingLocation(aCx, mFileName, &mLineNo, &mColumn);
-}
+ : mCallback(&aCallback), mHandle(aHandle), mTimeoutHandle(Nothing()) {}
IdleRequest::~IdleRequest()
{
}
NS_IMPL_CYCLE_COLLECTION_CLASS(IdleRequest)
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_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
-nsresult
-IdleRequest::SetTimeout(uint32_t aTimeout)
+void
+IdleRequest::SetTimeoutHandle(int32_t aHandle)
{
- 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;
+ mTimeoutHandle = Some(aHandle);
}
nsresult
-IdleRequest::Cancel()
-{
- mCallback = nullptr;
- CancelTimeout();
- if (isInList()) {
- remove();
- }
- Release();
-
- return NS_OK;
-}
-
-void
-IdleRequest::SetDeadline(TimeStamp aDeadline)
-{
- mozilla::dom::Performance* perf = mWindow->GetPerformance();
- mDeadline =
- perf ? perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline) : 0.0;
-}
-
-nsresult
-IdleRequest::RunIdleRequestCallback(bool aDidTimeout)
+IdleRequest::Run(nsPIDOMWindowInner* aWindow, DOMHighResTimeStamp aDeadline,
+ bool aDidTimeout)
{
MOZ_ASSERT(NS_IsMainThread());
+ MOZ_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();
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
--- a/dom/base/IdleRequest.h
+++ b/dom/base/IdleRequest.h
@@ -7,68 +7,50 @@
#ifndef mozilla_dom_idlerequest_h
#define mozilla_dom_idlerequest_h
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsDOMNavigationTiming.h"
-#include "nsITimeoutHandler.h"
class nsPIDOMWindowInner;
namespace mozilla {
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);
-
- virtual nsresult Call() override;
- virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
- uint32_t* aColumn) override;
- virtual void MarkForCC() override;
+ IdleRequest(nsPIDOMWindowInner* aWindow, IdleRequestCallback& aCallback,
+ uint32_t aHandle);
- nsresult SetTimeout(uint32_t aTimout);
- nsresult RunIdleRequestCallback(bool aDidTimeout);
- void CancelTimeout();
+ nsresult Run(nsPIDOMWindowInner* aWindow, DOMHighResTimeStamp aDeadline,
+ bool aDidTimeout);
- 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 Handle() const
{
return mHandle;
}
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;
mozilla::Maybe<int32_t> mTimeoutHandle;
- DOMHighResTimeStamp mDeadline;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_idlerequest_h
new file mode 100644
--- /dev/null
+++ b/dom/base/RunnableTimeoutHandler.cpp
@@ -0,0 +1,54 @@
+/* -*- 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 "RunnableTimeoutHandler.h"
+
+#include "nsIRunnable.h"
+
+namespace mozilla {
+namespace dom {
+
+RunnableTimeoutHandler::RunnableTimeoutHandler(
+ already_AddRefed<nsIRunnable> aRunnable)
+ : mRunnable(Move(aRunnable))
+{
+ MOZ_ASSERT(mRunnable);
+}
+
+nsresult
+RunnableTimeoutHandler::Call()
+{
+ return mRunnable->Run();
+}
+
+void
+RunnableTimeoutHandler::GetLocation(const char** aFileName, uint32_t* aLineNo,
+ uint32_t* aColumn)
+{
+ *aFileName = "";
+ *aLineNo = 0;
+ *aColumn = 0;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(RunnableTimeoutHandler)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(RunnableTimeoutHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(RunnableTimeoutHandler)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(RunnableTimeoutHandler)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mRunnable)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(RunnableTimeoutHandler)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRunnable)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RunnableTimeoutHandler)
+ NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+NS_INTERFACE_MAP_END
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/RunnableTimeoutHandler.h
@@ -0,0 +1,45 @@
+/* -*- 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_runnable_timeout_handler_h
+#define mozilla_dom_runnable_timeout_handler_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsITimeoutHandler.h"
+
+class nsIRunnable;
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * RunnableTimeoutHandler wraps a regular nsIRunnable to allow
+ * executing runnables in the same way as one would execute a timeout
+ * from nsGlobalWindow::SetTimeoutOrInterval.
+ */
+class RunnableTimeoutHandler final : public nsITimeoutHandler
+{
+public:
+ explicit RunnableTimeoutHandler(already_AddRefed<nsIRunnable> aRunnable);
+ virtual nsresult Call() override;
+ virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
+ uint32_t* aColumn) override;
+ virtual void MarkForCC() override {}
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(RunnableTimeoutHandler)
+
+private:
+ ~RunnableTimeoutHandler() {}
+ nsCOMPtr<nsIRunnable> mRunnable;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_runnable_timeout_handler_h
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -37,17 +37,21 @@ public:
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
// The target may be specified to use a particular event queue for the
// resulting timer runnable. A nullptr target will result in the
// default main thread being used.
nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
- enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
+ enum class Reason
+ {
+ eTimeoutOrInterval,
+ eIdleRequestTimeoutHandler,
+ };
#ifdef DEBUG
bool HasRefCntOne() const;
#endif // DEBUG
// Window for which this timeout fires
RefPtr<nsGlobalWindow> mWindow;
@@ -58,16 +62,18 @@ public:
bool mCleared;
// True if this is one of the timeouts that are currently running
bool mRunning;
// 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()
uint32_t mTimeoutId;
// Interval in milliseconds
uint32_t mInterval;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -190,16 +190,17 @@ EXPORTS.mozilla.dom += [
'Navigator.h',
'NodeInfo.h',
'NodeInfoInlines.h',
'NodeIterator.h',
'PartialSHistory.h',
'Pose.h',
'ProcessGlobal.h',
'ResponsiveImageSelector.h',
+ 'RunnableTimeoutHandler.h',
'SameProcessMessageQueue.h',
'ScreenOrientation.h',
'ScriptSettings.h',
'ShadowRoot.h',
'StructuredCloneHolder.h',
'StructuredCloneTags.h',
'StyleSheetList.h',
'SubtleCrypto.h',
@@ -328,16 +329,17 @@ UNIFIED_SOURCES += [
'nsXHTMLContentSerializer.cpp',
'nsXMLContentSerializer.cpp',
'nsXMLNameSpaceMap.cpp',
'PartialSHistory.cpp',
'Pose.cpp',
'PostMessageEvent.cpp',
'ProcessGlobal.cpp',
'ResponsiveImageSelector.cpp',
+ 'RunnableTimeoutHandler.cpp',
'SameProcessMessageQueue.cpp',
'ScreenOrientation.cpp',
'ScriptSettings.cpp',
'ShadowRoot.cpp',
'StructuredCloneHolder.cpp',
'StyleSheetList.cpp',
'SubtleCrypto.cpp',
'TabGroup.cpp',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -15,16 +15,17 @@
#include "nsContentSecurityManager.h"
#include "nsScreen.h"
#include "nsHistory.h"
#include "nsDOMNavigationTiming.h"
#include "nsIDOMStorageManager.h"
#include "mozilla/dom/DOMStorage.h"
#include "mozilla/dom/IdleRequest.h"
#include "mozilla/dom/Performance.h"
+#include "mozilla/dom/RunnableTimeoutHandler.h"
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
#include "mozilla/dom/Timeout.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
#include "mozilla/dom/WindowOrientationObserver.h"
#endif
#include "nsDOMOfflineResourceList.h"
@@ -547,118 +548,267 @@ DialogValueHolder::Get(JSContext* aCx, J
if (aSubject->Subsumes(mOrigin)) {
aError = nsContentUtils::XPConnect()->VariantToJS(aCx, aScope,
mValue, aResult);
} else {
aResult.setUndefined();
}
}
-void
-nsGlobalWindow::PostThrottledIdleCallback()
+class IdleRequestExecutor final : public nsIRunnable
+ , public nsICancelableRunnable
+ , public nsIIncrementalRunnable
+{
+public:
+ explicit IdleRequestExecutor(nsGlobalWindow* aWindow)
+ : mDispatched(false)
+ , mDeadline(TimeStamp::Now())
+ , mWindow(aWindow)
+ {
+ MOZ_ASSERT(aWindow->IsInnerWindow());
+ }
+
+ 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 Dispatch();
+private:
+ ~IdleRequestExecutor() {
+ mWindow = nullptr;
+ }
+
+ 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_METHOD
+IdleRequestExecutor::Run()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mDispatched = false;
+ if (mWindow) {
+ return mWindow->ExecuteIdleRequest(mDeadline);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+IdleRequestExecutor::Cancel()
+{
+ mWindow = nullptr;
+ return NS_OK;
+}
+
+void
+IdleRequestExecutor::SetDeadline(TimeStamp aDeadline)
+{
+ if (!mWindow) {
+ return;
+ }
+
+ mDeadline = aDeadline;
+}
+
+void
+IdleRequestExecutor::Dispatch()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!mDispatched) {
+ mDispatched = true;
+ RefPtr<IdleRequestExecutor> request = this;
+ NS_IdleDispatchToCurrentThread(request.forget());
+ }
+}
+
+void
+nsGlobalWindow::ScheduleIdleRequestDispatch()
{
AssertIsOnMainThread();
- if (mThrottledIdleRequestCallbacks.isEmpty())
- return;
-
- RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
- // ownership transferred from mThrottledIdleRequestCallbacks to
- // mIdleRequestCallbacks
- mIdleRequestCallbacks.insertBack(request);
- NS_IdleDispatchToCurrentThread(request.forget());
+ if (mIdleRequestCallbacks.isEmpty()) {
+ if (mIdleRequestExecutor) {
+ mIdleRequestExecutor->Cancel();
+ mIdleRequestExecutor = nullptr;
+ }
+
+ return;
+ }
+
+ if (!mIdleRequestExecutor) {
+ mIdleRequestExecutor = new IdleRequestExecutor(this);
+ }
+
+ nsPIDOMWindowOuter* outer = GetOuterWindow();
+ if (outer && outer->AsOuter()->IsBackground()) {
+ int32_t dummy;
+ RefPtr<RunnableTimeoutHandler> timeout(new RunnableTimeoutHandler(
+ NewRunnableMethod(mIdleRequestExecutor, &IdleRequestExecutor::Dispatch)));
+ SetTimeoutOrInterval(timeout, 0, false,
+ Timeout::Reason::eIdleRequestTimeoutHandler, &dummy);
+
+ return;
+ }
+
+ mIdleRequestExecutor->Dispatch();
}
/* static */ void
nsGlobalWindow::InsertIdleCallbackIntoList(IdleRequest* aRequest,
IdleRequests& aList)
{
+ AssertIsOnMainThread();
aList.insertBack(aRequest);
aRequest->AddRef();
}
+/* static */ void
+nsGlobalWindow::RemoveIdleCallbackFromList(mozilla::dom::IdleRequest* aRequest,
+ IdleRequests& aList)
+{
+ AssertIsOnMainThread();
+ aRequest->removeFrom(aList);
+ aRequest->Release();
+}
+
+nsresult
+nsGlobalWindow::RunIdleRequest(IdleRequest* aRequest,
+ DOMHighResTimeStamp aDeadline, bool aDidTimeout)
+{
+ AssertIsOnMainThread();
+ RefPtr<IdleRequest> request(aRequest);
+ nsresult result = request->Run(AsInner(), aDeadline, aDidTimeout);
+ RemoveIdleCallbackFromList(request, mIdleRequestCallbacks);
+ return result;
+}
+
+nsresult
+nsGlobalWindow::ExecuteIdleRequest(TimeStamp aDeadline)
+{
+ AssertIsOnMainThread();
+ RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
+
+ if (!request) {
+ return NS_OK;
+ }
+
+ if (request->HasTimeout()) {
+ ClearTimeoutOrInterval(request->Handle(),
+ Timeout::Reason::eIdleRequestTimeoutHandler);
+ }
+
+ DOMHighResTimeStamp deadline = 0.0;
+
+ if (Performance* perf = GetPerformance()) {
+ deadline = perf->GetDOMTiming()->TimeStampToDOMHighRes(aDeadline);
+ }
+
+ nsresult result = RunIdleRequest(request, deadline, false);
+
+ ScheduleIdleRequestDispatch();
+ return result;
+}
+
uint32_t
-nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
- IdleRequestCallback& aCallback,
+nsGlobalWindow::RequestIdleCallback(IdleRequestCallback& aCallback,
const IdleRequestOptions& aOptions,
ErrorResult& aError)
{
MOZ_RELEASE_ASSERT(IsInnerWindow());
AssertIsOnMainThread();
- uint32_t handle = ++mIdleRequestCallbackCounter;
+ uint32_t handle = mIdleRequestCallbackCounter++;
RefPtr<IdleRequest> request =
- new IdleRequest(aCx, AsInner(), aCallback, handle);
+ new IdleRequest(AsInner(), aCallback, handle);
if (aOptions.mTimeout.WasPassed()) {
- aError = request->SetTimeout(aOptions.mTimeout.Value());
- if (NS_WARN_IF(aError.Failed())) {
+ int32_t timeoutHandle;
+ RefPtr<RunnableTimeoutHandler> timeout(new RunnableTimeoutHandler(
+ NewRunnableMethod<IdleRequest*, DOMHighResTimeStamp, bool>(
+ this, &nsGlobalWindow::RunIdleRequest, request, 0.0, true)));
+
+ nsresult rv = SetTimeoutOrInterval(
+ timeout, aOptions.mTimeout.Value(), false,
+ Timeout::Reason::eIdleRequestTimeoutHandler, &timeoutHandle);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
return 0;
}
- }
-
- nsGlobalWindow* outer = GetOuterWindowInternal();
- if (outer && outer->AsOuter()->IsBackground()) {
- // mThrottledIdleRequestCallbacks now owns request
- InsertIdleCallbackIntoList(request, mThrottledIdleRequestCallbacks);
-
- NS_DelayedDispatchToCurrentThread(
- NewRunnableMethod(this, &nsGlobalWindow::PostThrottledIdleCallback),
- 10000);
- } else {
- MOZ_ASSERT(mThrottledIdleRequestCallbacks.isEmpty());
-
- // mIdleRequestCallbacks now owns request
- InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
-
- NS_IdleDispatchToCurrentThread(request.forget());
+
+ request->SetTimeoutHandle(timeoutHandle);
+ }
+
+ // If the list of idle callback requests is not empty it means that
+ // we've already dispatched the first idle request. It is the
+ // responsibility of that to dispatch the next.
+ bool needsScheduling = mIdleRequestCallbacks.isEmpty();
+ // mIdleRequestCallbacks now owns request
+ InsertIdleCallbackIntoList(request, mIdleRequestCallbacks);
+
+ if (needsScheduling) {
+ ScheduleIdleRequestDispatch();
}
return handle;
}
void
nsGlobalWindow::CancelIdleCallback(uint32_t aHandle)
{
MOZ_RELEASE_ASSERT(IsInnerWindow());
for (IdleRequest* r : mIdleRequestCallbacks) {
if (r->Handle() == aHandle) {
- r->Cancel();
+ RemoveIdleCallbackFromList(r, mIdleRequestCallbacks);
break;
}
}
}
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();
- }
-}
-
-void nsGlobalWindow::UnthrottleIdleCallbackRequests()
-{
- AssertIsOnMainThread();
-
- while (!mThrottledIdleRequestCallbacks.isEmpty()) {
- RefPtr<IdleRequest> request(mThrottledIdleRequestCallbacks.popFirst());
- mIdleRequestCallbacks.insertBack(request);
- NS_IdleDispatchToCurrentThread(request.forget());
- }
-}
-
+ RefPtr<IdleRequest> request = mIdleRequestCallbacks.getFirst();
+ RemoveIdleCallbackFromList(request, mIdleRequestCallbacks);
+ }
+}
namespace mozilla {
namespace dom {
extern uint64_t
NextWindowID();
} // namespace dom
} // namespace mozilla
@@ -1265,18 +1415,18 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
mTimeoutInsertionPoint(nullptr),
mTimeoutIdCounter(1),
mTimeoutFiringDepth(0),
mSuspendDepth(0),
mFreezeDepth(0),
mBackPressureDelayMS(0),
mFocusMethod(0),
mSerial(0),
- mIdleCallbackTimeoutCounter(1),
mIdleRequestCallbackCounter(1),
+ mIdleRequestExecutor(nullptr),
#ifdef DEBUG
mSetOpenerWindowCalled(false),
#endif
#ifdef MOZ_B2G
mNetworkUploadObserverEnabled(false),
mNetworkDownloadObserverEnabled(false),
#endif
mCleanedUp(false),
@@ -1978,24 +2128,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedDoc)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentPrincipal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDoc)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIdleService)
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
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
@@ -2092,16 +2239,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMozSelfSupport)
tmp->UnlinkHostObjectURIs();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
tmp->DisableIdleCallbackRequests();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
#ifdef DEBUG
void
nsGlobalWindow::RiskyUnlink()
@@ -10139,22 +10287,16 @@ void nsGlobalWindow::SetIsBackground(boo
MOZ_ASSERT(IsOuterWindow());
bool resetTimers = (!aIsBackground && AsOuter()->IsBackground());
nsPIDOMWindow::SetIsBackground(aIsBackground);
if (resetTimers) {
ResetTimersForThrottleReduction(gMinBackgroundTimeoutValue);
}
- if (!aIsBackground) {
- nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
- if (inner) {
- inner->UnthrottleIdleCallbackRequests();
- }
- }
#ifdef MOZ_GAMEPAD
if (!aIsBackground) {
nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
if (inner) {
inner->SyncGamepadState();
}
}
#endif
@@ -12579,21 +12721,23 @@ nsGlobalWindow::OpenInternal(const nsASt
//*****************************************************************************
uint32_t sNestingLevel;
uint32_t
nsGlobalWindow::GetTimeoutId(Timeout::Reason aReason)
{
switch (aReason) {
- case Timeout::Reason::eIdleCallbackTimeout:
- return ++mIdleCallbackTimeoutCounter;
case Timeout::Reason::eTimeoutOrInterval:
+ return ++mTimeoutIdCounter;
+ case Timeout::Reason::eIdleRequestTimeoutHandler:
+ return ++mIdleRequestTimeoutHandlerCounter;
default:
- return ++mTimeoutIdCounter;
+ MOZ_ASSERT_UNREACHABLE("Unhandled timeout reason");
+ return 0;
}
}
nsGlobalWindow*
nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError)
{
nsGlobalWindow* currentInner;
nsGlobalWindow* forwardTo;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -96,32 +96,35 @@ class nsHistory;
class nsGlobalWindowObserver;
class nsGlobalWindow;
class nsDOMWindowUtils;
class nsIIdleService;
struct nsRect;
class nsWindowSizes;
+class IdleRequestExecutor;
+
namespace mozilla {
class DOMEventTargetHelper;
class ThrottledEventQueue;
namespace dom {
class BarProp;
struct ChannelPixelLayout;
class Console;
class Crypto;
class CustomElementRegistry;
class DocGroup;
class External;
class Function;
class Gamepad;
enum class ImageBitmapFormat : uint32_t;
class IdleRequest;
class IdleRequestCallback;
+class IncrementalRunnable;
class Location;
class MediaQueryList;
class MozSelfSupport;
class Navigator;
class OwningExternalOrWindowProxy;
class Promise;
class PostMessageEvent;
struct RequestInit;
@@ -1072,23 +1075,21 @@ public:
void GetOuterHeight(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
mozilla::ErrorResult& aError);
void SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
mozilla::ErrorResult& aError);
int32_t RequestAnimationFrame(mozilla::dom::FrameRequestCallback& aCallback,
mozilla::ErrorResult& aError);
void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
- uint32_t RequestIdleCallback(JSContext* aCx,
- mozilla::dom::IdleRequestCallback& aCallback,
+ uint32_t RequestIdleCallback(mozilla::dom::IdleRequestCallback& aCallback,
const mozilla::dom::IdleRequestOptions& aOptions,
mozilla::ErrorResult& aError);
void CancelIdleCallback(uint32_t aHandle);
-
#ifdef MOZ_WEBSPEECH
mozilla::dom::SpeechSynthesis*
GetSpeechSynthesis(mozilla::ErrorResult& aError);
bool HasActiveSpeechSynthesis();
#endif
already_AddRefed<nsICSSDeclaration>
GetDefaultComputedStyle(mozilla::dom::Element& aElt,
const nsAString& aPseudoElt,
@@ -1728,16 +1729,30 @@ public:
// Dispatch a runnable related to the global.
virtual nsresult Dispatch(const char* aName,
mozilla::dom::TaskCategory aCategory,
already_AddRefed<nsIRunnable>&& aRunnable) override;
virtual already_AddRefed<nsIEventTarget>
EventTargetFor(mozilla::dom::TaskCategory aCategory) const override;
+ void DisableIdleCallbackRequests();
+ uint32_t IdleRequestHandle() const { return mIdleRequestCallbackCounter; }
+ nsresult RunIdleRequest(mozilla::dom::IdleRequest* aRequest,
+ DOMHighResTimeStamp aDeadline, bool aDidTimeout);
+ nsresult ExecuteIdleRequest(TimeStamp aDeadline);
+ void ScheduleIdleRequestDispatch();
+
+ typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
+ static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
+ IdleRequests& aList);
+
+ static void RemoveIdleCallbackFromList(mozilla::dom::IdleRequest* aRequest,
+ IdleRequests& aList);
+
protected:
// These members are only used on outer window objects. Make sure
// you never set any of these on an inner object!
bool mFullScreen : 1;
bool mFullscreenMode : 1;
bool mIsClosed : 1;
bool mInClose : 1;
// mHavePendingClose means we've got a termination function set to
@@ -1877,31 +1892,23 @@ protected:
int32_t mBackPressureDelayMS;
// the method that was used to focus mFocusedNode
uint32_t mFocusMethod;
uint32_t mSerial;
- void DisableIdleCallbackRequests();
- void UnthrottleIdleCallbackRequests();
-
- void PostThrottledIdleCallback();
+ // The value for the next idle request timeout handler handle
+ uint32_t mIdleRequestTimeoutHandlerCounter;
- 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;
nsCOMPtr<nsIURI> mLastOpenedURI;
#endif
#ifdef MOZ_B2G
bool mNetworkUploadObserverEnabled;
@@ -1967,16 +1974,17 @@ protected:
nsTArray<RefPtr<mozilla::dom::VRDisplay>> mVRDisplays;
nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
friend class nsDOMScriptableHelper;
friend class nsDOMWindowUtils;
friend class mozilla::dom::PostMessageEvent;
friend class DesktopNotification;
+ friend class IdleRequestExecutor;
static WindowByIdTable* sWindowsById;
static bool sWarnedAboutWindowInternal;
};
inline nsISupports*
ToSupports(nsGlobalWindow *p)
{
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1301,19 +1301,16 @@ DOMInterfaces = {
'concrete': False,
},
'Window': {
'nativeType': 'nsGlobalWindow',
'binaryNames': {
'postMessage': 'postMessageMoz',
},
- 'implicitJSContext': [
- 'requestIdleCallback'
- ],
},
'WindowProxy': {
'nativeType': 'nsPIDOMWindowOuter',
'headerFile': 'nsPIDOMWindow.h',
'concrete': False
},