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/. */
6
7 #ifndef mozilla_dom_BrowsingContextGroup_h
8 #define mozilla_dom_BrowsingContextGroup_h
9
10 #include "mozilla/dom/BrowsingContext.h"
11 #include "mozilla/FunctionRef.h"
12 #include "nsRefPtrHashtable.h"
13 #include "nsHashKeys.h"
14 #include "nsTArray.h"
15 #include "nsTHashSet.h"
16 #include "nsWrapperCache.h"
17 #include "nsXULAppAPI.h"
18
19 namespace mozilla {
20 class ThrottledEventQueue;
21
22 namespace dom {
23
24 class BrowsingContext;
25 class WindowContext;
26 class ContentParent;
27 class DocGroup;
28
29 // A BrowsingContextGroup represents the Unit of Related Browsing Contexts in
30 // the standard.
31 //
32 // A BrowsingContext may not hold references to other BrowsingContext objects
33 // which are not in the same BrowsingContextGroup.
34 class BrowsingContextGroup final : public nsWrapperCache {
35 public:
36 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContextGroup)
37 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(BrowsingContextGroup)
38
39 // Interact with the list of synced contexts. This controls the lifecycle of
40 // the BrowsingContextGroup and contexts loaded within them.
41 void Register(nsISupports* aContext);
42 void Unregister(nsISupports* aContext);
43
44 // Control which processes will be used to host documents loaded in this
45 // BrowsingContextGroup. There should only ever be one host process per remote
46 // type.
47 //
48 // A new host process will be subscribed to the BrowsingContextGroup unless it
49 // is still launching, in which case it will subscribe itself when it is done
50 // launching.
51 void EnsureHostProcess(ContentParent* aProcess);
52
53 // A removed host process will no longer be used to host documents loaded in
54 // this BrowsingContextGroup.
55 void RemoveHostProcess(ContentParent* aProcess);
56
57 // Synchronize the current BrowsingContextGroup state down to the given
58 // content process, and continue updating it.
59 //
60 // You rarely need to call this directly, as it's automatically called by
61 // |EnsureHostProcess| as needed.
62 void Subscribe(ContentParent* aProcess);
63
64 // Stop synchronizing the current BrowsingContextGroup state down to a given
65 // content process. The content process must no longer be a host process.
66 void Unsubscribe(ContentParent* aProcess);
67
68 // Look up the process which should be used to host documents with this
69 // RemoteType. This will be a non-dead process associated with this
70 // BrowsingContextGroup, if possible.
71 ContentParent* GetHostProcess(const nsACString& aRemoteType);
72
73 // When a BrowsingContext is being discarded, we may want to keep the
74 // corresponding BrowsingContextGroup alive until the other process
75 // acknowledges that the BrowsingContext has been discarded. A `KeepAlive`
76 // will be added to the `BrowsingContextGroup`, delaying destruction.
77 void AddKeepAlive();
78 void RemoveKeepAlive();
79
80 // A `KeepAlivePtr` will hold both a strong reference to the
81 // `BrowsingContextGroup` and holds a `KeepAlive`. When the pointer is
82 // dropped, it will release both the strong reference and the keepalive.
83 struct KeepAliveDeleter {
operatorKeepAliveDeleter84 void operator()(BrowsingContextGroup* aPtr) {
85 if (RefPtr<BrowsingContextGroup> ptr = already_AddRefed(aPtr)) {
86 ptr->RemoveKeepAlive();
87 }
88 }
89 };
90 using KeepAlivePtr = UniquePtr<BrowsingContextGroup, KeepAliveDeleter>;
91 KeepAlivePtr MakeKeepAlivePtr();
92
93 // Call when we want to check if we should suspend or resume all top level
94 // contexts.
95 void UpdateToplevelsSuspendedIfNeeded();
96
97 // Get a reference to the list of toplevel contexts in this
98 // BrowsingContextGroup.
Toplevels()99 nsTArray<RefPtr<BrowsingContext>>& Toplevels() { return mToplevels; }
GetToplevels(nsTArray<RefPtr<BrowsingContext>> & aToplevels)100 void GetToplevels(nsTArray<RefPtr<BrowsingContext>>& aToplevels) {
101 aToplevels.AppendElements(mToplevels);
102 }
103
Id()104 uint64_t Id() { return mId; }
105
106 nsISupports* GetParentObject() const;
107 JSObject* WrapObject(JSContext* aCx,
108 JS::Handle<JSObject*> aGivenProto) override;
109
110 // Get or create a BrowsingContextGroup with the given ID.
111 static already_AddRefed<BrowsingContextGroup> GetOrCreate(uint64_t aId);
112 static already_AddRefed<BrowsingContextGroup> GetExisting(uint64_t aId);
113 static already_AddRefed<BrowsingContextGroup> Create();
114 static already_AddRefed<BrowsingContextGroup> Select(
115 WindowContext* aParent, BrowsingContext* aOpener);
116
117 // For each 'ContentParent', except for 'aExcludedParent',
118 // associated with this group call 'aCallback'.
119 template <typename Func>
EachOtherParent(ContentParent * aExcludedParent,Func && aCallback)120 void EachOtherParent(ContentParent* aExcludedParent, Func&& aCallback) {
121 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
122 for (const auto& key : mSubscribers) {
123 if (key != aExcludedParent) {
124 aCallback(key);
125 }
126 }
127 }
128
129 // For each 'ContentParent' associated with
130 // this group call 'aCallback'.
131 template <typename Func>
EachParent(Func && aCallback)132 void EachParent(Func&& aCallback) {
133 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
134 for (const auto& key : mSubscribers) {
135 aCallback(key);
136 }
137 }
138
139 nsresult QueuePostMessageEvent(already_AddRefed<nsIRunnable>&& aRunnable);
140
141 void FlushPostMessageEvents();
142
143 // Increase or decrease the suspension level in InputTaskManager
144 void UpdateInputTaskManagerIfNeeded(bool aIsActive);
145
146 static BrowsingContextGroup* GetChromeGroup();
147
148 void GetDocGroups(nsTArray<DocGroup*>& aDocGroups);
149
150 // Called by Document when a Document needs to be added to a DocGroup.
151 already_AddRefed<DocGroup> AddDocument(const nsACString& aKey,
152 Document* aDocument);
153
154 // Called by Document when a Document needs to be removed from a DocGroup.
155 // aDocGroup should be from aDocument. This is done to avoid the assert
156 // in GetDocGroup() which can crash when called during unlinking.
157 void RemoveDocument(Document* aDocument, DocGroup* aDocGroup);
158
GetTimerEventQueue()159 mozilla::ThrottledEventQueue* GetTimerEventQueue() const {
160 return mTimerEventQueue;
161 }
162
GetWorkerEventQueue()163 mozilla::ThrottledEventQueue* GetWorkerEventQueue() const {
164 return mWorkerEventQueue;
165 }
166
167 static void GetAllGroups(nsTArray<RefPtr<BrowsingContextGroup>>& aGroups);
168
169 void IncInputEventSuspensionLevel();
170 void DecInputEventSuspensionLevel();
171
172 void ChildDestroy();
173
174 private:
175 friend class CanonicalBrowsingContext;
176
177 explicit BrowsingContextGroup(uint64_t aId);
178 ~BrowsingContextGroup();
179
180 void MaybeDestroy();
181 void Destroy();
182
183 bool ShouldSuspendAllTopLevelContexts() const;
184
185 bool HasActiveBC();
186 void DecInputTaskManagerSuspensionLevel();
187 void IncInputTaskManagerSuspensionLevel();
188
189 uint64_t mId;
190
191 uint32_t mKeepAliveCount = 0;
192
193 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
194 bool mDestroyed = false;
195 #endif
196
197 // A BrowsingContextGroup contains a series of {Browsing,Window}Context
198 // objects. They are addressed using a hashtable to avoid linear lookup when
199 // adding or removing elements from the set.
200 //
201 // FIXME: This list is only required over a counter to keep nested
202 // non-discarded contexts within discarded contexts alive. It should be
203 // removed in the future.
204 // FIXME: Consider introducing a better common base than `nsISupports`?
205 nsTHashSet<nsRefPtrHashKey<nsISupports>> mContexts;
206
207 // The set of toplevel browsing contexts in the current BrowsingContextGroup.
208 nsTArray<RefPtr<BrowsingContext>> mToplevels;
209
210 // Whether or not all toplevels in this group should be suspended
211 bool mToplevelsSuspended = false;
212
213 // DocGroups are thread-safe, and not able to be cycle collected,
214 // but we still keep strong pointers. When all Documents are removed
215 // from DocGroup (by the BrowsingContextGroup), the DocGroup is
216 // removed from here.
217 nsRefPtrHashtable<nsCStringHashKey, DocGroup> mDocGroups;
218
219 // The content process which will host documents in this BrowsingContextGroup
220 // which need to be loaded with a given remote type.
221 //
222 // A non-launching host process must also be a subscriber, though a launching
223 // host process may not yet be subscribed, and a subscriber need not be a host
224 // process.
225 nsRefPtrHashtable<nsCStringHashKey, ContentParent> mHosts;
226
227 nsTHashSet<nsRefPtrHashKey<ContentParent>> mSubscribers;
228
229 // A queue to store postMessage events during page load, the queue will be
230 // flushed once the page is loaded
231 RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
232
233 RefPtr<mozilla::ThrottledEventQueue> mTimerEventQueue;
234 RefPtr<mozilla::ThrottledEventQueue> mWorkerEventQueue;
235
236 // A counter to keep track of the input event suspension level of this BCG
237 //
238 // We use BrowsingContextGroup to emulate process isolation in Fission, so
239 // documents within the same the same BCG will behave like they share
240 // the same input task queue.
241 uint32_t mInputEventSuspensionLevel = 0;
242 // Whether this BCG has increased the suspension level in InputTaskManager
243 bool mHasIncreasedInputTaskManagerSuspensionLevel = false;
244 };
245 } // namespace dom
246 } // namespace mozilla
247
ImplCycleCollectionUnlink(mozilla::dom::BrowsingContextGroup::KeepAlivePtr & aField)248 inline void ImplCycleCollectionUnlink(
249 mozilla::dom::BrowsingContextGroup::KeepAlivePtr& aField) {
250 aField = nullptr;
251 }
252
253 inline void ImplCycleCollectionTraverse(
254 nsCycleCollectionTraversalCallback& aCallback,
255 mozilla::dom::BrowsingContextGroup::KeepAlivePtr& aField, const char* aName,
256 uint32_t aFlags = 0) {
257 CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
258 }
259
260 #endif // !defined(mozilla_dom_BrowsingContextGroup_h)
261