Bug 1835710 - Cancel off-thread JIT compilation before changing nursery allocation...
[gecko.git] / dom / base / DocGroup.cpp
blob16f673d063ffb2bfa3ba7078df2ffc3990f644bd
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/DocGroup.h"
9 #include "mozilla/AbstractThread.h"
10 #include "mozilla/PerformanceUtils.h"
11 #include "mozilla/SchedulerGroup.h"
12 #include "mozilla/StaticPrefs_dom.h"
13 #include "mozilla/Telemetry.h"
14 #include "mozilla/ThrottledEventQueue.h"
15 #include "mozilla/dom/BrowsingContext.h"
16 #include "mozilla/dom/CustomElementRegistry.h"
17 #include "mozilla/dom/DOMTypes.h"
18 #include "mozilla/dom/JSExecutionManager.h"
19 #include "mozilla/dom/WindowContext.h"
20 #include "nsDOMMutationObserver.h"
21 #include "nsIDirectTaskDispatcher.h"
22 #include "nsIXULRuntime.h"
23 #include "nsProxyRelease.h"
24 #include "nsThread.h"
25 #if defined(XP_WIN)
26 # include <processthreadsapi.h> // for GetCurrentProcessId()
27 #else
28 # include <unistd.h> // for getpid()
29 #endif // defined(XP_WIN)
31 namespace {
33 #define NS_LABELLINGEVENTTARGET_IID \
34 { \
35 0x6087fa50, 0xe387, 0x45c8, { \
36 0xab, 0x72, 0xd2, 0x1f, 0x69, 0xee, 0xd3, 0x15 \
37 } \
40 // LabellingEventTarget labels all dispatches with the DocGroup that
41 // created it.
42 class LabellingEventTarget final : public nsISerialEventTarget,
43 public nsIDirectTaskDispatcher {
44 public:
45 NS_DECLARE_STATIC_IID_ACCESSOR(NS_LABELLINGEVENTTARGET_IID)
47 explicit LabellingEventTarget(
48 mozilla::PerformanceCounter* aPerformanceCounter)
49 : mPerformanceCounter(aPerformanceCounter),
50 mMainThread(
51 static_cast<nsThread*>(mozilla::GetMainThreadSerialEventTarget())) {
54 NS_DECL_THREADSAFE_ISUPPORTS
55 NS_DECL_NSIEVENTTARGET_FULL
56 NS_DECL_NSIDIRECTTASKDISPATCHER
58 private:
59 ~LabellingEventTarget() = default;
60 const RefPtr<mozilla::PerformanceCounter> mPerformanceCounter;
61 const RefPtr<nsThread> mMainThread;
64 NS_DEFINE_STATIC_IID_ACCESSOR(LabellingEventTarget, NS_LABELLINGEVENTTARGET_IID)
66 } // namespace
68 NS_IMETHODIMP
69 LabellingEventTarget::DispatchFromScript(nsIRunnable* aRunnable,
70 uint32_t aFlags) {
71 return Dispatch(do_AddRef(aRunnable), aFlags);
74 NS_IMETHODIMP
75 LabellingEventTarget::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
76 uint32_t aFlags) {
77 if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
78 return NS_ERROR_UNEXPECTED;
81 return mozilla::SchedulerGroup::LabeledDispatch(
82 mozilla::TaskCategory::Other, std::move(aRunnable), mPerformanceCounter);
85 NS_IMETHODIMP
86 LabellingEventTarget::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
87 return NS_ERROR_NOT_IMPLEMENTED;
90 NS_IMETHODIMP
91 LabellingEventTarget::RegisterShutdownTask(nsITargetShutdownTask*) {
92 return NS_ERROR_NOT_IMPLEMENTED;
95 NS_IMETHODIMP
96 LabellingEventTarget::UnregisterShutdownTask(nsITargetShutdownTask*) {
97 return NS_ERROR_NOT_IMPLEMENTED;
100 NS_IMETHODIMP
101 LabellingEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread) {
102 *aIsOnCurrentThread = NS_IsMainThread();
103 return NS_OK;
106 NS_IMETHODIMP_(bool)
107 LabellingEventTarget::IsOnCurrentThreadInfallible() {
108 return NS_IsMainThread();
111 //-----------------------------------------------------------------------------
112 // nsIDirectTaskDispatcher
113 //-----------------------------------------------------------------------------
114 // We are always running on the main thread, forward to the nsThread's
115 // MainThread
116 NS_IMETHODIMP
117 LabellingEventTarget::DispatchDirectTask(already_AddRefed<nsIRunnable> aEvent) {
118 return mMainThread->DispatchDirectTask(std::move(aEvent));
121 NS_IMETHODIMP LabellingEventTarget::DrainDirectTasks() {
122 return mMainThread->DrainDirectTasks();
125 NS_IMETHODIMP LabellingEventTarget::HaveDirectTasks(bool* aValue) {
126 return mMainThread->HaveDirectTasks(aValue);
129 NS_IMPL_ISUPPORTS(LabellingEventTarget, nsIEventTarget, nsISerialEventTarget,
130 nsIDirectTaskDispatcher)
132 namespace mozilla::dom {
134 AutoTArray<RefPtr<DocGroup>, 2>* DocGroup::sPendingDocGroups = nullptr;
136 NS_IMPL_CYCLE_COLLECTION_CLASS(DocGroup)
138 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DocGroup)
139 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSignalSlotList)
140 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContextGroup)
141 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
143 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DocGroup)
144 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignalSlotList)
145 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContextGroup)
147 // If we still have any documents in this array, they were just unlinked, so
148 // clear out our weak pointers to them.
149 tmp->mDocuments.Clear();
150 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
152 /* static */
153 already_AddRefed<DocGroup> DocGroup::Create(
154 BrowsingContextGroup* aBrowsingContextGroup, const nsACString& aKey) {
155 RefPtr<DocGroup> docGroup = new DocGroup(aBrowsingContextGroup, aKey);
156 docGroup->mEventTarget =
157 new LabellingEventTarget(docGroup->GetPerformanceCounter());
158 return docGroup.forget();
161 /* static */
162 nsresult DocGroup::GetKey(nsIPrincipal* aPrincipal, bool aCrossOriginIsolated,
163 nsACString& aKey) {
164 // Use GetBaseDomain() to handle things like file URIs, IP address URIs,
165 // etc. correctly.
166 nsresult rv = aCrossOriginIsolated ? aPrincipal->GetOrigin(aKey)
167 : aPrincipal->GetSiteOrigin(aKey);
168 if (NS_FAILED(rv)) {
169 aKey.Truncate();
172 return rv;
175 void DocGroup::SetExecutionManager(JSExecutionManager* aManager) {
176 mExecutionManager = aManager;
179 mozilla::dom::CustomElementReactionsStack*
180 DocGroup::CustomElementReactionsStack() {
181 MOZ_ASSERT(NS_IsMainThread());
182 if (!mReactionsStack) {
183 mReactionsStack = new mozilla::dom::CustomElementReactionsStack();
186 return mReactionsStack;
189 void DocGroup::AddDocument(Document* aDocument) {
190 MOZ_ASSERT(NS_IsMainThread());
191 MOZ_ASSERT(!mDocuments.Contains(aDocument));
192 MOZ_ASSERT(mBrowsingContextGroup);
193 // If the document is loaded as data it may not have a container, in which
194 // case it can be difficult to determine the BrowsingContextGroup it's
195 // associated with. XSLT can also add the document to the DocGroup before it
196 // gets a container in some cases, in which case this will be asserted
197 // elsewhere.
198 MOZ_ASSERT_IF(
199 aDocument->GetBrowsingContext(),
200 aDocument->GetBrowsingContext()->Group() == mBrowsingContextGroup);
201 mDocuments.AppendElement(aDocument);
204 void DocGroup::RemoveDocument(Document* aDocument) {
205 MOZ_ASSERT(NS_IsMainThread());
206 MOZ_ASSERT(mDocuments.Contains(aDocument));
207 mDocuments.RemoveElement(aDocument);
209 if (mDocuments.IsEmpty()) {
210 mBrowsingContextGroup = nullptr;
214 DocGroup::DocGroup(BrowsingContextGroup* aBrowsingContextGroup,
215 const nsACString& aKey)
216 : mKey(aKey),
217 mBrowsingContextGroup(aBrowsingContextGroup),
218 mAgentClusterId(nsID::GenerateUUID()) {
219 // This method does not add itself to
220 // mBrowsingContextGroup->mDocGroups as the caller does it for us.
221 MOZ_ASSERT(NS_IsMainThread());
222 if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
223 mArena = new mozilla::dom::DOMArena();
226 mPerformanceCounter = new mozilla::PerformanceCounter("DocGroup:"_ns + aKey);
229 DocGroup::~DocGroup() {
230 MOZ_RELEASE_ASSERT(NS_IsMainThread());
231 MOZ_RELEASE_ASSERT(mDocuments.IsEmpty());
233 if (mIframePostMessageQueue) {
234 FlushIframePostMessageQueue();
238 RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
239 AssertIsOnMainThread();
240 MOZ_ASSERT(mPerformanceCounter);
241 #if defined(XP_WIN)
242 uint32_t pid = GetCurrentProcessId();
243 #else
244 uint32_t pid = getpid();
245 #endif
246 uint64_t windowID = 0;
247 uint16_t count = 0;
248 uint64_t duration = 0;
249 nsCString host;
250 bool isTopLevel = false;
251 RefPtr<BrowsingContext> top;
252 RefPtr<AbstractThread> mainThread =
253 AbstractMainThreadFor(TaskCategory::Performance);
255 for (const auto& document : *this) {
256 if (host.IsEmpty()) {
257 nsCOMPtr<nsIURI> docURI = document->GetDocumentURI();
258 if (!docURI) {
259 continue;
262 docURI->GetHost(host);
263 if (host.IsEmpty()) {
264 host = docURI->GetSpecOrDefault();
268 BrowsingContext* context = document->GetBrowsingContext();
269 if (!context) {
270 continue;
273 top = context->Top();
275 if (!top || !top->GetCurrentWindowContext()) {
276 continue;
279 isTopLevel = context->IsTop();
280 windowID = top->GetCurrentWindowContext()->OuterWindowId();
281 break;
284 MOZ_ASSERT(!host.IsEmpty());
285 duration = mPerformanceCounter->GetExecutionDuration();
286 FallibleTArray<CategoryDispatch> items;
288 // now that we have the host and window ids, let's look at the perf counters
289 for (uint32_t index = 0; index < (uint32_t)TaskCategory::Count; index++) {
290 TaskCategory category = static_cast<TaskCategory>(index);
291 count = mPerformanceCounter->GetDispatchCount(DispatchCategory(category));
292 CategoryDispatch item = CategoryDispatch(index, count);
293 if (!items.AppendElement(item, fallible)) {
294 NS_ERROR("Could not complete the operation");
295 break;
299 if (!isTopLevel && top && top->IsInProcess()) {
300 return PerformanceInfoPromise::CreateAndResolve(
301 PerformanceInfo(host, pid, windowID, duration,
302 mPerformanceCounter->GetID(), false, isTopLevel,
303 PerformanceMemoryInfo(), // Empty memory info
304 items),
305 __func__);
308 MOZ_ASSERT(mainThread);
309 RefPtr<DocGroup> self = this;
310 return CollectMemoryInfo(self, mainThread)
311 ->Then(
312 mainThread, __func__,
313 [self, host, pid, windowID, duration, isTopLevel,
314 items = std::move(items)](const PerformanceMemoryInfo& aMemoryInfo) {
315 PerformanceInfo info =
316 PerformanceInfo(host, pid, windowID, duration,
317 self->mPerformanceCounter->GetID(), false,
318 isTopLevel, aMemoryInfo, items);
320 return PerformanceInfoPromise::CreateAndResolve(std::move(info),
321 __func__);
323 [self](const nsresult rv) {
324 return PerformanceInfoPromise::CreateAndReject(rv, __func__);
328 nsresult DocGroup::Dispatch(TaskCategory aCategory,
329 already_AddRefed<nsIRunnable>&& aRunnable) {
330 MOZ_RELEASE_ASSERT(NS_IsMainThread());
332 if (mPerformanceCounter) {
333 mPerformanceCounter->IncrementDispatchCounter(DispatchCategory(aCategory));
335 return SchedulerGroup::LabeledDispatch(aCategory, std::move(aRunnable),
336 mPerformanceCounter);
339 nsISerialEventTarget* DocGroup::EventTargetFor(TaskCategory aCategory) const {
340 MOZ_RELEASE_ASSERT(NS_IsMainThread());
341 MOZ_ASSERT(!mDocuments.IsEmpty());
343 // Here we have the same event target for every TaskCategory. The
344 // reason for that is that currently TaskCategory isn't used, and
345 // it's unsure if it ever will be (See Bug 1624819).
346 return mEventTarget;
349 AbstractThread* DocGroup::AbstractMainThreadFor(TaskCategory aCategory) {
350 MOZ_RELEASE_ASSERT(NS_IsMainThread());
351 MOZ_ASSERT(!mDocuments.IsEmpty());
353 // Here we have the same thread for every TaskCategory. The reason
354 // for that is that currently TaskCategory isn't used, and it's
355 // unsure if it ever will be (See Bug 1624819).
356 return AbstractThread::MainThread();
359 void DocGroup::SignalSlotChange(HTMLSlotElement& aSlot) {
360 MOZ_ASSERT(!mSignalSlotList.Contains(&aSlot));
361 mSignalSlotList.AppendElement(&aSlot);
363 if (!sPendingDocGroups) {
364 // Queue a mutation observer compound microtask.
365 nsDOMMutationObserver::QueueMutationObserverMicroTask();
366 sPendingDocGroups = new AutoTArray<RefPtr<DocGroup>, 2>;
369 sPendingDocGroups->AppendElement(this);
372 bool DocGroup::TryToLoadIframesInBackground() {
373 return !FissionAutostart() &&
374 StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
375 StaticPrefs::dom_cross_origin_iframes_loaded_in_background();
378 nsresult DocGroup::QueueIframePostMessages(
379 already_AddRefed<nsIRunnable>&& aRunnable, uint64_t aWindowId) {
380 if (DocGroup::TryToLoadIframesInBackground()) {
381 if (!mIframePostMessageQueue) {
382 nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
383 mIframePostMessageQueue = ThrottledEventQueue::Create(
384 target, "Background Loading Iframe PostMessage Queue",
385 nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
386 nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
387 MOZ_ALWAYS_SUCCEEDS(rv);
390 // Ensure the queue is disabled. Unlike the postMessageEvent queue
391 // in BrowsingContextGroup, this postMessage queue should always
392 // be paused, because if we leave it open, the postMessage may get
393 // dispatched to an unloaded iframe
394 MOZ_ASSERT(mIframePostMessageQueue);
395 MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
397 mIframesUsedPostMessageQueue.Insert(aWindowId);
399 mIframePostMessageQueue->Dispatch(std::move(aRunnable), NS_DISPATCH_NORMAL);
400 return NS_OK;
402 return NS_ERROR_FAILURE;
405 void DocGroup::TryFlushIframePostMessages(uint64_t aWindowId) {
406 if (DocGroup::TryToLoadIframesInBackground()) {
407 mIframesUsedPostMessageQueue.Remove(aWindowId);
408 if (mIframePostMessageQueue && mIframesUsedPostMessageQueue.IsEmpty()) {
409 MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
410 nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
411 MOZ_ALWAYS_SUCCEEDS(rv);
412 FlushIframePostMessageQueue();
417 void DocGroup::FlushIframePostMessageQueue() {
418 nsCOMPtr<nsIRunnable> event;
419 while ((event = mIframePostMessageQueue->GetEvent())) {
420 Dispatch(TaskCategory::Other, event.forget());
424 nsTArray<RefPtr<HTMLSlotElement>> DocGroup::MoveSignalSlotList() {
425 for (const RefPtr<HTMLSlotElement>& slot : mSignalSlotList) {
426 slot->RemovedFromSignalSlotList();
428 return std::move(mSignalSlotList);
431 bool DocGroup::IsActive() const {
432 for (Document* doc : mDocuments) {
433 if (doc->IsCurrentActiveDocument()) {
434 return true;
438 return false;
441 } // namespace mozilla::dom