tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BrowsingContextGroup.h (13755B)


      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/PrincipalHashKey.h"
     11 #include "mozilla/dom/BrowsingContext.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 // 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
     27 
     28 class BrowsingContext;
     29 class WindowContext;
     30 class ContentParent;
     31 class DocGroup;
     32 
     33 struct DocGroupKey {
     34  nsCString mKey;
     35  bool mOriginKeyed = false;
     36 
     37  bool operator==(const DocGroupKey& aOther) const {
     38    return mKey == aOther.mKey && mOriginKeyed == aOther.mOriginKeyed;
     39  };
     40  PLDHashNumber Hash() const {
     41    return mozilla::HashGeneric(mozilla::HashString(mKey), mOriginKeyed);
     42  }
     43 };
     44 
     45 // A BrowsingContextGroup represents the Unit of Related Browsing Contexts in
     46 // the standard.
     47 //
     48 // A BrowsingContext may not hold references to other BrowsingContext objects
     49 // which are not in the same BrowsingContextGroup.
     50 class BrowsingContextGroup final : public nsWrapperCache {
     51 public:
     52  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BrowsingContextGroup)
     53  NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(BrowsingContextGroup)
     54 
     55  // Interact with the list of synced contexts. This controls the lifecycle of
     56  // the BrowsingContextGroup and contexts loaded within them.
     57  void Register(nsISupports* aContext);
     58  void Unregister(nsISupports* aContext);
     59 
     60  // Control which processes will be used to host documents loaded in this
     61  // BrowsingContextGroup. There should only ever be one host process per remote
     62  // type.
     63  //
     64  // A new host process will be subscribed to the BrowsingContextGroup unless it
     65  // is still launching, in which case it will subscribe itself when it is done
     66  // launching.
     67  void EnsureHostProcess(ContentParent* aProcess);
     68 
     69  // A removed host process will no longer be used to host documents loaded in
     70  // this BrowsingContextGroup.
     71  void RemoveHostProcess(ContentParent* aProcess);
     72 
     73  // Synchronize the current BrowsingContextGroup state down to the given
     74  // content process, and continue updating it.
     75  //
     76  // You rarely need to call this directly, as it's automatically called by
     77  // |EnsureHostProcess| as needed.
     78  void Subscribe(ContentParent* aProcess);
     79 
     80  // Stop synchronizing the current BrowsingContextGroup state down to a given
     81  // content process. The content process must no longer be a host process.
     82  void Unsubscribe(ContentParent* aProcess);
     83 
     84  // Look up the process which should be used to host documents with this
     85  // RemoteType. This will be a non-dead process associated with this
     86  // BrowsingContextGroup, if possible.
     87  ContentParent* GetHostProcess(const nsACString& aRemoteType);
     88 
     89  // When a BrowsingContext is being discarded, we may want to keep the
     90  // corresponding BrowsingContextGroup alive until the other process
     91  // acknowledges that the BrowsingContext has been discarded. A `KeepAlive`
     92  // will be added to the `BrowsingContextGroup`, delaying destruction.
     93  void AddKeepAlive();
     94  void RemoveKeepAlive();
     95 
     96  // A `KeepAlivePtr` will hold both a strong reference to the
     97  // `BrowsingContextGroup` and holds a `KeepAlive`. When the pointer is
     98  // dropped, it will release both the strong reference and the keepalive.
     99  struct KeepAliveDeleter {
    100    void operator()(BrowsingContextGroup* aPtr) {
    101      if (RefPtr<BrowsingContextGroup> ptr = already_AddRefed(aPtr)) {
    102        ptr->RemoveKeepAlive();
    103      }
    104    }
    105  };
    106  using KeepAlivePtr = UniquePtr<BrowsingContextGroup, KeepAliveDeleter>;
    107  KeepAlivePtr MakeKeepAlivePtr();
    108 
    109  // Call when we want to check if we should suspend or resume all top level
    110  // contexts.
    111  void UpdateToplevelsSuspendedIfNeeded();
    112 
    113  // Get a reference to the list of toplevel contexts in this
    114  // BrowsingContextGroup.
    115  nsTArray<RefPtr<BrowsingContext>>& Toplevels() { return mToplevels; }
    116  void GetToplevels(nsTArray<RefPtr<BrowsingContext>>& aToplevels) {
    117    aToplevels.AppendElements(mToplevels);
    118  }
    119 
    120  uint64_t Id() { return mId; }
    121 
    122  nsISupports* GetParentObject() const;
    123  JSObject* WrapObject(JSContext* aCx,
    124                       JS::Handle<JSObject*> aGivenProto) override;
    125 
    126  // Get or create a BrowsingContextGroup with the given ID.
    127  static already_AddRefed<BrowsingContextGroup> GetOrCreate(uint64_t aId);
    128  static already_AddRefed<BrowsingContextGroup> GetExisting(uint64_t aId);
    129  static already_AddRefed<BrowsingContextGroup> Create(
    130      bool aPotentiallyCrossOriginIsolated = false);
    131  static already_AddRefed<BrowsingContextGroup> Select(
    132      WindowContext* aParent, BrowsingContext* aOpener);
    133 
    134  // Like `Create` but only generates and reserves a new ID without actually
    135  // creating the BrowsingContextGroup object.
    136  static uint64_t CreateId(bool aPotentiallyCrossOriginIsolated = false);
    137 
    138  // For each 'ContentParent', except for 'aExcludedParent',
    139  // associated with this group call 'aCallback'.
    140  template <typename Func>
    141  void EachOtherParent(ContentParent* aExcludedParent, Func&& aCallback) {
    142    MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
    143    for (const auto& key : mSubscribers) {
    144      if (key != aExcludedParent) {
    145        aCallback(key);
    146      }
    147    }
    148  }
    149 
    150  // For each 'ContentParent' associated with
    151  // this group call 'aCallback'.
    152  template <typename Func>
    153  void EachParent(Func&& aCallback) {
    154    MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
    155    for (const auto& key : mSubscribers) {
    156      aCallback(key);
    157    }
    158  }
    159 
    160  nsresult QueuePostMessageEvent(nsIRunnable* aRunnable);
    161 
    162  void FlushPostMessageEvents();
    163 
    164  // Increase or decrease the suspension level in InputTaskManager
    165  void UpdateInputTaskManagerIfNeeded(bool aIsActive);
    166 
    167  static BrowsingContextGroup* GetChromeGroup();
    168 
    169  void GetDocGroups(nsTArray<DocGroup*>& aDocGroups);
    170 
    171  // Called by Document when a Document needs to be added to a DocGroup.
    172  already_AddRefed<DocGroup> AddDocument(Document* aDocument);
    173 
    174  // Called by Document when a Document needs to be removed from a DocGroup.
    175  // aDocGroup should be from aDocument. This is done to avoid the assert
    176  // in GetDocGroup() which can crash when called during unlinking.
    177  void RemoveDocument(Document* aDocument, DocGroup* aDocGroup);
    178 
    179  mozilla::ThrottledEventQueue* GetTimerEventQueue() const {
    180    return mTimerEventQueue;
    181  }
    182 
    183  mozilla::ThrottledEventQueue* GetWorkerEventQueue() const {
    184    return mWorkerEventQueue;
    185  }
    186 
    187  void SetAreDialogsEnabled(bool aAreDialogsEnabled) {
    188    mAreDialogsEnabled = aAreDialogsEnabled;
    189  }
    190 
    191  bool GetAreDialogsEnabled() { return mAreDialogsEnabled; }
    192 
    193  bool GetDialogAbuseCount() { return mDialogAbuseCount; }
    194 
    195  // For tests only.
    196  void ResetDialogAbuseState();
    197 
    198  bool DialogsAreBeingAbused();
    199 
    200  TimeStamp GetLastDialogQuitTime() { return mLastDialogQuitTime; }
    201 
    202  void SetLastDialogQuitTime(TimeStamp aLastDialogQuitTime) {
    203    mLastDialogQuitTime = aLastDialogQuitTime;
    204  }
    205 
    206  // Whether all toplevel documents loaded in this group are allowed to be
    207  // Cross-Origin Isolated.
    208  //
    209  // This does not reflect the actual value of `crossOriginIsolated`, as that
    210  // also requires that the document is loaded within a `webCOOP+COEP` content
    211  // process.
    212  bool IsPotentiallyCrossOriginIsolated();
    213 
    214  void NotifyFocusedOrActiveBrowsingContextToProcess(ContentParent* aProcess);
    215 
    216  static void GetAllGroups(nsTArray<RefPtr<BrowsingContextGroup>>& aGroups);
    217 
    218  void IncInputEventSuspensionLevel();
    219  void DecInputEventSuspensionLevel();
    220 
    221  // As documents are loaded, whether or not each origin is origin-keyed is
    222  // recorded within the BrowsingContextGroup. This in turn controls whether
    223  // document.domain can be used, as well as the options available to us for
    224  // process isolation.
    225  // The relevant subset of this mapping is mirrored to content processes, to be
    226  // used when determining DocGroup keying.
    227  void SetUseOriginAgentClusterFromNetwork(nsIPrincipal* aPrincipal,
    228                                           bool aUseOriginAgentCluster);
    229  void SetUseOriginAgentClusterFromIPC(nsIPrincipal* aPrincipal,
    230                                       bool aUseOriginAgentCluster);
    231  Maybe<bool> UsesOriginAgentCluster(nsIPrincipal* aPrincipal);
    232 
    233  // Ensures that the given principal will return `Some(...)` from
    234  // `UsesOriginAgentCluster` going forward, setting it to a default value if no
    235  // value is set.
    236  // As the map can only be modified in the parent process, this method will
    237  // crash if there is no CrossOriginIsolatedStatus for the principal when
    238  // called in a content process.
    239  void EnsureUsesOriginAgentClusterInitialized(nsIPrincipal* aPrincipal);
    240 
    241  void ChildDestroy();
    242 
    243 private:
    244  friend class CanonicalBrowsingContext;
    245 
    246  explicit BrowsingContextGroup(uint64_t aId);
    247  ~BrowsingContextGroup();
    248 
    249  void MaybeDestroy();
    250  void Destroy();
    251 
    252  bool ShouldSuspendAllTopLevelContexts() const;
    253 
    254  bool HasActiveBC();
    255  void DecInputTaskManagerSuspensionLevel();
    256  void IncInputTaskManagerSuspensionLevel();
    257 
    258  uint64_t mId;
    259 
    260  uint32_t mKeepAliveCount = 0;
    261 
    262 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    263  bool mDestroyed = false;
    264 #endif
    265 
    266  // A BrowsingContextGroup contains a series of {Browsing,Window}Context
    267  // objects. They are addressed using a hashtable to avoid linear lookup when
    268  // adding or removing elements from the set.
    269  //
    270  // FIXME: This list is only required over a counter to keep nested
    271  // non-discarded contexts within discarded contexts alive. It should be
    272  // removed in the future.
    273  // FIXME: Consider introducing a better common base than `nsISupports`?
    274  nsTHashSet<nsRefPtrHashKey<nsISupports>> mContexts;
    275 
    276  // The set of toplevel browsing contexts in the current BrowsingContextGroup.
    277  nsTArray<RefPtr<BrowsingContext>> mToplevels;
    278 
    279  //  Whether or not all toplevels in this group should be suspended
    280  bool mToplevelsSuspended = false;
    281 
    282  // DocGroups are thread-safe, and not able to be cycle collected,
    283  // but we still keep strong pointers. When all Documents are removed
    284  // from DocGroup (by the BrowsingContextGroup), the DocGroup is
    285  // removed from here.
    286  nsRefPtrHashtable<nsGenericHashKey<DocGroupKey>, DocGroup> mDocGroups;
    287 
    288  // The content process which will host documents in this BrowsingContextGroup
    289  // which need to be loaded with a given remote type.
    290  //
    291  // A non-launching host process must also be a subscriber, though a launching
    292  // host process may not yet be subscribed, and a subscriber need not be a host
    293  // process.
    294  nsRefPtrHashtable<nsCStringHashKey, ContentParent> mHosts;
    295 
    296  // Whether or not a given http(s) origin uses origin or siteOrigin-keyed
    297  // DocGroups/AgentClusters. Only contains entries for http(s) origins.
    298  //
    299  // https://html.spec.whatwg.org/#historical-agent-cluster-key-map
    300  nsTHashMap<PrincipalHashKey, bool> mUseOriginAgentCluster;
    301 
    302  nsTHashSet<nsRefPtrHashKey<ContentParent>> mSubscribers;
    303 
    304  // A queue to store postMessage events during page load, the queue will be
    305  // flushed once the page is loaded
    306  RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
    307 
    308  RefPtr<mozilla::ThrottledEventQueue> mTimerEventQueue;
    309  RefPtr<mozilla::ThrottledEventQueue> mWorkerEventQueue;
    310 
    311  // A counter to keep track of the input event suspension level of this BCG
    312  //
    313  // We use BrowsingContextGroup to emulate process isolation in Fission, so
    314  // documents within the same the same BCG will behave like they share
    315  // the same input task queue.
    316  uint32_t mInputEventSuspensionLevel = 0;
    317  // Whether this BCG has increased the suspension level in InputTaskManager
    318  bool mHasIncreasedInputTaskManagerSuspensionLevel = false;
    319 
    320  // This flag keeps track of whether dialogs are
    321  // currently enabled for windows of this group.
    322  // It's OK to have these local to each process only because even if
    323  // frames from two/three different sites (and thus, processes) coordinate a
    324  // dialog abuse attack, they would only the double/triple number of dialogs,
    325  // as it is still limited per-site.
    326  bool mAreDialogsEnabled = true;
    327 
    328  // This counts the number of windows that have been opened in rapid succession
    329  // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
    330  // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
    331  // have elapsed without any other dialogs.
    332  // See comment for mAreDialogsEnabled as to why it's ok to have this local to
    333  // each process.
    334  uint32_t mDialogAbuseCount = 0;
    335 
    336  // This holds the time when the last modal dialog was shown. If more than
    337  // MAX_DIALOG_LIMIT dialogs are shown within the time span defined by
    338  // dom.successive_dialog_time_limit, we show a checkbox or confirmation prompt
    339  // to allow disabling of further dialogs from windows in this BC group.
    340  TimeStamp mLastDialogQuitTime;
    341 };
    342 }  // namespace dom
    343 }  // namespace mozilla
    344 
    345 inline void ImplCycleCollectionUnlink(
    346    mozilla::dom::BrowsingContextGroup::KeepAlivePtr& aField) {
    347  aField = nullptr;
    348 }
    349 
    350 inline void ImplCycleCollectionTraverse(
    351    nsCycleCollectionTraversalCallback& aCallback,
    352    mozilla::dom::BrowsingContextGroup::KeepAlivePtr& aField, const char* aName,
    353    uint32_t aFlags = 0) {
    354  CycleCollectionNoteChild(aCallback, aField.get(), aName, aFlags);
    355 }
    356 
    357 #endif  // !defined(mozilla_dom_BrowsingContextGroup_h)