summaryrefslogtreecommitdiffstats
path: root/docshell/base/BrowsingContextGroup.h
blob: fb1f2e528c32adca52fc7ef724958ecd7cd69af2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_BrowsingContextGroup_h
#define mozilla_dom_BrowsingContextGroup_h

#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/FunctionRef.h"
#include "nsRefPtrHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsTHashSet.h"
#include "nsWrapperCache.h"
#include "nsXULAppAPI.h"

namespace mozilla {
class ThrottledEventQueue;

namespace dom {

// Amount of time allowed between alert/prompt/confirm before enabling
// the stop dialog checkbox.
#define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3  // 3 sec

class BrowsingContext;
class WindowContext;
class ContentParent;
class DocGroup;

// A BrowsingContextGroup represents the Unit of Related Browsing Contexts in
// the standard.
//
// A BrowsingContext may not hold references to other BrowsingContext objects
// which are not in the same BrowsingContextGroup.
class BrowsingContextGroup final : public nsWrapperCache {
 public:
  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContextGroup)
  NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(BrowsingContextGroup)

  // Interact with the list of synced contexts. This controls the lifecycle of
  // the BrowsingContextGroup and contexts loaded within them.
  void Register(nsISupports* aContext);
  void Unregister(nsISupports* aContext);

  // Control which processes will be used to host documents loaded in this
  // BrowsingContextGroup. There should only ever be one host process per remote
  // type.
  //
  // A new host process will be subscribed to the BrowsingContextGroup unless it
  // is still launching, in which case it will subscribe itself when it is done
  // launching.
  void EnsureHostProcess(ContentParent* aProcess);

  // A removed host process will no longer be used to host documents loaded in
  // this BrowsingContextGroup.
  void RemoveHostProcess(ContentParent* aProcess);

  // Synchronize the current BrowsingContextGroup state down to the given
  // content process, and continue updating it.
  //
  // You rarely need to call this directly, as it's automatically called by
  // |EnsureHostProcess| as needed.
  void Subscribe(ContentParent* aProcess);

  // Stop synchronizing the current BrowsingContextGroup state down to a given
  // content process. The content process must no longer be a host process.
  void Unsubscribe(ContentParent* aProcess);

  // Look up the process which should be used to host documents with this
  // RemoteType. This will be a non-dead process associated with this
  // BrowsingContextGroup, if possible.
  ContentParent* GetHostProcess(const nsACString& aRemoteType);

  // When a BrowsingContext is being discarded, we may want to keep the
  // corresponding BrowsingContextGroup alive until the other process
  // acknowledges that the BrowsingContext has been discarded. A `KeepAlive`
  // will be added to the `BrowsingContextGroup`, delaying destruction.
  void AddKeepAlive();
  void RemoveKeepAlive();

  // A `KeepAlivePtr` will hold both a strong reference to the
  // `BrowsingContextGroup` and holds a `KeepAlive`. When the pointer is
  // dropped, it will release both the strong reference and the keepalive.
  struct KeepAliveDeleter {
    void operator()(BrowsingContextGroup* aPtr) {
      if (RefPtr<BrowsingContextGroup> ptr = already_AddRefed(aPtr)) {
        ptr->RemoveKeepAlive();
      }
    }
  };
  using KeepAlivePtr = UniquePtr<BrowsingContextGroup, KeepAliveDeleter>;
  KeepAlivePtr MakeKeepAlivePtr();

  // Call when we want to check if we should suspend or resume all top level
  // contexts.
  void UpdateToplevelsSuspendedIfNeeded();

  // Get a reference to the list of toplevel contexts in this
  // BrowsingContextGroup.
  nsTArray<RefPtr<BrowsingContext>>& Toplevels() { return mToplevels; }
  void GetToplevels(nsTArray<RefPtr<BrowsingContext>>& aToplevels) {
    aToplevels.AppendElements(mToplevels);
  }

  uint64_t Id() { return mId; }

  nsISupports* GetParentObject() const;
  JSObject* WrapObject(JSContext* aCx,
                       JS::Handle<JSObject*> aGivenProto) override;

  // Get or create a BrowsingContextGroup with the given ID.
  static already_AddRefed<BrowsingContextGroup> GetOrCreate(uint64_t aId);
  static already_AddRefed<BrowsingContextGroup> GetExisting(uint64_t aId);
  static already_AddRefed<BrowsingContextGroup> Create(
      bool aPotentiallyCrossOriginIsolated = false);
  static already_AddRefed<BrowsingContextGroup> Select(
      WindowContext* aParent, BrowsingContext* aOpener);

  // Like `Create` but only generates and reserves a new ID without actually
  // creating the BrowsingContextGroup object.
  static uint64_t CreateId(bool aPotentiallyCrossOriginIsolated = false);

  // For each 'ContentParent', except for 'aExcludedParent',
  // associated with this group call 'aCallback'.
  template <typename Func>
  void EachOtherParent(ContentParent* aExcludedParent, Func&& aCallback) {
    MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
    for (const auto& key : mSubscribers) {
      if (key != aExcludedParent) {
        aCallback(key);
      }
    }
  }

  // For each 'ContentParent' associated with
  // this group call 'aCallback'.
  template <typename Func>
  void EachParent(Func&& aCallback) {
    MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
    for (const auto& key : mSubscribers) {
      aCallback(key);
    }
  }

  nsresult QueuePostMessageEvent(already_AddRefed<nsIRunnable>&& aRunnable);

  void FlushPostMessageEvents();

  // Increase or decrease the suspension level in InputTaskManager
  void UpdateInputTaskManagerIfNeeded(bool aIsActive);

  static BrowsingContextGroup* GetChromeGroup();

  void GetDocGroups(nsTArray<DocGroup*>& aDocGroups);

  // Called by Document when a Document needs to be added to a DocGroup.
  already_AddRefed<DocGroup> AddDocument(const nsACString& aKey,
                                         Document* aDocument);

  // Called by Document when a Document needs to be removed from a DocGroup.
  // aDocGroup should be from aDocument. This is done to avoid the assert
  // in GetDocGroup() which can crash when called during unlinking.
  void RemoveDocument(Document* aDocument, DocGroup* aDocGroup);

  mozilla::ThrottledEventQueue* GetTimerEventQueue() const {
    return mTimerEventQueue;
  }

  mozilla::ThrottledEventQueue* GetWorkerEventQueue() const {
    return mWorkerEventQueue;
  }

  void SetAreDialogsEnabled(bool aAreDialogsEnabled) {
    mAreDialogsEnabled = aAreDialogsEnabled;
  }

  bool GetAreDialogsEnabled() { return mAreDialogsEnabled; }

  bool GetDialogAbuseCount() { return mDialogAbuseCount; }

  // For tests only.
  void ResetDialogAbuseState();

  bool DialogsAreBeingAbused();

  TimeStamp GetLastDialogQuitTime() { return mLastDialogQuitTime; }

  void SetLastDialogQuitTime(TimeStamp aLastDialogQuitTime) {
    mLastDialogQuitTime = aLastDialogQuitTime;
  }

  // Whether all toplevel documents loaded in this group are allowed to be
  // Cross-Origin Isolated.
  //
  // This does not reflect the actual value of `crossOriginIsolated`, as that
  // also requires that the document is loaded within a `webCOOP+COEP` content
  // process.
  bool IsPotentiallyCrossOriginIsolated();

  static void GetAllGroups(nsTArray<RefPtr<BrowsingContextGroup>>& aGroups);

  void IncInputEventSuspensionLevel();
  void DecInputEventSuspensionLevel();

  void ChildDestroy();

 private:
  friend class CanonicalBrowsingContext;

  explicit BrowsingContextGroup(uint64_t aId);
  ~BrowsingContextGroup();

  void MaybeDestroy();
  void Destroy();

  bool ShouldSuspendAllTopLevelContexts() const;

  bool HasActiveBC();
  void DecInputTaskManagerSuspensionLevel();
  void IncInputTaskManagerSuspensionLevel();

  uint64_t mId;

  uint32_t mKeepAliveCount = 0;

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  bool mDestroyed = false;
#endif

  // A BrowsingContextGroup contains a series of {Browsing,Window}Context
  // objects. They are addressed using a hashtable to avoid linear lookup when
  // adding or removing elements from the set.
  //
  // FIXME: This list is only required over a counter to keep nested
  // non-discarded contexts within discarded contexts alive. It should be
  // removed in the future.
  // FIXME: Consider introducing a better common base than `nsISupports`?
  nsTHashSet<nsRefPtrHashKey<nsISupports>> mContexts;

  // The set of toplevel browsing contexts in the current BrowsingContextGroup.
  nsTArray<RefPtr<BrowsingContext>> mToplevels;

  //  Whether or not all toplevels in this group should be suspended
  bool mToplevelsSuspended = false;

  // DocGroups are thread-safe, and not able to be cycle collected,
  // but we still keep strong pointers. When all Documents are removed
  // from DocGroup (by the BrowsingContextGroup), the DocGroup is
  // removed from here.
  nsRefPtrHashtable<nsCStringHashKey, DocGroup> mDocGroups;

  // The content process which will host documents in this BrowsingContextGroup
  // which need to be loaded with a given remote type.
  //
  // A non-launching host process must also be a subscriber, though a launching
  // host process may not yet be subscribed, and a subscriber need not be a host
  // process.
  nsRefPtrHashtable<nsCStringHashKey, ContentParent> mHosts;

  nsTHashSet<nsRefPtrHashKey<ContentParent>> mSubscribers;

  // A queue to store postMessage events during page load, the queue will be
  // flushed once the page is loaded
  RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;

  RefPtr<mozilla::ThrottledEventQueue> mTimerEventQueue;
  RefPtr<mozilla::ThrottledEventQueue> mWorkerEventQueue;

  // A counter to keep track of the input event suspension level of this BCG
  //
  // We use BrowsingContextGroup to emulate process isolation in Fission, so
  // documents within the same the same BCG will behave like they share
  // the same input task queue.
  uint32_t mInputEventSuspensionLevel = 0;
  // Whether this BCG has increased the suspension level in InputTaskManager
  bool mHasIncreasedInputTaskManagerSuspensionLevel = false;

  // This flag keeps track of whether dialogs are
  // currently enabled for windows of this group.
  // It's OK to have these local to each process only because even if
  // frames from two/three different sites (and thus, processes) coordinate a
  // dialog abuse attack, they would only the double/triple number of dialogs,
  // as it is still limited per-site.
  bool mAreDialogsEnabled = true;

  // This counts the number of windows that have been opened in rapid succession
  // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
  // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
  // have elapsed without any other dialogs.
  // See comment for mAreDialogsEnabled as to why it's ok to have this local to
  // each process.
  uint32_t mDialogAbuseCount = 0;

  // This holds the time when the last modal dialog was shown. If more than
  // MAX_DIALOG_LIMIT dialogs are shown within the time span defined by
  // dom.successive_dialog_time_limit, we show a checkbox or confirmation prompt
  // to allow disabling of further dialogs from windows in this BC group.
  TimeStamp mLastDialogQuitTime;
};
}  // namespace dom
}  // namespace mozilla

inline void ImplCycleCollectionUnlink(
    mozilla::dom::BrowsingContextGroup::KeepAlivePtr& aField) {
  aField = nullptr;
}

inline void ImplCycleCollectionTraverse(
    nsCycleCollectionTraversalCallback& aCallback,
    mozilla::dom::BrowsingContextGroup::KeepAlivePtr& aField, const char* aName,
    uint32_t aFlags = 0) {
  CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
}

#endif  // !defined(mozilla_dom_BrowsingContextGroup_h)