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)