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