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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_dom_BrowsingContextGroup_h
8 #define mozilla_dom_BrowsingContextGroup_h
10 #include "mozilla/dom/BrowsingContext.h"
11 #include "mozilla/FunctionRef.h"
12 #include "nsRefPtrHashtable.h"
13 #include "nsHashKeys.h"
15 #include "nsTHashSet.h"
16 #include "nsWrapperCache.h"
17 #include "nsXULAppAPI.h"
20 class ThrottledEventQueue
;
24 // Amount of time allowed between alert/prompt/confirm before enabling
25 // the stop dialog checkbox.
26 #define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
28 class BrowsingContext
;
33 // A BrowsingContextGroup represents the Unit of Related Browsing Contexts in
36 // A BrowsingContext may not hold references to other BrowsingContext objects
37 // which are not in the same BrowsingContextGroup.
38 class BrowsingContextGroup final
: public nsWrapperCache
{
40 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContextGroup
)
41 NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(BrowsingContextGroup
)
43 // Interact with the list of synced contexts. This controls the lifecycle of
44 // the BrowsingContextGroup and contexts loaded within them.
45 void Register(nsISupports
* aContext
);
46 void Unregister(nsISupports
* aContext
);
48 // Control which processes will be used to host documents loaded in this
49 // BrowsingContextGroup. There should only ever be one host process per remote
52 // A new host process will be subscribed to the BrowsingContextGroup unless it
53 // is still launching, in which case it will subscribe itself when it is done
55 void EnsureHostProcess(ContentParent
* aProcess
);
57 // A removed host process will no longer be used to host documents loaded in
58 // this BrowsingContextGroup.
59 void RemoveHostProcess(ContentParent
* aProcess
);
61 // Synchronize the current BrowsingContextGroup state down to the given
62 // content process, and continue updating it.
64 // You rarely need to call this directly, as it's automatically called by
65 // |EnsureHostProcess| as needed.
66 void Subscribe(ContentParent
* aProcess
);
68 // Stop synchronizing the current BrowsingContextGroup state down to a given
69 // content process. The content process must no longer be a host process.
70 void Unsubscribe(ContentParent
* aProcess
);
72 // Look up the process which should be used to host documents with this
73 // RemoteType. This will be a non-dead process associated with this
74 // BrowsingContextGroup, if possible.
75 ContentParent
* GetHostProcess(const nsACString
& aRemoteType
);
77 // When a BrowsingContext is being discarded, we may want to keep the
78 // corresponding BrowsingContextGroup alive until the other process
79 // acknowledges that the BrowsingContext has been discarded. A `KeepAlive`
80 // will be added to the `BrowsingContextGroup`, delaying destruction.
82 void RemoveKeepAlive();
84 // A `KeepAlivePtr` will hold both a strong reference to the
85 // `BrowsingContextGroup` and holds a `KeepAlive`. When the pointer is
86 // dropped, it will release both the strong reference and the keepalive.
87 struct KeepAliveDeleter
{
88 void operator()(BrowsingContextGroup
* aPtr
) {
89 if (RefPtr
<BrowsingContextGroup
> ptr
= already_AddRefed(aPtr
)) {
90 ptr
->RemoveKeepAlive();
94 using KeepAlivePtr
= UniquePtr
<BrowsingContextGroup
, KeepAliveDeleter
>;
95 KeepAlivePtr
MakeKeepAlivePtr();
97 // Call when we want to check if we should suspend or resume all top level
99 void UpdateToplevelsSuspendedIfNeeded();
101 // Get a reference to the list of toplevel contexts in this
102 // BrowsingContextGroup.
103 nsTArray
<RefPtr
<BrowsingContext
>>& Toplevels() { return mToplevels
; }
104 void GetToplevels(nsTArray
<RefPtr
<BrowsingContext
>>& aToplevels
) {
105 aToplevels
.AppendElements(mToplevels
);
108 uint64_t Id() { return mId
; }
110 nsISupports
* GetParentObject() const;
111 JSObject
* WrapObject(JSContext
* aCx
,
112 JS::Handle
<JSObject
*> aGivenProto
) override
;
114 // Get or create a BrowsingContextGroup with the given ID.
115 static already_AddRefed
<BrowsingContextGroup
> GetOrCreate(uint64_t aId
);
116 static already_AddRefed
<BrowsingContextGroup
> GetExisting(uint64_t aId
);
117 static already_AddRefed
<BrowsingContextGroup
> Create(
118 bool aPotentiallyCrossOriginIsolated
= false);
119 static already_AddRefed
<BrowsingContextGroup
> Select(
120 WindowContext
* aParent
, BrowsingContext
* aOpener
);
122 // Like `Create` but only generates and reserves a new ID without actually
123 // creating the BrowsingContextGroup object.
124 static uint64_t CreateId(bool aPotentiallyCrossOriginIsolated
= false);
126 // For each 'ContentParent', except for 'aExcludedParent',
127 // associated with this group call 'aCallback'.
128 template <typename Func
>
129 void EachOtherParent(ContentParent
* aExcludedParent
, Func
&& aCallback
) {
130 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
131 for (const auto& key
: mSubscribers
) {
132 if (key
!= aExcludedParent
) {
138 // For each 'ContentParent' associated with
139 // this group call 'aCallback'.
140 template <typename Func
>
141 void EachParent(Func
&& aCallback
) {
142 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
143 for (const auto& key
: mSubscribers
) {
148 nsresult
QueuePostMessageEvent(already_AddRefed
<nsIRunnable
>&& aRunnable
);
150 void FlushPostMessageEvents();
152 // Increase or decrease the suspension level in InputTaskManager
153 void UpdateInputTaskManagerIfNeeded(bool aIsActive
);
155 static BrowsingContextGroup
* GetChromeGroup();
157 void GetDocGroups(nsTArray
<DocGroup
*>& aDocGroups
);
159 // Called by Document when a Document needs to be added to a DocGroup.
160 already_AddRefed
<DocGroup
> AddDocument(const nsACString
& aKey
,
161 Document
* aDocument
);
163 // Called by Document when a Document needs to be removed from a DocGroup.
164 // aDocGroup should be from aDocument. This is done to avoid the assert
165 // in GetDocGroup() which can crash when called during unlinking.
166 void RemoveDocument(Document
* aDocument
, DocGroup
* aDocGroup
);
168 mozilla::ThrottledEventQueue
* GetTimerEventQueue() const {
169 return mTimerEventQueue
;
172 mozilla::ThrottledEventQueue
* GetWorkerEventQueue() const {
173 return mWorkerEventQueue
;
176 void SetAreDialogsEnabled(bool aAreDialogsEnabled
) {
177 mAreDialogsEnabled
= aAreDialogsEnabled
;
180 bool GetAreDialogsEnabled() { return mAreDialogsEnabled
; }
182 bool GetDialogAbuseCount() { return mDialogAbuseCount
; }
185 void ResetDialogAbuseState();
187 bool DialogsAreBeingAbused();
189 TimeStamp
GetLastDialogQuitTime() { return mLastDialogQuitTime
; }
191 void SetLastDialogQuitTime(TimeStamp aLastDialogQuitTime
) {
192 mLastDialogQuitTime
= aLastDialogQuitTime
;
195 // Whether all toplevel documents loaded in this group are allowed to be
196 // Cross-Origin Isolated.
198 // This does not reflect the actual value of `crossOriginIsolated`, as that
199 // also requires that the document is loaded within a `webCOOP+COEP` content
201 bool IsPotentiallyCrossOriginIsolated();
203 static void GetAllGroups(nsTArray
<RefPtr
<BrowsingContextGroup
>>& aGroups
);
205 void IncInputEventSuspensionLevel();
206 void DecInputEventSuspensionLevel();
211 friend class CanonicalBrowsingContext
;
213 explicit BrowsingContextGroup(uint64_t aId
);
214 ~BrowsingContextGroup();
219 bool ShouldSuspendAllTopLevelContexts() const;
222 void DecInputTaskManagerSuspensionLevel();
223 void IncInputTaskManagerSuspensionLevel();
227 uint32_t mKeepAliveCount
= 0;
229 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
230 bool mDestroyed
= false;
233 // A BrowsingContextGroup contains a series of {Browsing,Window}Context
234 // objects. They are addressed using a hashtable to avoid linear lookup when
235 // adding or removing elements from the set.
237 // FIXME: This list is only required over a counter to keep nested
238 // non-discarded contexts within discarded contexts alive. It should be
239 // removed in the future.
240 // FIXME: Consider introducing a better common base than `nsISupports`?
241 nsTHashSet
<nsRefPtrHashKey
<nsISupports
>> mContexts
;
243 // The set of toplevel browsing contexts in the current BrowsingContextGroup.
244 nsTArray
<RefPtr
<BrowsingContext
>> mToplevels
;
246 // Whether or not all toplevels in this group should be suspended
247 bool mToplevelsSuspended
= false;
249 // DocGroups are thread-safe, and not able to be cycle collected,
250 // but we still keep strong pointers. When all Documents are removed
251 // from DocGroup (by the BrowsingContextGroup), the DocGroup is
252 // removed from here.
253 nsRefPtrHashtable
<nsCStringHashKey
, DocGroup
> mDocGroups
;
255 // The content process which will host documents in this BrowsingContextGroup
256 // which need to be loaded with a given remote type.
258 // A non-launching host process must also be a subscriber, though a launching
259 // host process may not yet be subscribed, and a subscriber need not be a host
261 nsRefPtrHashtable
<nsCStringHashKey
, ContentParent
> mHosts
;
263 nsTHashSet
<nsRefPtrHashKey
<ContentParent
>> mSubscribers
;
265 // A queue to store postMessage events during page load, the queue will be
266 // flushed once the page is loaded
267 RefPtr
<mozilla::ThrottledEventQueue
> mPostMessageEventQueue
;
269 RefPtr
<mozilla::ThrottledEventQueue
> mTimerEventQueue
;
270 RefPtr
<mozilla::ThrottledEventQueue
> mWorkerEventQueue
;
272 // A counter to keep track of the input event suspension level of this BCG
274 // We use BrowsingContextGroup to emulate process isolation in Fission, so
275 // documents within the same the same BCG will behave like they share
276 // the same input task queue.
277 uint32_t mInputEventSuspensionLevel
= 0;
278 // Whether this BCG has increased the suspension level in InputTaskManager
279 bool mHasIncreasedInputTaskManagerSuspensionLevel
= false;
281 // This flag keeps track of whether dialogs are
282 // currently enabled for windows of this group.
283 // It's OK to have these local to each process only because even if
284 // frames from two/three different sites (and thus, processes) coordinate a
285 // dialog abuse attack, they would only the double/triple number of dialogs,
286 // as it is still limited per-site.
287 bool mAreDialogsEnabled
= true;
289 // This counts the number of windows that have been opened in rapid succession
290 // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
291 // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
292 // have elapsed without any other dialogs.
293 // See comment for mAreDialogsEnabled as to why it's ok to have this local to
295 uint32_t mDialogAbuseCount
= 0;
297 // This holds the time when the last modal dialog was shown. If more than
298 // MAX_DIALOG_LIMIT dialogs are shown within the time span defined by
299 // dom.successive_dialog_time_limit, we show a checkbox or confirmation prompt
300 // to allow disabling of further dialogs from windows in this BC group.
301 TimeStamp mLastDialogQuitTime
;
304 } // namespace mozilla
306 inline void ImplCycleCollectionUnlink(
307 mozilla::dom::BrowsingContextGroup::KeepAlivePtr
& aField
) {
311 inline void ImplCycleCollectionTraverse(
312 nsCycleCollectionTraversalCallback
& aCallback
,
313 mozilla::dom::BrowsingContextGroup::KeepAlivePtr
& aField
, const char* aName
,
314 uint32_t aFlags
= 0) {
315 CycleCollectionNoteChild(aCallback
, aField
.get(), aName
, aFlags
);
318 #endif // !defined(mozilla_dom_BrowsingContextGroup_h)