--- a/dom/base/IdleRequest.cpp
+++ b/dom/base/IdleRequest.cpp
@@ -3,155 +3,57 @@
/* 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/TimeoutManager.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,
- 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);
-}
+IdleRequest::IdleRequest(IdleRequestCallback& aCallback, uint32_t aHandle)
+ : mCallback(&aCallback), mHandle(aHandle), mTimeoutHandle(Nothing()) {}
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)
+void
+IdleRequest::SetTimeoutHandle(int32_t aHandle)
{
- int32_t handle;
- nsresult rv = mWindow->TimeoutManager().SetTimeout(
- this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
- mTimeoutHandle = Some(handle);
-
- return rv;
+ mTimeoutHandle = Some(aHandle);
}
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)
-{
- 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()) {
- mWindow->TimeoutManager().ClearTimeout(
- 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(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
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -36,17 +36,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,
+ eIdleCallbackTimeout,
+ };
#ifdef DEBUG
bool HasRefCnt(uint32_t aCount) const;
#endif // DEBUG
void SetWhenOrTimeRemaining(const TimeStamp& aBaseTime,
const TimeDuration& aDelay);
@@ -71,16 +75,18 @@ public:
bool mRunning;
// True if this is a repeating/interval timer
bool mIsInterval;
// True if this is a timeout coming from a tracking script
bool mIsTracking;
+ // 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;
new file mode 100644
--- /dev/null
+++ b/dom/base/TimeoutHandler.cpp
@@ -0,0 +1,35 @@
+/* -*- 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)
+{
+ 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_ISUPPORTS(TimeoutHandler, nsITimeoutHandler)
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/TimeoutHandler.h
@@ -0,0 +1,47 @@
+/* -*- 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:
+ NS_DECL_ISUPPORTS
+
+ TimeoutHandler() : mFileName(""), mLineNo(0), mColumn(0) {}
+ explicit TimeoutHandler(JSContext *aCx);
+
+ virtual nsresult Call() override;
+ virtual void GetLocation(const char** aFileName, uint32_t* aLineNo,
+ uint32_t* aColumn) override;
+ virtual void MarkForCC() override {}
+protected:
+ 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
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -200,16 +200,17 @@ EXPORTS.mozilla.dom += [
'ShadowRoot.h',
'StructuredCloneHolder.h',
'StructuredCloneTags.h',
'StyleSheetList.h',
'SubtleCrypto.h',
'TabGroup.h',
'Text.h',
'Timeout.h',
+ 'TimeoutHandler.h',
'TimeoutManager.h',
'TreeWalker.h',
'WebKitCSSMatrix.h',
'WebSocket.h',
'WindowOrientationObserver.h',
]
UNIFIED_SOURCES += [
@@ -340,16 +341,17 @@ UNIFIED_SOURCES += [
'StructuredCloneHolder.cpp',
'StyleSheetList.cpp',
'SubtleCrypto.cpp',
'TabGroup.cpp',
'Text.cpp',
'TextInputProcessor.cpp',
'ThirdPartyUtil.cpp',
'Timeout.cpp',
+ 'TimeoutHandler.cpp',
'TimeoutManager.cpp',
'TreeWalker.cpp',
'WebKitCSSMatrix.cpp',
'WebSocket.cpp',
'WindowNamedPropertiesHandler.cpp',
'WindowOrientationObserver.cpp',
]
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -18,16 +18,17 @@
#include "nsDOMNavigationTiming.h"
#include "nsIDOMStorageManager.h"
#include "mozilla/dom/Storage.h"
#include "mozilla/dom/IdleRequest.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/StorageEventBinding.h"
#include "mozilla/dom/Timeout.h"
+#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/TimeoutManager.h"
#include "mozilla/IntegerPrintfMacros.h"
#if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
#include "mozilla/dom/WindowOrientationObserver.h"
#endif
#include "nsDOMOfflineResourceList.h"
#include "nsError.h"
#include "nsIIdleService.h"
@@ -517,124 +518,329 @@ 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_IMETHODIMP
+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());
+ }
+}
+
+class IdleRequestExecutorTimeoutHandler final : public TimeoutHandler
+{
+public:
+ explicit IdleRequestExecutorTimeoutHandler(IdleRequestExecutor *aExecutor)
+ : mExecutor(aExecutor) {}
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestExecutorTimeoutHandler)
+
+ nsresult Call() override
+ {
+ mExecutor->Dispatch();
+ return NS_OK;
+ }
+private:
+ ~IdleRequestExecutorTimeoutHandler() {}
+ RefPtr<IdleRequestExecutor> mExecutor;
+};
+
+NS_IMPL_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler, mExecutor)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestExecutorTimeoutHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestExecutorTimeoutHandler)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestExecutorTimeoutHandler)
+ NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+NS_INTERFACE_MAP_END
+
+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()) {
+ nsCOMPtr<nsITimeoutHandler> handler = new IdleRequestExecutorTimeoutHandler(mIdleRequestExecutor);
+ int32_t dummy;
+ mTimeoutManager->SetTimeout(handler, 0, false,
+ Timeout::Reason::eIdleCallbackTimeout, &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()) {
+ mTimeoutManager->ClearTimeout(request->Handle(),
+ Timeout::Reason::eIdleCallbackTimeout);
+ }
+
+ 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_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(IdleRequestTimeoutHandler)
+
+ nsresult Call() override
+ {
+ return nsGlobalWindow::Cast(mWindow)->RunIdleRequest(mIdleRequest, 0.0, true);
+ }
+
+private:
+ ~IdleRequestTimeoutHandler() {}
+
+ RefPtr<IdleRequest> mIdleRequest;
+ nsCOMPtr<nsPIDOMWindowInner> mWindow;
+};
+
+NS_IMPL_CYCLE_COLLECTION(IdleRequestTimeoutHandler, mIdleRequest, mWindow)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IdleRequestTimeoutHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IdleRequestTimeoutHandler)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IdleRequestTimeoutHandler)
+ NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
+NS_INTERFACE_MAP_END
+
uint32_t
nsGlobalWindow::RequestIdleCallback(JSContext* aCx,
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(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 = mTimeoutManager->SetTimeout(
+ handler, aOptions.mTimeout.Value(), false,
+ Timeout::Reason::eIdleCallbackTimeout, &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);
}
}
bool
nsGlobalWindow::IsBackgroundInternal() const
{
return !mOuterWindow || mOuterWindow->IsBackground();
}
-
namespace mozilla {
namespace dom {
extern uint64_t
NextWindowID();
} // namespace dom
} // namespace mozilla
template<class T>
@@ -1215,16 +1421,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
mHasSeenGamepadInput(false),
mNotifiedIDDestroyed(false),
mAllowScriptsToClose(false),
mSuspendDepth(0),
mFreezeDepth(0),
mFocusMethod(0),
mSerial(0),
mIdleRequestCallbackCounter(1),
+ mIdleRequestExecutor(nullptr),
#ifdef DEBUG
mSetOpenerWindowCalled(false),
#endif
#ifdef MOZ_B2G
mNetworkUploadObserverEnabled(false),
mNetworkDownloadObserverEnabled(false),
#endif
mCleanedUp(false),
@@ -1926,24 +2133,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)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
// Traverse stuff from nsPIDOMWindow
@@ -2038,16 +2242,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioWorklet)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
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()
@@ -10040,17 +10245,16 @@ void nsGlobalWindow::SetIsBackground(boo
if (!inner) {
return;
}
if (resetTimers) {
inner->mTimeoutManager->ResetTimersForThrottleReduction();
}
- inner->UnthrottleIdleCallbackRequests();
inner->SyncGamepadState();
}
void nsGlobalWindow::MaybeUpdateTouchState()
{
FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
if (mMayHaveTouchEventListener) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -97,32 +97,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 : uint8_t;
class IdleRequest;
class IdleRequestCallback;
+class IncrementalRunnable;
class Location;
class MediaQueryList;
class MozSelfSupport;
class Navigator;
class OwningExternalOrWindowProxy;
class Promise;
class PostMessageEvent;
struct RequestInit;
@@ -1100,17 +1103,16 @@ public:
void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
uint32_t RequestIdleCallback(JSContext* aCx,
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,
@@ -1770,16 +1772,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 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
@@ -1906,29 +1922,23 @@ protected:
uint32_t mSuspendDepth;
uint32_t mFreezeDepth;
// the method that was used to focus mFocusedNode
uint32_t mFocusMethod;
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 value for the next idle request timeout handler handle
+ uint32_t mIdleRequestTimeoutHandlerCounter;
// 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;
@@ -1995,16 +2005,17 @@ protected:
nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
friend class nsDOMScriptableHelper;
friend class nsDOMWindowUtils;
friend class mozilla::dom::PostMessageEvent;
friend class DesktopNotification;
friend class mozilla::dom::TimeoutManager;
+ friend class IdleRequestExecutor;
static WindowByIdTable* sWindowsById;
static bool sWarnedAboutWindowInternal;
};
inline nsISupports*
ToSupports(nsGlobalWindow *p)
{