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:
+20
-103
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<!doctype html>
|
||||
<script>
|
||||
addEventListener('load', _ => {
|
||||
let params = new URLSearchParams(window.location.search);
|
||||
window.opener.postMessage(params.get('name'), '*');
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user