зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
15daab2123
Коммит
5042a856cf
|
@ -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,7 +25,7 @@ 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:
|
||||||
|
@ -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;
|
const char* reason;
|
||||||
|
switch (info->mReason) {
|
||||||
|
case Timeout::Reason::eTimeoutOrInterval:
|
||||||
if (info->mIsInterval) {
|
if (info->mIsInterval) {
|
||||||
reason = "setInterval handler";
|
reason = "setInterval handler";
|
||||||
} else {
|
} else {
|
||||||
reason = "setTimeout handler";
|
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"),
|
||||||
|
|
Загрузка…
Ссылка в новой задаче