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"
26 # include <processthreadsapi.h> // for GetCurrentProcessId()
28 # include <unistd.h> // for getpid()
29 #endif // defined(XP_WIN)
33 #define NS_LABELLINGEVENTTARGET_IID \
35 0x6087fa50, 0xe387, 0x45c8, { \
36 0xab, 0x72, 0xd2, 0x1f, 0x69, 0xee, 0xd3, 0x15 \
40 // LabellingEventTarget labels all dispatches with the DocGroup that
42 class LabellingEventTarget final
: public nsISerialEventTarget
,
43 public nsIDirectTaskDispatcher
{
45 NS_DECLARE_STATIC_IID_ACCESSOR(NS_LABELLINGEVENTTARGET_IID
)
47 explicit LabellingEventTarget(
48 mozilla::PerformanceCounter
* aPerformanceCounter
)
49 : mPerformanceCounter(aPerformanceCounter
),
51 static_cast<nsThread
*>(mozilla::GetMainThreadSerialEventTarget())) {
54 NS_DECL_THREADSAFE_ISUPPORTS
55 NS_DECL_NSIEVENTTARGET_FULL
56 NS_DECL_NSIDIRECTTASKDISPATCHER
59 ~LabellingEventTarget() = default;
60 const RefPtr
<mozilla::PerformanceCounter
> mPerformanceCounter
;
61 const RefPtr
<nsThread
> mMainThread
;
64 NS_DEFINE_STATIC_IID_ACCESSOR(LabellingEventTarget
, NS_LABELLINGEVENTTARGET_IID
)
69 LabellingEventTarget::DispatchFromScript(nsIRunnable
* aRunnable
,
71 return Dispatch(do_AddRef(aRunnable
), aFlags
);
75 LabellingEventTarget::Dispatch(already_AddRefed
<nsIRunnable
> aRunnable
,
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
);
86 LabellingEventTarget::DelayedDispatch(already_AddRefed
<nsIRunnable
>, uint32_t) {
87 return NS_ERROR_NOT_IMPLEMENTED
;
91 LabellingEventTarget::RegisterShutdownTask(nsITargetShutdownTask
*) {
92 return NS_ERROR_NOT_IMPLEMENTED
;
96 LabellingEventTarget::UnregisterShutdownTask(nsITargetShutdownTask
*) {
97 return NS_ERROR_NOT_IMPLEMENTED
;
101 LabellingEventTarget::IsOnCurrentThread(bool* aIsOnCurrentThread
) {
102 *aIsOnCurrentThread
= NS_IsMainThread();
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
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
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();
162 nsresult
DocGroup::GetKey(nsIPrincipal
* aPrincipal
, bool aCrossOriginIsolated
,
164 // Use GetBaseDomain() to handle things like file URIs, IP address URIs,
166 nsresult rv
= aCrossOriginIsolated
? aPrincipal
->GetOrigin(aKey
)
167 : aPrincipal
->GetSiteOrigin(aKey
);
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
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
)
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
);
242 uint32_t pid
= GetCurrentProcessId();
244 uint32_t pid
= getpid();
246 uint64_t windowID
= 0;
248 uint64_t duration
= 0;
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();
262 docURI
->GetHost(host
);
263 if (host
.IsEmpty()) {
264 host
= docURI
->GetSpecOrDefault();
268 BrowsingContext
* context
= document
->GetBrowsingContext();
273 top
= context
->Top();
275 if (!top
|| !top
->GetCurrentWindowContext()) {
279 isTopLevel
= context
->IsTop();
280 windowID
= top
->GetCurrentWindowContext()->OuterWindowId();
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");
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
308 MOZ_ASSERT(mainThread
);
309 RefPtr
<DocGroup
> self
= this;
310 return CollectMemoryInfo(self
, mainThread
)
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
),
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).
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
);
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()) {
441 } // namespace mozilla::dom