Bug 1734997 - Prototype the Prioritized Task Scheduling API r=smaug

Spec: https://wicg.github.io/scheduling-apis/

Differential Revision: https://phabricator.services.mozilla.com/D133494
This commit is contained in:
Sean Feng 2022-04-21 18:47:52 +00:00
Родитель 15daab2123
Коммит 5042a856cf
27 изменённых файлов: 1099 добавлений и 17 удалений

Просмотреть файл

@ -21,7 +21,7 @@ namespace dom {
class AbortSignal; class AbortSignal;
class AbortController final : public nsISupports, public nsWrapperCache { class AbortController : public nsISupports, public nsWrapperCache {
public: public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortController) NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AbortController)
@ -40,8 +40,8 @@ class AbortController final : public nsISupports, public nsWrapperCache {
void Abort(JSContext* aCx, JS::Handle<JS::Value> aReason); void Abort(JSContext* aCx, JS::Handle<JS::Value> aReason);
private: protected:
~AbortController(); virtual ~AbortController();
nsCOMPtr<nsIGlobalObject> mGlobal; nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<AbortSignal> mSignal; RefPtr<AbortSignal> mSignal;

Просмотреть файл

@ -25,9 +25,9 @@ namespace dom {
// it appears only to be used internally in the Fetch API. It might be a good // it appears only to be used internally in the Fetch API. It might be a good
// idea to split AbortSignal into an implementation that can follow, and an // idea to split AbortSignal into an implementation that can follow, and an
// implementation that can't, to provide this complexity only when it's needed. // implementation that can't, to provide this complexity only when it's needed.
class AbortSignal final : public DOMEventTargetHelper, class AbortSignal : public DOMEventTargetHelper,
public AbortSignalImpl, public AbortSignalImpl,
public AbortFollower { public AbortFollower {
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AbortSignal, NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(AbortSignal,
@ -57,7 +57,9 @@ class AbortSignal final : public DOMEventTargetHelper,
// AbortFollower // AbortFollower
void RunAbortAlgorithm() override; void RunAbortAlgorithm() override;
private: virtual bool IsTaskSignal() const { return false; }
protected:
~AbortSignal(); ~AbortSignal();
}; };

Просмотреть файл

@ -35,6 +35,7 @@ class Timeout final : protected LinkedListElement<RefPtr<Timeout>> {
eTimeoutOrInterval, eTimeoutOrInterval,
eIdleCallbackTimeout, eIdleCallbackTimeout,
eAbortSignalTimeout, eAbortSignalTimeout,
eDelayedWebTaskTimeout,
}; };
struct TimeoutIdAndReason { struct TimeoutIdAndReason {

Просмотреть файл

@ -451,6 +451,7 @@ uint32_t TimeoutManager::GetTimeoutId(Timeout::Reason aReason) {
return ++mIdleCallbackTimeoutCounter; return ++mIdleCallbackTimeoutCounter;
case Timeout::Reason::eTimeoutOrInterval: case Timeout::Reason::eTimeoutOrInterval:
return ++mTimeoutIdCounter; return ++mTimeoutIdCounter;
case Timeout::Reason::eDelayedWebTaskTimeout:
default: default:
return std::numeric_limits<uint32_t>::max(); // no cancellation support return std::numeric_limits<uint32_t>::max(); // no cancellation support
} }

Просмотреть файл

@ -153,6 +153,7 @@
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/dom/ProxyHandlerUtils.h" #include "mozilla/dom/ProxyHandlerUtils.h"
#include "mozilla/dom/RootedDictionary.h" #include "mozilla/dom/RootedDictionary.h"
#include "mozilla/dom/WebTaskSchedulerMainThread.h"
#include "mozilla/dom/ScriptLoader.h" #include "mozilla/dom/ScriptLoader.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/ServiceWorker.h" #include "mozilla/dom/ServiceWorker.h"
@ -1291,6 +1292,11 @@ void nsGlobalWindowInner::FreeInnerObjects() {
mContentMediaController = nullptr; mContentMediaController = nullptr;
if (mWebTaskScheduler) {
mWebTaskScheduler->Disconnect();
mWebTaskScheduler = nullptr;
}
mSharedWorkers.Clear(); mSharedWorkers.Clear();
#ifdef MOZ_WEBSPEECH #ifdef MOZ_WEBSPEECH
@ -1386,6 +1392,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler)
#ifdef MOZ_WEBSPEECH #ifdef MOZ_WEBSPEECH
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
#endif #endif
@ -1484,6 +1492,11 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
if (tmp->mWebTaskScheduler) {
tmp->mWebTaskScheduler->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
}
#ifdef MOZ_WEBSPEECH #ifdef MOZ_WEBSPEECH
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
#endif #endif
@ -1752,6 +1765,10 @@ void nsGlobalWindowInner::InitDocumentDependentState(JSContext* aCx) {
mLocalStorage = nullptr; mLocalStorage = nullptr;
mSessionStorage = nullptr; mSessionStorage = nullptr;
mPerformance = nullptr; mPerformance = nullptr;
if (mWebTaskScheduler) {
mWebTaskScheduler->Disconnect();
mWebTaskScheduler = nullptr;
}
// This must be called after nullifying the internal objects because here we // This must be called after nullifying the internal objects because here we
// could recreate them, calling the getter methods, and store them into the JS // could recreate them, calling the getter methods, and store them into the JS
@ -4226,6 +4243,14 @@ Selection* nsGlobalWindowInner::GetSelection(ErrorResult& aError) {
FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr); FORWARD_TO_OUTER_OR_THROW(GetSelectionOuter, (), aError, nullptr);
} }
WebTaskScheduler* nsGlobalWindowInner::Scheduler() {
if (!mWebTaskScheduler) {
mWebTaskScheduler = WebTaskScheduler::CreateForMainThread(this);
}
MOZ_ASSERT(mWebTaskScheduler);
return mWebTaskScheduler;
}
bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive, bool nsGlobalWindowInner::Find(const nsAString& aString, bool aCaseSensitive,
bool aBackwards, bool aWrapAround, bool aBackwards, bool aWrapAround,
bool aWholeWord, bool aSearchInFrames, bool aWholeWord, bool aSearchInFrames,
@ -6332,6 +6357,11 @@ static const char* GetTimeoutReasonString(Timeout* aTimeout) {
return "setIdleCallback handler (timed out)"; return "setIdleCallback handler (timed out)";
case Timeout::Reason::eAbortSignalTimeout: case Timeout::Reason::eAbortSignalTimeout:
return "AbortSignal timeout"; return "AbortSignal timeout";
case Timeout::Reason::eDelayedWebTaskTimeout:
return "delayedWebTaskCallback handler (timed out)";
default:
MOZ_CRASH("Unexpected enum value");
return "";
} }
MOZ_CRASH("Unexpected enum value"); MOZ_CRASH("Unexpected enum value");
return ""; return "";

Просмотреть файл

@ -130,6 +130,8 @@ struct RequestInit;
class RequestOrUSVString; class RequestOrUSVString;
class SharedWorker; class SharedWorker;
class Selection; class Selection;
class WebTaskScheduler;
class WebTaskSchedulerMainThread;
class SpeechSynthesis; class SpeechSynthesis;
class Timeout; class Timeout;
class U2F; class U2F;
@ -988,6 +990,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
// https://whatpr.org/html/4734/structured-data.html#cross-origin-isolated // https://whatpr.org/html/4734/structured-data.html#cross-origin-isolated
bool CrossOriginIsolated() const override; bool CrossOriginIsolated() const override;
mozilla::dom::WebTaskScheduler* Scheduler();
protected: protected:
// Web IDL helpers // Web IDL helpers
@ -1346,6 +1350,8 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
private: private:
RefPtr<mozilla::dom::ContentMediaController> mContentMediaController; RefPtr<mozilla::dom::ContentMediaController> mContentMediaController;
RefPtr<mozilla::dom::WebTaskSchedulerMainThread> mWebTaskScheduler;
protected: protected:
// Whether we need to care about orientation changes. // Whether we need to care about orientation changes.
bool mHasOrientationChangeListeners : 1; bool mHasOrientationChangeListeners : 1;

Просмотреть файл

@ -42,7 +42,8 @@
DOMInterfaces = { DOMInterfaces = {
'AbortSignal': { 'AbortSignal': {
'implicitJSContext': [ 'throwIfAborted' ] 'implicitJSContext': [ 'throwIfAborted' ],
'concrete': True,
}, },
'AnonymousContent': { 'AnonymousContent': {
@ -641,6 +642,11 @@ DOMInterfaces = {
'concrete': True, 'concrete': True,
}, },
'TaskController' : {
'nativeType' : 'mozilla::dom::WebTaskController',
'headerFile' : 'mozilla/dom/WebTaskController.h'
},
'TransceiverImpl': { 'TransceiverImpl': {
'nativeType': 'mozilla::TransceiverImpl', 'nativeType': 'mozilla::TransceiverImpl',
'headerFile': 'TransceiverImpl.h' 'headerFile': 'TransceiverImpl.h'
@ -747,6 +753,11 @@ DOMInterfaces = {
'headerFile': 'RTCStatsReport.h' 'headerFile': 'RTCStatsReport.h'
}, },
'Scheduler': {
'nativeType': 'mozilla::dom::WebTaskScheduler',
'headerFile': 'mozilla/dom/WebTaskScheduler.h',
},
'Screen': { 'Screen': {
'nativeType': 'nsScreen', 'nativeType': 'nsScreen',
}, },

Просмотреть файл

@ -111,6 +111,7 @@ DIRS += [
"prio", "prio",
"l10n", "l10n",
"origin-trials", "origin-trials",
"webscheduling",
] ]

Просмотреть файл

@ -0,0 +1,9 @@
[Exposed=(Window, Worker), Pref="dom.enable_web_task_scheduling"]
interface TaskPriorityChangeEvent : Event {
constructor (DOMString type , TaskPriorityChangeEventInit priorityChangeEventInitDict);
readonly attribute TaskPriority previousPriority;
};
dictionary TaskPriorityChangeEventInit : EventInit {
required TaskPriority previousPriority;
};

Просмотреть файл

@ -0,0 +1,42 @@
enum TaskPriority {
"user-blocking",
"user-visible",
"background"
};
[Exposed=(Window, Worker), Pref="dom.enable_web_task_scheduling"]
interface TaskSignal : AbortSignal {
readonly attribute TaskPriority priority;
attribute EventHandler onprioritychange;
};
dictionary SchedulerPostTaskOptions {
AbortSignal signal;
TaskPriority priority;
[EnforceRange] unsigned long long delay = 0;
};
callback SchedulerPostTaskCallback = any ();
[Exposed=(Window, Worker), Pref="dom.enable_web_task_scheduling"]
interface Scheduler {
Promise<any> postTask(
SchedulerPostTaskCallback callback,
optional SchedulerPostTaskOptions options = {}
);
};
dictionary TaskControllerInit {
TaskPriority priority = "user-visible";
};
[Exposed=(Window,Worker), Pref="dom.enable_web_task_scheduling"]
interface TaskController : AbortController {
[Throws]
constructor(optional TaskControllerInit init = {});
[Throws]
void setPriority(TaskPriority priority);
};

Просмотреть файл

@ -79,3 +79,9 @@ partial interface mixin WindowOrWorkerGlobalScope {
[Throws, Pref="dom.caches.enabled", SameObject] [Throws, Pref="dom.caches.enabled", SameObject]
readonly attribute CacheStorage caches; readonly attribute CacheStorage caches;
}; };
// https://wicg.github.io/scheduling-apis/#ref-for-windoworworkerglobalscope-scheduler
partial interface mixin WindowOrWorkerGlobalScope {
[Pref="dom.enable_web_task_scheduling", SameObject]
readonly attribute Scheduler scheduler;
};

Просмотреть файл

@ -985,6 +985,7 @@ WEBIDL_FILES = [
"WebGLRenderingContext.webidl", "WebGLRenderingContext.webidl",
"WebGPU.webidl", "WebGPU.webidl",
"WebSocket.webidl", "WebSocket.webidl",
"WebTaskScheduling.webidl",
"WebXR.webidl", "WebXR.webidl",
"WheelEvent.webidl", "WheelEvent.webidl",
"WidevineCDMManifest.webidl", "WidevineCDMManifest.webidl",
@ -1158,6 +1159,7 @@ GENERATED_EVENTS_WEBIDL_FILES = [
"SecurityPolicyViolationEvent.webidl", "SecurityPolicyViolationEvent.webidl",
"StyleSheetApplicableStateChangeEvent.webidl", "StyleSheetApplicableStateChangeEvent.webidl",
"SubmitEvent.webidl", "SubmitEvent.webidl",
"TaskPriorityChangeEvent.webidl",
"TCPServerSocketEvent.webidl", "TCPServerSocketEvent.webidl",
"TCPSocketErrorEvent.webidl", "TCPSocketErrorEvent.webidl",
"TCPSocketEvent.webidl", "TCPSocketEvent.webidl",

Просмотреть файл

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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_TaskSignal_h
#define mozilla_dom_TaskSignal_h
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/AbortSignal.h"
#include "mozilla/dom/WebTaskSchedulingBinding.h"
#include "WebTaskScheduler.h"
namespace mozilla::dom {
class TaskSignal : public AbortSignal {
public:
TaskSignal(nsIGlobalObject* aGlobal, TaskPriority aPriority)
: AbortSignal(aGlobal, false, JS::UndefinedHandleValue),
mPriority(aPriority),
mPriorityChanging(false) {}
IMPL_EVENT_HANDLER(prioritychange);
TaskPriority Priority() const { return mPriority; }
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override {
return TaskSignal_Binding::Wrap(aCx, this, aGivenProto);
}
void SetPriority(TaskPriority aPriority) { mPriority = aPriority; }
bool IsTaskSignal() const override { return true; }
bool PriorityChanging() const { return mPriorityChanging; }
void SetPriorityChanging(bool aPriorityChanging) {
mPriorityChanging = aPriorityChanging;
}
void SetWebTaskScheduler(WebTaskScheduler* aScheduler) {
mSchedulers.AppendElement(aScheduler);
}
void RunPriorityChangeAlgorithms() {
for (const WeakPtr<WebTaskScheduler>& scheduler : mSchedulers) {
scheduler->RunTaskSignalPriorityChange(this);
}
}
private:
TaskPriority mPriority;
bool mPriorityChanging;
nsTArray<WeakPtr<WebTaskScheduler>> mSchedulers;
~TaskSignal() = default;
};
} // namespace mozilla::dom
#endif

Просмотреть файл

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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 "WebTaskController.h"
#include "TaskSignal.h"
#include "mozilla/dom/TaskPriorityChangeEvent.h"
namespace mozilla::dom {
WebTaskController::WebTaskController(nsIGlobalObject* aGlobal,
TaskPriority aPriority)
: AbortController(aGlobal) {
MOZ_ASSERT(!mSignal);
mSignal = new TaskSignal(aGlobal, aPriority);
}
void WebTaskController::SetPriority(TaskPriority aPriority, ErrorResult& aRv) {
// https://wicg.github.io/scheduling-apis/#tasksignal-signal-priority-change
RefPtr<TaskSignal> taskSignal = static_cast<TaskSignal*>(mSignal.get());
MOZ_ASSERT(taskSignal);
if (taskSignal->PriorityChanging()) {
aRv.ThrowNotAllowedError("Signal's priority changing is true");
return;
}
if (taskSignal->Priority() == aPriority) {
return;
}
taskSignal->SetPriorityChanging(true);
TaskPriority previousPriority = taskSignal->Priority();
taskSignal->SetPriority(aPriority);
taskSignal->RunPriorityChangeAlgorithms();
TaskPriorityChangeEventInit init;
init.mPreviousPriority = previousPriority;
RefPtr<TaskPriorityChangeEvent> event = TaskPriorityChangeEvent::Constructor(
taskSignal, u"prioritychange"_ns, init);
event->SetTrusted(true);
taskSignal->DispatchEvent(*event);
taskSignal->SetPriorityChanging(false);
}
already_AddRefed<WebTaskController> WebTaskController::Constructor(
const GlobalObject& aGlobal, const TaskControllerInit& aInit,
ErrorResult& aRv) {
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<WebTaskController> webTaskController =
new WebTaskController(global, aInit.mPriority);
return webTaskController.forget();
}
JSObject* WebTaskController::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return TaskController_Binding::Wrap(aCx, this, aGivenProto);
}
} // namespace mozilla::dom

Просмотреть файл

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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_WebTaskController_h
#define mozilla_dom_WebTaskController_h
#include "nsWrapperCache.h"
#include "mozilla/dom/WebTaskSchedulingBinding.h"
#include "mozilla/dom/AbortController.h"
namespace mozilla::dom {
class WebTaskController : public AbortController {
public:
explicit WebTaskController(nsIGlobalObject* aGlobal, TaskPriority aPriority);
static already_AddRefed<WebTaskController> Constructor(
const GlobalObject& aGlobal, const TaskControllerInit& aInit,
ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void SetPriority(TaskPriority aPriority, ErrorResult& aRv);
private:
~WebTaskController() = default;
};
} // namespace mozilla::dom
#endif

Просмотреть файл

@ -0,0 +1,350 @@
/* -*- 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 "nsTHashMap.h"
#include "WebTaskScheduler.h"
#include "WebTaskSchedulerWorker.h"
#include "WebTaskSchedulerMainThread.h"
#include "TaskSignal.h"
#include "nsGlobalWindowInner.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/TimeoutManager.h"
namespace mozilla::dom {
inline void ImplCycleCollectionTraverse(
nsCycleCollectionTraversalCallback& aCallback, WebTaskQueue& aQueue,
const char* aName, uint32_t aFlags = 0) {
ImplCycleCollectionTraverse(aCallback, aQueue.Tasks(), aName, aFlags);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(WebTask)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WebTask)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WebTask)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebTask)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebTask)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebTask)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION(DelayedWebTaskHandler)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DelayedWebTaskHandler)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(DelayedWebTaskHandler)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DelayedWebTaskHandler)
void WebTask::RunAbortAlgorithm() {
// no-op if WebTask::Run has been called already
if (mPromise->State() == Promise::PromiseState::Pending) {
// There are two things that can keep a WebTask alive, either the abort
// signal or WebTaskQueue.
// It's possible that this task get cleared out from the WebTaskQueue first,
// and then the abort signal get aborted. For example, the callback function
// was async and there's a signal.abort() call in the callback.
if (isInList()) {
remove();
}
AutoJSAPI jsapi;
if (!jsapi.Init(mPromise->GetGlobalObject())) {
mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
} else {
JSContext* cx = jsapi.cx();
JS::RootedValue reason(cx);
Signal()->GetReason(cx, &reason);
mPromise->MaybeReject(reason);
}
}
MOZ_ASSERT(!isInList());
}
bool WebTask::Run() {
MOZ_ASSERT(HasScheduled());
remove();
ErrorResult error;
nsIGlobalObject* global = mPromise->GetGlobalObject();
if (!global || global->IsDying()) {
return false;
}
AutoJSAPI jsapi;
if (!jsapi.Init(global)) {
return false;
}
JS::Rooted<JS::Value> returnVal(jsapi.cx());
MOZ_ASSERT(mPromise->State() == Promise::PromiseState::Pending);
MOZ_KnownLive(mCallback)->Call(&returnVal, error, "WebTask",
CallbackFunction::eRethrowExceptions);
error.WouldReportJSException();
Promise::PromiseState promiseState = mPromise->State();
// If the state is Rejected, it means the above Call triggers the
// RunAbortAlgorithm method and rejected the promise
MOZ_ASSERT_IF(promiseState != Promise::PromiseState::Pending,
promiseState == Promise::PromiseState::Rejected);
if (promiseState == Promise::PromiseState::Pending) {
if (error.Failed()) {
mPromise->MaybeReject(std::move(error));
} else {
mPromise->MaybeResolve(returnVal);
}
}
MOZ_ASSERT(!isInList());
return true;
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebTaskScheduler, mParent,
mStaticPriorityTaskQueues,
mDynamicPriorityTaskQueues)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebTaskScheduler, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebTaskScheduler, Release)
/* static */
already_AddRefed<WebTaskSchedulerMainThread>
WebTaskScheduler::CreateForMainThread(nsGlobalWindowInner* aWindow) {
RefPtr<WebTaskSchedulerMainThread> scheduler =
new WebTaskSchedulerMainThread(aWindow->AsGlobal());
return scheduler.forget();
}
already_AddRefed<WebTaskSchedulerWorker> WebTaskScheduler::CreateForWorker(
WorkerPrivate* aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<WebTaskSchedulerWorker> scheduler =
new WebTaskSchedulerWorker(aWorkerPrivate);
return scheduler.forget();
}
WebTaskScheduler::WebTaskScheduler(nsIGlobalObject* aParent)
: mParent(aParent), mNextEnqueueOrder(1) {
MOZ_ASSERT(aParent);
}
JSObject* WebTaskScheduler::WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) {
return Scheduler_Binding::Wrap(cx, this, aGivenProto);
}
already_AddRefed<Promise> WebTaskScheduler::PostTask(
SchedulerPostTaskCallback& aCallback,
const SchedulerPostTaskOptions& aOptions) {
const Optional<OwningNonNull<AbortSignal>>& taskSignal = aOptions.mSignal;
const Optional<TaskPriority>& taskPriority = aOptions.mPriority;
ErrorResult rv;
// Instead of making WebTaskScheduler::PostTask throws, we always
// create the promise and return it. This is because we need to
// create the promise explicitly to be able to reject it with
// signal's reason.
RefPtr<Promise> promise = Promise::Create(mParent, rv);
if (rv.Failed()) {
return nullptr;
}
nsIGlobalObject* global = GetParentObject();
if (!global || global->IsDying()) {
promise->MaybeRejectWithNotSupportedError("Current window is detached");
return promise.forget();
}
if (taskSignal.WasPassed()) {
AbortSignal& signalValue = taskSignal.Value();
if (signalValue.Aborted()) {
AutoJSAPI jsapi;
if (!jsapi.Init(global)) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
JSContext* cx = jsapi.cx();
JS::RootedValue reason(cx);
signalValue.GetReason(cx, &reason);
promise->MaybeReject(reason);
return promise.forget();
}
}
// Let queue be the result of selecting the scheduler task queue for scheduler
// given signal and priority.
WebTaskQueue& taskQueue = SelectTaskQueue(taskSignal, taskPriority);
uint64_t delay = aOptions.mDelay;
RefPtr<WebTask> task = CreateTask(taskQueue, taskSignal, aCallback, promise);
if (delay > 0) {
nsresult rv = SetTimeoutForDelayedTask(task, delay);
if (NS_FAILED(rv)) {
promise->MaybeRejectWithUnknownError(
"Failed to setup timeout for delayed task");
}
return promise.forget();
}
if (!QueueTask(task)) {
promise->MaybeRejectWithNotSupportedError("Unable to queue the task");
return promise.forget();
}
return promise.forget();
}
already_AddRefed<WebTask> WebTaskScheduler::CreateTask(
WebTaskQueue& aQueue, const Optional<OwningNonNull<AbortSignal>>& aSignal,
SchedulerPostTaskCallback& aCallback, Promise* aPromise) {
uint32_t nextEnqueueOrder = mNextEnqueueOrder;
++mNextEnqueueOrder;
RefPtr<WebTask> task = new WebTask(nextEnqueueOrder, aCallback, aPromise);
aQueue.AddTask(task);
if (aSignal.WasPassed()) {
AbortSignal& signalValue = aSignal.Value();
task->Follow(&signalValue);
}
return task.forget();
}
bool WebTaskScheduler::QueueTask(WebTask* aTask) {
if (!DispatchEventLoopRunnable()) {
return false;
}
MOZ_ASSERT(!aTask->HasScheduled());
aTask->SetHasScheduled(true);
return true;
}
WebTask* WebTaskScheduler::GetNextTask() const {
// We first combine queues from both mStaticPriorityTaskQueues and
// mDynamicPriorityTaskQueues into a single hash map which the
// keys are the priorities and the values are all the queues that
// belong to this priority.
//
// Then From the queues which
// 1. Have scheduled tasks
// 2. Its priority is not less than any other queues' priority
// We pick the task which has the smallest enqueue order.
nsTHashMap<nsUint32HashKey, nsTArray<WebTaskQueue*>> allQueues;
for (auto iter = mStaticPriorityTaskQueues.ConstIter(); !iter.Done();
iter.Next()) {
const auto& queue = iter.Data();
if (!queue->Tasks().isEmpty() && queue->GetFirstScheduledTask()) {
nsTArray<WebTaskQueue*>& queuesForThisPriority =
allQueues.LookupOrInsert(static_cast<uint32_t>(iter.Key()));
queuesForThisPriority.AppendElement(queue.get());
}
}
for (auto iter = mDynamicPriorityTaskQueues.ConstIter(); !iter.Done();
iter.Next()) {
const auto& queue = iter.Data();
if (!queue->Tasks().isEmpty() && queue->GetFirstScheduledTask()) {
nsTArray<WebTaskQueue*>& queuesForThisPriority = allQueues.LookupOrInsert(
static_cast<uint32_t>(iter.Key()->Priority()));
queuesForThisPriority.AppendElement(queue.get());
}
}
if (allQueues.IsEmpty()) {
return nullptr;
}
for (uint32_t priority = static_cast<uint32_t>(TaskPriority::User_blocking);
priority < static_cast<uint32_t>(TaskPriority::EndGuard_); ++priority) {
if (auto queues = allQueues.Lookup(priority)) {
WebTaskQueue* oldestQueue = nullptr;
MOZ_ASSERT(!queues.Data().IsEmpty());
for (auto& webTaskQueue : queues.Data()) {
MOZ_ASSERT(webTaskQueue->GetFirstScheduledTask());
if (!oldestQueue) {
oldestQueue = webTaskQueue;
} else {
WebTask* firstScheduledRunnableForCurrentQueue =
webTaskQueue->GetFirstScheduledTask();
WebTask* firstScheduledRunnableForOldQueue =
oldestQueue->GetFirstScheduledTask();
if (firstScheduledRunnableForOldQueue->EnqueueOrder() >
firstScheduledRunnableForCurrentQueue->EnqueueOrder()) {
oldestQueue = webTaskQueue;
}
}
}
MOZ_ASSERT(oldestQueue);
return oldestQueue->GetFirstScheduledTask();
}
}
return nullptr;
}
void WebTaskScheduler::Disconnect() {
mStaticPriorityTaskQueues.Clear();
mDynamicPriorityTaskQueues.Clear();
}
void WebTaskScheduler::RunTaskSignalPriorityChange(TaskSignal* aTaskSignal) {
WebTaskQueue* const taskQueue = mDynamicPriorityTaskQueues.Get(aTaskSignal);
MOZ_ASSERT(taskQueue);
taskQueue->SetPriority(aTaskSignal->Priority());
}
WebTaskQueue& WebTaskScheduler::SelectTaskQueue(
const Optional<OwningNonNull<AbortSignal>>& aSignal,
const Optional<TaskPriority>& aPriority) {
bool useSignal = !aPriority.WasPassed() && aSignal.WasPassed() &&
aSignal.Value().IsTaskSignal();
if (useSignal) {
TaskSignal* taskSignal = static_cast<TaskSignal*>(&(aSignal.Value()));
WebTaskQueue* const taskQueue =
mDynamicPriorityTaskQueues.GetOrInsertNew(taskSignal);
taskQueue->SetPriority(taskSignal->Priority());
taskSignal->SetWebTaskScheduler(this);
MOZ_ASSERT(mDynamicPriorityTaskQueues.Contains(taskSignal));
return *taskQueue;
}
TaskPriority taskPriority =
aPriority.WasPassed() ? aPriority.Value() : TaskPriority::User_visible;
WebTaskQueue* const taskQueue = mStaticPriorityTaskQueues.GetOrInsertNew(
static_cast<uint32_t>(taskPriority));
taskQueue->SetPriority(taskPriority);
MOZ_ASSERT(
mStaticPriorityTaskQueues.Contains(static_cast<uint32_t>(taskPriority)));
return *taskQueue;
}
} // namespace mozilla::dom

Просмотреть файл

@ -0,0 +1,172 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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_WebTaskScheduler_h
#define mozilla_dom_WebTaskScheduler_h
#include "nsThreadUtils.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
#include "nsClassHashtable.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/AbortFollower.h"
#include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/WebTaskSchedulingBinding.h"
namespace mozilla::dom {
class WebTask : public LinkedListElement<RefPtr<WebTask>>,
public AbortFollower,
public SupportsWeakPtr {
friend class WebTaskScheduler;
public:
MOZ_CAN_RUN_SCRIPT bool Run();
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(WebTask)
WebTask(uint32_t aEnqueueOrder, SchedulerPostTaskCallback& aCallback,
Promise* aPromise)
: mEnqueueOrder(aEnqueueOrder),
mCallback(&aCallback),
mPromise(aPromise),
mHasScheduled(false) {}
void RunAbortAlgorithm() override;
bool HasScheduled() const { return mHasScheduled; }
uint32_t EnqueueOrder() const { return mEnqueueOrder; }
private:
void SetHasScheduled(bool aHasScheduled) { mHasScheduled = aHasScheduled; }
uint32_t mEnqueueOrder;
RefPtr<SchedulerPostTaskCallback> mCallback;
RefPtr<Promise> mPromise;
bool mHasScheduled;
~WebTask() = default;
};
class WebTaskQueue {
public:
WebTaskQueue() = default;
TaskPriority Priority() const { return mPriority; }
void SetPriority(TaskPriority aNewPriority) { mPriority = aNewPriority; }
LinkedList<RefPtr<WebTask>>& Tasks() { return mTasks; }
void AddTask(WebTask* aTask) { mTasks.insertBack(aTask); }
// TODO: To optimize it, we could have the scheduled and unscheduled
// tasks stored separately.
WebTask* GetFirstScheduledTask() {
for (const auto& task : mTasks) {
if (task->HasScheduled()) {
return task;
}
}
return nullptr;
}
~WebTaskQueue() { mTasks.clear(); }
private:
TaskPriority mPriority = TaskPriority::User_visible;
LinkedList<RefPtr<WebTask>> mTasks;
};
class WebTaskSchedulerMainThread;
class WebTaskSchedulerWorker;
class WebTaskScheduler : public nsWrapperCache, public SupportsWeakPtr {
friend class DelayedWebTaskHandler;
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebTaskScheduler)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebTaskScheduler)
static already_AddRefed<WebTaskSchedulerMainThread> CreateForMainThread(
nsGlobalWindowInner* aWindow);
static already_AddRefed<WebTaskSchedulerWorker> CreateForWorker(
WorkerPrivate* aWorkerPrivate);
explicit WebTaskScheduler(nsIGlobalObject* aParent);
already_AddRefed<Promise> PostTask(SchedulerPostTaskCallback& aCallback,
const SchedulerPostTaskOptions& aOptions);
nsIGlobalObject* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) override;
WebTask* GetNextTask() const;
void Disconnect();
void RunTaskSignalPriorityChange(TaskSignal* aTaskSignal);
protected:
virtual ~WebTaskScheduler() = default;
nsCOMPtr<nsIGlobalObject> mParent;
uint32_t mNextEnqueueOrder;
private:
already_AddRefed<WebTask> CreateTask(
WebTaskQueue& aQueue, const Optional<OwningNonNull<AbortSignal>>& aSignal,
SchedulerPostTaskCallback& aCallback, Promise* aPromise);
bool QueueTask(WebTask* aTask);
WebTaskQueue& SelectTaskQueue(
const Optional<OwningNonNull<AbortSignal>>& aSignal,
const Optional<TaskPriority>& aPriority);
virtual nsresult SetTimeoutForDelayedTask(WebTask* aTask,
uint64_t aDelay) = 0;
virtual bool DispatchEventLoopRunnable() = 0;
nsClassHashtable<nsUint32HashKey, WebTaskQueue> mStaticPriorityTaskQueues;
nsClassHashtable<nsPtrHashKey<TaskSignal>, WebTaskQueue>
mDynamicPriorityTaskQueues;
};
class DelayedWebTaskHandler final : public TimeoutHandler {
public:
DelayedWebTaskHandler(JSContext* aCx, WebTaskScheduler* aScheduler,
WebTask* aTask)
: TimeoutHandler(aCx), mScheduler(aScheduler), mWebTask(aTask) {}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(DelayedWebTaskHandler)
MOZ_CAN_RUN_SCRIPT bool Call(const char* /* unused */) override {
if (mScheduler && mWebTask) {
MOZ_ASSERT(!mWebTask->HasScheduled());
if (!mScheduler->QueueTask(mWebTask)) {
return false;
}
}
return true;
}
private:
~DelayedWebTaskHandler() override = default;
WeakPtr<WebTaskScheduler> mScheduler;
// WebTask gets added to WebTaskQueue, and WebTaskQueue keeps its alive.
WeakPtr<WebTask> mWebTask;
};
} // namespace mozilla::dom
#endif

Просмотреть файл

@ -0,0 +1,54 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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 "mozilla/TaskController.h"
#include "mozilla/dom/TimeoutManager.h"
#include "nsContentUtils.h"
#include "WebTaskSchedulerMainThread.h"
namespace mozilla::dom {
NS_IMETHODIMP WebTaskMainThreadRunnable::Run() {
if (mScheduler) {
RefPtr<WebTask> task = mScheduler->GetNextTask();
if (task) {
task->Run();
}
}
return NS_OK;
}
nsresult WebTaskSchedulerMainThread::SetTimeoutForDelayedTask(WebTask* aTask,
uint64_t aDelay) {
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (!cx) {
return NS_ERROR_UNEXPECTED;
}
nsIGlobalObject* global = GetParentObject();
MOZ_ASSERT(global);
RefPtr<DelayedWebTaskHandler> handler =
new DelayedWebTaskHandler(cx, this, aTask);
int32_t delay = aDelay > INT32_MAX ? INT32_MAX : (int32_t)aDelay;
int32_t handle;
return global->AsInnerWindow()->TimeoutManager().SetTimeout(
handler, delay, /* aIsInterval */ false,
Timeout::Reason::eDelayedWebTaskTimeout, &handle);
}
bool WebTaskSchedulerMainThread::DispatchEventLoopRunnable() {
RefPtr<WebTaskMainThreadRunnable> runnable =
new WebTaskMainThreadRunnable(this);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThreadQueue(runnable.forget(),
EventQueuePriority::Normal));
return true;
}
} // namespace mozilla::dom

Просмотреть файл

@ -0,0 +1,42 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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_WebSchedulerMainThread_h
#define mozilla_dom_WebSchedulerMainThread_h
#include "WebTaskScheduler.h"
#include "nsCycleCollectionParticipant.h"
#include "nsThreadUtils.h"
#include "mozilla/dom/AbortFollower.h"
namespace mozilla::dom {
class WebTaskMainThreadRunnable final : public Runnable {
public:
explicit WebTaskMainThreadRunnable(WebTaskSchedulerMainThread* aScheduler)
: Runnable("WebTaskMainThreadRunnable"), mScheduler(aScheduler) {}
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override;
private:
~WebTaskMainThreadRunnable() = default;
WeakPtr<WebTaskSchedulerMainThread> mScheduler;
};
class WebTaskSchedulerMainThread final : public WebTaskScheduler {
public:
explicit WebTaskSchedulerMainThread(nsIGlobalObject* aParent)
: WebTaskScheduler(aParent) {}
private:
nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay) override;
bool DispatchEventLoopRunnable() override;
~WebTaskSchedulerMainThread() = default;
};
} // namespace mozilla::dom
#endif

Просмотреть файл

@ -0,0 +1,59 @@
/* -*- 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 "WebTaskSchedulerWorker.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/TimeoutManager.h"
namespace mozilla::dom {
WebTaskWorkerRunnable::WebTaskWorkerRunnable(
WorkerPrivate* aWorkerPrivate, WebTaskSchedulerWorker* aSchedulerWorker)
: WorkerSameThreadRunnable(aWorkerPrivate),
mSchedulerWorker(aSchedulerWorker) {
MOZ_ASSERT(mSchedulerWorker);
}
WebTaskSchedulerWorker::WebTaskSchedulerWorker(WorkerPrivate* aWorkerPrivate)
: WebTaskScheduler(aWorkerPrivate->GlobalScope()),
mWorkerPrivate(aWorkerPrivate) {}
bool WebTaskWorkerRunnable::WorkerRun(JSContext* aCx,
WorkerPrivate* aWorkerPrivate) {
aWorkerPrivate->AssertIsOnWorkerThread();
if (mSchedulerWorker) {
RefPtr<WebTask> task = mSchedulerWorker->GetNextTask();
if (task) {
task->Run();
}
}
return true;
}
nsresult WebTaskSchedulerWorker::SetTimeoutForDelayedTask(WebTask* aTask,
uint64_t aDelay) {
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (!cx) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<DelayedWebTaskHandler> handler =
new DelayedWebTaskHandler(cx, this, aTask);
ErrorResult rv;
int32_t delay = aDelay > INT32_MAX ? INT32_MAX : (int32_t)aDelay;
mWorkerPrivate->SetTimeout(cx, handler, delay,
/* aIsInterval */ false,
Timeout::Reason::eDelayedWebTaskTimeout, rv);
return rv.StealNSResult();
}
bool WebTaskSchedulerWorker::DispatchEventLoopRunnable() {
RefPtr<WebTaskWorkerRunnable> runnable =
new WebTaskWorkerRunnable(mWorkerPrivate, this);
return runnable->Dispatch();
}
} // namespace mozilla::dom

Просмотреть файл

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:expandtab:shiftwidth=2:tabstop=2:
*/
/* 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_WebTaskSchedulerWorker_h
#define mozilla_dom_WebTaskSchedulerWorker_h
#include "WebTaskScheduler.h"
#include "mozilla/LinkedList.h"
#include "mozilla/dom/WorkerRunnable.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WebTaskSchedulingBinding.h"
namespace mozilla {
namespace dom {
class WebTaskWorkerRunnable : public WorkerSameThreadRunnable {
public:
WebTaskWorkerRunnable(WorkerPrivate* aWorkerPrivate,
WebTaskSchedulerWorker* aSchedulerWorker);
MOZ_CAN_RUN_SCRIPT_BOUNDARY
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
private:
~WebTaskWorkerRunnable() = default;
WeakPtr<WebTaskSchedulerWorker> mSchedulerWorker;
};
class WebTaskSchedulerWorker final : public WebTaskScheduler {
public:
explicit WebTaskSchedulerWorker(WorkerPrivate* aWorkerPrivate);
private:
~WebTaskSchedulerWorker() = default;
nsresult SetTimeoutForDelayedTask(WebTask* aTask, uint64_t aDelay) override;
bool DispatchEventLoopRunnable() override;
CheckedUnsafePtr<WorkerPrivate> mWorkerPrivate;
};
} // namespace dom
} // namespace mozilla
#endif

Просмотреть файл

@ -0,0 +1,27 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Performance")
EXPORTS.mozilla.dom += [
"TaskSignal.h",
"WebTaskController.h",
"WebTaskScheduler.h",
"WebTaskSchedulerMainThread.h",
"WebTaskSchedulerWorker.h",
]
UNIFIED_SOURCES += [
"WebTaskController.cpp",
"WebTaskScheduler.cpp",
"WebTaskSchedulerMainThread.cpp",
"WebTaskSchedulerWorker.cpp",
]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul"

Просмотреть файл

@ -52,6 +52,7 @@
#include "mozilla/dom/TimeoutHandler.h" #include "mozilla/dom/TimeoutHandler.h"
#include "mozilla/dom/WorkerBinding.h" #include "mozilla/dom/WorkerBinding.h"
#include "mozilla/dom/WorkerScope.h" #include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/WebTaskScheduler.h"
#include "mozilla/dom/JSExecutionManager.h" #include "mozilla/dom/JSExecutionManager.h"
#include "mozilla/dom/WindowContext.h" #include "mozilla/dom/WindowContext.h"
#include "mozilla/extensions/ExtensionBrowser.h" // extensions::Create{AndDispatchInitWorkerContext,WorkerLoaded,WorkerDestroyed}Runnable #include "mozilla/extensions/ExtensionBrowser.h" // extensions::Create{AndDispatchInitWorkerContext,WorkerLoaded,WorkerDestroyed}Runnable
@ -4591,10 +4592,15 @@ bool WorkerPrivate::NotifyInternal(WorkerStatus aStatus) {
// If the worker script never ran, or failed to compile, we don't need to do // If the worker script never ran, or failed to compile, we don't need to do
// anything else. // anything else.
if (!GlobalScope()) { WorkerGlobalScope* global = GlobalScope();
if (!global) {
return true; return true;
} }
if (WebTaskScheduler* scheduler = global->GetExistingScheduler()) {
scheduler->Disconnect();
}
// Don't abort the script now, but we dispatch a runnable to do it when the // Don't abort the script now, but we dispatch a runnable to do it when the
// current JS frame is executed. // current JS frame is executed.
if (aStatus == Closing) { if (aStatus == Closing) {
@ -4872,14 +4878,25 @@ bool WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) {
// break out of the loop. // break out of the loop.
RefPtr<TimeoutHandler> handler(info->mHandler); RefPtr<TimeoutHandler> handler(info->mHandler);
if (info->mReason == Timeout::Reason::eTimeoutOrInterval) {
const char* reason;
if (info->mIsInterval) {
reason = "setInterval handler";
} else {
reason = "setTimeout handler";
}
const char* reason;
switch (info->mReason) {
case Timeout::Reason::eTimeoutOrInterval:
if (info->mIsInterval) {
reason = "setInterval handler";
} else {
reason = "setTimeout handler";
}
break;
case Timeout::Reason::eDelayedWebTaskTimeout:
reason = "delayedWebTask handler";
break;
default:
MOZ_ASSERT(info->mReason == Timeout::Reason::eAbortSignalTimeout);
reason = "AbortSignal Timeout";
}
if (info->mReason == Timeout::Reason::eTimeoutOrInterval ||
info->mReason == Timeout::Reason::eDelayedWebTaskTimeout) {
RefPtr<WorkerGlobalScope> scope(this->GlobalScope()); RefPtr<WorkerGlobalScope> scope(this->GlobalScope());
CallbackDebuggerNotificationGuard guard( CallbackDebuggerNotificationGuard guard(
scope, info->mIsInterval scope, info->mIsInterval
@ -4892,7 +4909,7 @@ bool WorkerPrivate::RunExpiredTimeouts(JSContext* aCx) {
} }
} else { } else {
MOZ_ASSERT(info->mReason == Timeout::Reason::eAbortSignalTimeout); MOZ_ASSERT(info->mReason == Timeout::Reason::eAbortSignalTimeout);
MOZ_ALWAYS_TRUE(handler->Call("AbortSignal timeout")); MOZ_ALWAYS_TRUE(handler->Call(reason));
} }
NS_ASSERTION(data->mRunningExpiredTimeouts, "Someone changed this!"); NS_ASSERTION(data->mRunningExpiredTimeouts, "Someone changed this!");

Просмотреть файл

@ -65,6 +65,7 @@
#include "mozilla/dom/Performance.h" #include "mozilla/dom/Performance.h"
#include "mozilla/dom/Promise.h" #include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h" #include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/WebTaskSchedulerWorker.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SerializedStackHolder.h" #include "mozilla/dom/SerializedStackHolder.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h" #include "mozilla/dom/ServiceWorkerDescriptor.h"
@ -380,6 +381,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope,
WorkerGlobalScopeBase) WorkerGlobalScopeBase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebTaskScheduler)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
@ -391,6 +393,10 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope,
WorkerGlobalScopeBase) WorkerGlobalScopeBase)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto) NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
if (tmp->mWebTaskScheduler) {
tmp->mWebTaskScheduler->Disconnect();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebTaskScheduler)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
@ -696,6 +702,21 @@ already_AddRefed<IDBFactory> WorkerGlobalScope::GetIndexedDB(
return indexedDB.forget(); return indexedDB.forget();
} }
WebTaskScheduler* WorkerGlobalScope::Scheduler() {
mWorkerPrivate->AssertIsOnWorkerThread();
if (!mWebTaskScheduler) {
mWebTaskScheduler = WebTaskScheduler::CreateForWorker(mWorkerPrivate);
}
MOZ_ASSERT(mWebTaskScheduler);
return mWebTaskScheduler;
}
WebTaskScheduler* WorkerGlobalScope::GetExistingScheduler() const {
return mWebTaskScheduler;
}
already_AddRefed<Promise> WorkerGlobalScope::CreateImageBitmap( already_AddRefed<Promise> WorkerGlobalScope::CreateImageBitmap(
const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions, const ImageBitmapSource& aImage, const ImageBitmapOptions& aOptions,
ErrorResult& aRv) { ErrorResult& aRv) {

Просмотреть файл

@ -76,6 +76,8 @@ class WorkerLocation;
class WorkerNavigator; class WorkerNavigator;
class WorkerPrivate; class WorkerPrivate;
class VsyncWorkerChild; class VsyncWorkerChild;
class WebTaskScheduler;
class WebTaskSchedulerWorker;
struct RequestInit; struct RequestInit;
namespace cache { namespace cache {
@ -311,6 +313,9 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase {
already_AddRefed<cache::CacheStorage> GetCaches(ErrorResult& aRv); already_AddRefed<cache::CacheStorage> GetCaches(ErrorResult& aRv);
WebTaskScheduler* Scheduler();
WebTaskScheduler* GetExistingScheduler() const;
bool WindowInteractionAllowed() const; bool WindowInteractionAllowed() const;
void AllowWindowInteraction(); void AllowWindowInteraction();
@ -346,6 +351,7 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase {
RefPtr<IDBFactory> mIndexedDB; RefPtr<IDBFactory> mIndexedDB;
RefPtr<cache::CacheStorage> mCacheStorage; RefPtr<cache::CacheStorage> mCacheStorage;
RefPtr<DebuggerNotificationManager> mDebuggerNotificationManager; RefPtr<DebuggerNotificationManager> mDebuggerNotificationManager;
RefPtr<WebTaskSchedulerWorker> mWebTaskScheduler;
uint32_t mWindowInteractionsAllowed = 0; uint32_t mWindowInteractionsAllowed = 0;
}; };

Просмотреть файл

@ -2146,6 +2146,12 @@
value: true value: true
mirror: always mirror: always
# Whether the scheduler interface will be exposed
- name: dom.enable_web_task_scheduling
type: RelaxedAtomicBool
value: @IS_NIGHTLY_BUILD@
mirror: always
# If this is true, it's allowed to fire "cut", "copy" and "paste" events. # If this is true, it's allowed to fire "cut", "copy" and "paste" events.
# Additionally, "input" events may expose clipboard content when inputType # Additionally, "input" events may expose clipboard content when inputType
# is "insertFromPaste" or something. # is "insertFromPaste" or something.

Просмотреть файл

@ -880,6 +880,7 @@ STATIC_ATOMS = [
Atom("onpopupshowing", "onpopupshowing"), Atom("onpopupshowing", "onpopupshowing"),
Atom("onpopupshown", "onpopupshown"), Atom("onpopupshown", "onpopupshown"),
Atom("onprocessorerror", "onprocessorerror"), Atom("onprocessorerror", "onprocessorerror"),
Atom("onprioritychange", "onprioritychange"),
Atom("onpush", "onpush"), Atom("onpush", "onpush"),
Atom("onpushsubscriptionchange", "onpushsubscriptionchange"), Atom("onpushsubscriptionchange", "onpushsubscriptionchange"),
Atom("onRadioStateChange", "onRadioStateChange"), Atom("onRadioStateChange", "onRadioStateChange"),