Bug 1751497 - adjust wpt test-verify and test-coverage tasks to be fission only....
[gecko.git] / docshell / base / BrowsingContext.cpp
blob79f197f0662b780628058fe6954b43fac6055b98
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/. */
7 #include "mozilla/dom/BrowsingContext.h"
9 #include "ipc/IPCMessageUtils.h"
11 #ifdef ACCESSIBILITY
12 # include "mozilla/a11y/DocAccessibleParent.h"
13 # include "mozilla/a11y/Platform.h"
14 # include "mozilla/a11y/RemoteAccessibleBase.h"
15 # include "nsAccessibilityService.h"
16 # if defined(XP_WIN)
17 # include "mozilla/a11y/AccessibleWrap.h"
18 # include "mozilla/a11y/Compatibility.h"
19 # include "mozilla/a11y/nsWinUtils.h"
20 # endif
21 #endif
22 #include "mozilla/AppShutdown.h"
23 #include "mozilla/dom/CanonicalBrowsingContext.h"
24 #include "mozilla/dom/BrowserChild.h"
25 #include "mozilla/dom/BrowserParent.h"
26 #include "mozilla/dom/BrowsingContextGroup.h"
27 #include "mozilla/dom/BrowsingContextBinding.h"
28 #include "mozilla/dom/ContentChild.h"
29 #include "mozilla/dom/ContentParent.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/dom/Element.h"
32 #include "mozilla/dom/HTMLEmbedElement.h"
33 #include "mozilla/dom/HTMLIFrameElement.h"
34 #include "mozilla/dom/Location.h"
35 #include "mozilla/dom/LocationBinding.h"
36 #include "mozilla/dom/MediaDevices.h"
37 #include "mozilla/dom/PopupBlocker.h"
38 #include "mozilla/dom/ScriptSettings.h"
39 #include "mozilla/dom/SessionStorageManager.h"
40 #include "mozilla/dom/SessionStoreDataCollector.h"
41 #include "mozilla/dom/StructuredCloneTags.h"
42 #include "mozilla/dom/UserActivationIPCUtils.h"
43 #include "mozilla/dom/WindowBinding.h"
44 #include "mozilla/dom/WindowGlobalChild.h"
45 #include "mozilla/dom/WindowGlobalParent.h"
46 #include "mozilla/dom/WindowProxyHolder.h"
47 #include "mozilla/dom/SyncedContextInlines.h"
48 #include "mozilla/dom/XULFrameElement.h"
49 #include "mozilla/net/DocumentLoadListener.h"
50 #include "mozilla/net/RequestContextService.h"
51 #include "mozilla/Assertions.h"
52 #include "mozilla/AsyncEventDispatcher.h"
53 #include "mozilla/ClearOnShutdown.h"
54 #include "mozilla/Components.h"
55 #include "mozilla/HashTable.h"
56 #include "mozilla/Logging.h"
57 #include "mozilla/MediaFeatureChange.h"
58 #include "mozilla/ResultExtensions.h"
59 #include "mozilla/Services.h"
60 #include "mozilla/StaticPrefs_fission.h"
61 #include "mozilla/StaticPrefs_media.h"
62 #include "mozilla/StaticPrefs_page_load.h"
63 #include "mozilla/StaticPtr.h"
64 #include "mozilla/URLQueryStringStripper.h"
65 #include "nsIURIFixup.h"
66 #include "nsIXULRuntime.h"
68 #include "nsDocShell.h"
69 #include "nsDocShellLoadState.h"
70 #include "nsFocusManager.h"
71 #include "nsGlobalWindowOuter.h"
72 #include "nsIObserverService.h"
73 #include "nsISHistory.h"
74 #include "nsContentUtils.h"
75 #include "nsQueryObject.h"
76 #include "nsSandboxFlags.h"
77 #include "nsScriptError.h"
78 #include "nsThreadUtils.h"
79 #include "xpcprivate.h"
81 #include "AutoplayPolicy.h"
82 #include "GVAutoplayRequestStatusIPC.h"
84 extern mozilla::LazyLogModule gAutoplayPermissionLog;
85 extern mozilla::LazyLogModule gTimeoutDeferralLog;
87 #define AUTOPLAY_LOG(msg, ...) \
88 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
90 namespace IPC {
91 // Allow serialization and deserialization of OrientationType over IPC
92 template <>
93 struct ParamTraits<mozilla::dom::OrientationType>
94 : public ContiguousEnumSerializer<
95 mozilla::dom::OrientationType,
96 mozilla::dom::OrientationType::Portrait_primary,
97 mozilla::dom::OrientationType::EndGuard_> {};
99 template <>
100 struct ParamTraits<mozilla::dom::DisplayMode>
101 : public ContiguousEnumSerializer<mozilla::dom::DisplayMode,
102 mozilla::dom::DisplayMode::Browser,
103 mozilla::dom::DisplayMode::EndGuard_> {};
105 template <>
106 struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
107 : public ContiguousEnumSerializer<
108 mozilla::dom::PrefersColorSchemeOverride,
109 mozilla::dom::PrefersColorSchemeOverride::None,
110 mozilla::dom::PrefersColorSchemeOverride::EndGuard_> {};
112 template <>
113 struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
114 : public ContiguousEnumSerializer<
115 mozilla::dom::ExplicitActiveStatus,
116 mozilla::dom::ExplicitActiveStatus::None,
117 mozilla::dom::ExplicitActiveStatus::EndGuard_> {};
119 // Allow serialization and deserialization of TouchEventsOverride over IPC
120 template <>
121 struct ParamTraits<mozilla::dom::TouchEventsOverride>
122 : public ContiguousEnumSerializer<
123 mozilla::dom::TouchEventsOverride,
124 mozilla::dom::TouchEventsOverride::Disabled,
125 mozilla::dom::TouchEventsOverride::EndGuard_> {};
126 } // namespace IPC
128 namespace mozilla {
129 namespace dom {
131 // Explicit specialization of the `Transaction` type. Required by the `extern
132 // template class` declaration in the header.
133 template class syncedcontext::Transaction<BrowsingContext>;
135 extern mozilla::LazyLogModule gUserInteractionPRLog;
137 #define USER_ACTIVATION_LOG(msg, ...) \
138 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
140 static LazyLogModule gBrowsingContextLog("BrowsingContext");
141 static LazyLogModule gBrowsingContextSyncLog("BrowsingContextSync");
143 typedef nsTHashMap<nsUint64HashKey, BrowsingContext*> BrowsingContextMap;
145 // All BrowsingContexts indexed by Id
146 static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts;
147 // Top-level Content BrowsingContexts only, indexed by BrowserId instead of Id
148 static StaticAutoPtr<BrowsingContextMap> sCurrentTopByBrowserId;
150 static void UnregisterBrowserId(BrowsingContext* aBrowsingContext) {
151 if (!aBrowsingContext->IsTopContent() || !sCurrentTopByBrowserId) {
152 return;
155 // Avoids an extra lookup
156 auto browserIdEntry =
157 sCurrentTopByBrowserId->Lookup(aBrowsingContext->BrowserId());
158 if (browserIdEntry && browserIdEntry.Data() == aBrowsingContext) {
159 browserIdEntry.Remove();
163 static void Register(BrowsingContext* aBrowsingContext) {
164 sBrowsingContexts->InsertOrUpdate(aBrowsingContext->Id(), aBrowsingContext);
165 if (aBrowsingContext->IsTopContent()) {
166 sCurrentTopByBrowserId->InsertOrUpdate(aBrowsingContext->BrowserId(),
167 aBrowsingContext);
170 aBrowsingContext->Group()->Register(aBrowsingContext);
173 BrowsingContext* BrowsingContext::GetParent() const {
174 return mParentWindow ? mParentWindow->GetBrowsingContext() : nullptr;
177 bool BrowsingContext::IsInSubtreeOf(BrowsingContext* aContext) {
178 BrowsingContext* bc = this;
179 do {
180 if (bc == aContext) {
181 return true;
183 } while ((bc = bc->GetParent()));
184 return false;
187 BrowsingContext* BrowsingContext::Top() {
188 BrowsingContext* bc = this;
189 while (bc->mParentWindow) {
190 bc = bc->GetParent();
192 return bc;
195 const BrowsingContext* BrowsingContext::Top() const {
196 const BrowsingContext* bc = this;
197 while (bc->mParentWindow) {
198 bc = bc->GetParent();
200 return bc;
203 int32_t BrowsingContext::IndexOf(BrowsingContext* aChild) {
204 int32_t index = -1;
205 for (BrowsingContext* child : Children()) {
206 ++index;
207 if (child == aChild) {
208 break;
211 return index;
214 WindowContext* BrowsingContext::GetTopWindowContext() const {
215 if (mParentWindow) {
216 return mParentWindow->TopWindowContext();
218 return mCurrentWindowContext;
221 /* static */
222 void BrowsingContext::Init() {
223 if (!sBrowsingContexts) {
224 sBrowsingContexts = new BrowsingContextMap();
225 sCurrentTopByBrowserId = new BrowsingContextMap();
226 ClearOnShutdown(&sBrowsingContexts);
227 ClearOnShutdown(&sCurrentTopByBrowserId);
231 /* static */
232 LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; }
234 /* static */
235 LogModule* BrowsingContext::GetSyncLog() { return gBrowsingContextSyncLog; }
237 /* static */
238 already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) {
239 return do_AddRef(sBrowsingContexts->Get(aId));
242 /* static */
243 already_AddRefed<BrowsingContext> BrowsingContext::GetCurrentTopByBrowserId(
244 uint64_t aBrowserId) {
245 return do_AddRef(sCurrentTopByBrowserId->Get(aBrowserId));
248 /* static */
249 already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow(
250 WindowProxyHolder& aProxy) {
251 return do_AddRef(aProxy.get());
254 CanonicalBrowsingContext* BrowsingContext::Canonical() {
255 return CanonicalBrowsingContext::Cast(this);
258 bool BrowsingContext::IsOwnedByProcess() const {
259 return mIsInProcess && mDocShell &&
260 !nsDocShell::Cast(mDocShell)->WillChangeProcess();
263 bool BrowsingContext::SameOriginWithTop() {
264 MOZ_ASSERT(IsInProcess());
265 // If the top BrowsingContext is not same-process to us, it is cross-origin
266 if (!Top()->IsInProcess()) {
267 return false;
270 nsIDocShell* docShell = GetDocShell();
271 if (!docShell) {
272 return false;
274 Document* doc = docShell->GetDocument();
275 if (!doc) {
276 return false;
278 nsIPrincipal* principal = doc->NodePrincipal();
280 nsIDocShell* topDocShell = Top()->GetDocShell();
281 if (!topDocShell) {
282 return false;
284 Document* topDoc = topDocShell->GetDocument();
285 if (!topDoc) {
286 return false;
288 nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
290 return principal->Equals(topPrincipal);
293 /* static */
294 already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
295 nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
296 BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType,
297 bool aIsPopupRequested, bool aCreatedDynamically) {
298 if (aParent) {
299 MOZ_DIAGNOSTIC_ASSERT(aParent->GetWindowContext());
300 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->mType == aType);
301 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->GetBrowserId() != 0);
304 MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess());
306 uint64_t id = nsContentUtils::GenerateBrowsingContextId();
308 MOZ_LOG(GetLog(), LogLevel::Debug,
309 ("Creating 0x%08" PRIx64 " in %s", id,
310 XRE_IsParentProcess() ? "Parent" : "Child"));
312 RefPtr<BrowsingContext> parentBC =
313 aParent ? aParent->GetBrowsingContext() : nullptr;
314 RefPtr<WindowContext> parentWC =
315 aParent ? aParent->GetWindowContext() : nullptr;
316 BrowsingContext* inherit = parentBC ? parentBC.get() : aOpener;
318 // Determine which BrowsingContextGroup this context should be created in.
319 RefPtr<BrowsingContextGroup> group = aSpecificGroup;
320 if (aType == Type::Chrome) {
321 MOZ_DIAGNOSTIC_ASSERT(!group);
322 group = BrowsingContextGroup::GetChromeGroup();
323 } else if (!group) {
324 group = BrowsingContextGroup::Select(parentWC, aOpener);
327 // Configure initial values for synced fields.
328 FieldValues fields;
329 fields.mName = aName;
331 if (aOpener) {
332 MOZ_DIAGNOSTIC_ASSERT(!aParent,
333 "new BC with both initial opener and parent");
334 MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == group);
335 MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == aType);
336 fields.mOpenerId = aOpener->Id();
337 fields.mHadOriginalOpener = true;
340 if (aParent) {
341 MOZ_DIAGNOSTIC_ASSERT(parentBC->Group() == group);
342 MOZ_DIAGNOSTIC_ASSERT(parentBC->mType == aType);
343 fields.mEmbedderInnerWindowId = aParent->WindowID();
345 // XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
346 // 1608448) Check if the parent was itself loading already
347 auto readystate = aParent->GetDocument()->GetReadyStateEnum();
348 fields.mAncestorLoading =
349 parentBC->GetAncestorLoading() ||
350 readystate == Document::ReadyState::READYSTATE_LOADING ||
351 readystate == Document::ReadyState::READYSTATE_INTERACTIVE;
354 fields.mBrowserId =
355 parentBC ? parentBC->GetBrowserId() : nsContentUtils::GenerateBrowserId();
357 fields.mOpenerPolicy = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
358 if (aOpener && aOpener->SameOriginWithTop()) {
359 // We inherit the opener policy if there is a creator and if the creator's
360 // origin is same origin with the creator's top-level origin.
361 // If it is cross origin we should not inherit the CrossOriginOpenerPolicy
362 fields.mOpenerPolicy = aOpener->Top()->GetOpenerPolicy();
363 } else if (aOpener) {
364 // They are not same origin
365 auto topPolicy = aOpener->Top()->GetOpenerPolicy();
366 MOZ_RELEASE_ASSERT(topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
367 topPolicy ==
368 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS);
371 fields.mHistoryID = nsID::GenerateUUID();
372 fields.mExplicitActive = [&] {
373 if (parentBC) {
374 // Non-root browsing-contexts inherit their status from its parent.
375 return ExplicitActiveStatus::None;
377 if (aType == Type::Content) {
378 // Content gets managed by the chrome front-end / embedder element and
379 // starts as inactive.
380 return ExplicitActiveStatus::Inactive;
382 // Chrome starts as active.
383 return ExplicitActiveStatus::Active;
384 }();
386 fields.mFullZoom = parentBC ? parentBC->FullZoom() : 1.0f;
387 fields.mTextZoom = parentBC ? parentBC->TextZoom() : 1.0f;
389 bool allowContentRetargeting =
390 inherit ? inherit->GetAllowContentRetargetingOnChildren() : true;
391 fields.mAllowContentRetargeting = allowContentRetargeting;
392 fields.mAllowContentRetargetingOnChildren = allowContentRetargeting;
394 // Assume top allows fullscreen for its children unless otherwise stated.
395 // Subframes start with it false unless otherwise noted in SetEmbedderElement.
396 fields.mFullscreenAllowedByOwner = !aParent;
398 fields.mAllowPlugins = inherit ? inherit->GetAllowPlugins() : true;
400 fields.mDefaultLoadFlags =
401 inherit ? inherit->GetDefaultLoadFlags() : nsIRequest::LOAD_NORMAL;
403 fields.mOrientationLock = mozilla::hal::eScreenOrientation_None;
405 fields.mUseGlobalHistory = inherit ? inherit->GetUseGlobalHistory() : false;
407 fields.mUseErrorPages = true;
409 fields.mTouchEventsOverrideInternal = TouchEventsOverride::None;
411 fields.mAllowJavascript = inherit ? inherit->GetAllowJavascript() : true;
413 fields.mIsPopupRequested = aIsPopupRequested;
415 if (!parentBC) {
416 fields.mShouldDelayMediaFromStart =
417 StaticPrefs::media_block_autoplay_until_in_foreground();
420 RefPtr<BrowsingContext> context;
421 if (XRE_IsParentProcess()) {
422 context = new CanonicalBrowsingContext(parentWC, group, id,
423 /* aOwnerProcessId */ 0,
424 /* aEmbedderProcessId */ 0, aType,
425 std::move(fields));
426 } else {
427 context =
428 new BrowsingContext(parentWC, group, id, aType, std::move(fields));
431 context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent;
432 context->mCreatedDynamically = aCreatedDynamically;
433 if (inherit) {
434 context->mPrivateBrowsingId = inherit->mPrivateBrowsingId;
435 context->mUseRemoteTabs = inherit->mUseRemoteTabs;
436 context->mUseRemoteSubframes = inherit->mUseRemoteSubframes;
437 context->mOriginAttributes = inherit->mOriginAttributes;
440 nsCOMPtr<nsIRequestContextService> rcsvc =
441 net::RequestContextService::GetOrCreate();
442 if (rcsvc) {
443 nsCOMPtr<nsIRequestContext> requestContext;
444 nsresult rv = rcsvc->NewRequestContext(getter_AddRefs(requestContext));
445 if (NS_SUCCEEDED(rv) && requestContext) {
446 context->mRequestContextId = requestContext->GetID();
450 return context.forget();
453 already_AddRefed<BrowsingContext> BrowsingContext::CreateIndependent(
454 Type aType) {
455 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
456 "BCs created in the content process must be related to "
457 "some BrowserChild");
458 RefPtr<BrowsingContext> bc(
459 CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, false));
460 bc->mWindowless = bc->IsContent();
461 bc->mEmbeddedByThisProcess = true;
462 bc->EnsureAttached();
463 return bc.forget();
466 void BrowsingContext::EnsureAttached() {
467 if (!mEverAttached) {
468 Register(this);
470 // Attach the browsing context to the tree.
471 Attach(/* aFromIPC */ false, /* aOriginProcess */ nullptr);
475 /* static */
476 void BrowsingContext::CreateFromIPC(BrowsingContext::IPCInitializer&& aInit,
477 BrowsingContextGroup* aGroup,
478 ContentParent* aOriginProcess) {
479 MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
480 MOZ_DIAGNOSTIC_ASSERT(aGroup);
482 uint64_t originId = 0;
483 if (aOriginProcess) {
484 originId = aOriginProcess->ChildID();
485 aGroup->EnsureHostProcess(aOriginProcess);
488 MOZ_LOG(GetLog(), LogLevel::Debug,
489 ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
490 aInit.mId, originId));
492 RefPtr<WindowContext> parent = aInit.GetParent();
494 RefPtr<BrowsingContext> context;
495 if (XRE_IsParentProcess()) {
496 // If the new BrowsingContext has a parent, it is a sub-frame embedded in
497 // whatever process sent the message. If it doesn't, and is not windowless,
498 // it is a new window or tab, and will be embedded in the parent process.
499 uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0;
500 context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
501 embedderProcessId, Type::Content,
502 std::move(aInit.mFields));
503 } else {
504 context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
505 std::move(aInit.mFields));
508 context->mWindowless = aInit.mWindowless;
509 context->mCreatedDynamically = aInit.mCreatedDynamically;
510 context->mChildOffset = aInit.mChildOffset;
511 if (context->GetHasSessionHistory()) {
512 context->CreateChildSHistory();
513 if (mozilla::SessionHistoryInParent()) {
514 context->GetChildSessionHistory()->SetIndexAndLength(
515 aInit.mSessionHistoryIndex, aInit.mSessionHistoryCount, nsID());
519 // NOTE: Call through the `Set` methods for these values to ensure that any
520 // relevant process-local state is also updated.
521 context->SetOriginAttributes(aInit.mOriginAttributes);
522 context->SetRemoteTabs(aInit.mUseRemoteTabs);
523 context->SetRemoteSubframes(aInit.mUseRemoteSubframes);
524 context->mRequestContextId = aInit.mRequestContextId;
525 // NOTE: Private browsing ID is set by `SetOriginAttributes`.
527 Register(context);
529 context->Attach(/* aFromIPC */ true, aOriginProcess);
532 BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
533 BrowsingContextGroup* aGroup,
534 uint64_t aBrowsingContextId, Type aType,
535 FieldValues&& aInit)
536 : mFields(std::move(aInit)),
537 mType(aType),
538 mBrowsingContextId(aBrowsingContextId),
539 mGroup(aGroup),
540 mParentWindow(aParentWindow),
541 mPrivateBrowsingId(0),
542 mEverAttached(false),
543 mIsInProcess(false),
544 mIsDiscarded(false),
545 mWindowless(false),
546 mDanglingRemoteOuterProxies(false),
547 mEmbeddedByThisProcess(false),
548 mUseRemoteTabs(false),
549 mUseRemoteSubframes(false),
550 mCreatedDynamically(false),
551 mIsInBFCache(false),
552 mCanExecuteScripts(true),
553 mChildOffset(0) {
554 MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup);
555 MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
556 MOZ_RELEASE_ASSERT(mGroup);
559 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
560 // XXX(nika): We should communicate that we are now an active BrowsingContext
561 // process to the parent & do other validation here.
562 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
563 MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
564 mDocShell = aDocShell;
565 mDanglingRemoteOuterProxies = !mIsInProcess;
566 mIsInProcess = true;
567 if (mChildSessionHistory) {
568 mChildSessionHistory->SetIsInProcess(true);
572 // This class implements a callback that will return the remote window proxy for
573 // mBrowsingContext in that compartment, if it has one. It also removes the
574 // proxy from the map, because the object will be transplanted into another kind
575 // of object.
576 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
577 : public js::CompartmentTransplantCallback {
578 public:
579 explicit CompartmentRemoteProxyTransplantCallback(
580 BrowsingContext* aBrowsingContext)
581 : mBrowsingContext(aBrowsingContext) {}
583 virtual JSObject* getObjectToTransplant(
584 JS::Compartment* compartment) override {
585 auto* priv = xpc::CompartmentPrivate::Get(compartment);
586 if (!priv) {
587 return nullptr;
590 auto& map = priv->GetRemoteProxyMap();
591 auto result = map.lookup(mBrowsingContext);
592 if (!result) {
593 return nullptr;
595 JSObject* resultObject = result->value();
596 map.remove(result);
598 return resultObject;
601 private:
602 BrowsingContext* mBrowsingContext;
605 void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies(
606 JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) {
607 if (!mDanglingRemoteOuterProxies) {
608 return;
610 mDanglingRemoteOuterProxies = false;
612 CompartmentRemoteProxyTransplantCallback cb(this);
613 js::RemapRemoteWindowProxies(aCx, &cb, aOuter);
616 bool BrowsingContext::IsActive() const {
617 const BrowsingContext* current = this;
618 do {
619 auto explicit_ = current->GetExplicitActive();
620 if (explicit_ != ExplicitActiveStatus::None) {
621 return explicit_ == ExplicitActiveStatus::Active;
623 if (mParentWindow && !mParentWindow->IsCurrent()) {
624 return false;
626 } while ((current = current->GetParent()));
628 return false;
631 bool BrowsingContext::GetIsActiveBrowserWindow() {
632 if (!XRE_IsParentProcess()) {
633 return Top()->GetIsActiveBrowserWindowInternal();
636 // chrome:// urls loaded in the parent won't receive
637 // their own activation so we defer to the top chrome
638 // Browsing Context when in the parent process.
639 RefPtr<CanonicalBrowsingContext> chromeTop =
640 Canonical()->TopCrossChromeBoundary();
641 return chromeTop->GetIsActiveBrowserWindowInternal();
644 void BrowsingContext::SetIsActiveBrowserWindow(bool aActive) {
645 Unused << SetIsActiveBrowserWindowInternal(aActive);
648 bool BrowsingContext::FullscreenAllowed() const {
649 for (auto* current = this; current; current = current->GetParent()) {
650 if (!current->GetFullscreenAllowedByOwner()) {
651 return false;
654 return true;
657 static bool OwnerAllowsFullscreen(const Element& aEmbedder) {
658 if (aEmbedder.IsXULElement()) {
659 return !aEmbedder.HasAttr(nsGkAtoms::disablefullscreen);
661 if (aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) {
662 // This is controlled by feature policy.
663 return true;
665 if (const auto* embed = HTMLEmbedElement::FromNode(aEmbedder)) {
666 return embed->AllowFullscreen();
668 return false;
671 void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
672 mEmbeddedByThisProcess = true;
674 // Update embedder-element-specific fields in a shared transaction.
675 // Don't do this when clearing our embedder, as we're being destroyed either
676 // way.
677 if (aEmbedder) {
678 Transaction txn;
679 txn.SetEmbedderElementType(Some(aEmbedder->LocalName()));
680 if (nsCOMPtr<nsPIDOMWindowInner> inner =
681 do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
682 txn.SetEmbedderInnerWindowId(inner->WindowID());
684 txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder));
685 if (XRE_IsParentProcess() && IsTopContent()) {
686 nsAutoString messageManagerGroup;
687 if (aEmbedder->IsXULElement()) {
688 aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
689 if (!aEmbedder->AttrValueIs(kNameSpaceID_None,
690 nsGkAtoms::initiallyactive,
691 nsGkAtoms::_false, eIgnoreCase)) {
692 txn.SetExplicitActive(ExplicitActiveStatus::Active);
695 txn.SetMessageManagerGroup(messageManagerGroup);
697 bool useGlobalHistory = !aEmbedder->HasAttr(
698 kNameSpaceID_None, nsGkAtoms::disableglobalhistory);
699 txn.SetUseGlobalHistory(useGlobalHistory);
702 MOZ_ALWAYS_SUCCEEDS(txn.Commit(this));
705 if (XRE_IsParentProcess() && IsTopContent()) {
706 Canonical()->MaybeSetPermanentKey(aEmbedder);
709 mEmbedderElement = aEmbedder;
711 if (mEmbedderElement) {
712 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
713 obs->NotifyWhenScriptSafe(ToSupports(this),
714 "browsing-context-did-set-embedder", nullptr);
719 void BrowsingContext::Embed() {
720 if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
721 frame->BindToBrowsingContext(this);
725 void BrowsingContext::Attach(bool aFromIPC, ContentParent* aOriginProcess) {
726 MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
727 MOZ_DIAGNOSTIC_ASSERT_IF(aFromIPC, aOriginProcess || XRE_IsContentProcess());
728 mEverAttached = true;
730 if (MOZ_LOG_TEST(GetLog(), LogLevel::Debug)) {
731 nsAutoCString suffix;
732 mOriginAttributes.CreateSuffix(suffix);
733 MOZ_LOG(GetLog(), LogLevel::Debug,
734 ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64
735 " (private=%d, remote=%d, fission=%d, oa=%s)",
736 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
737 GetParent() ? GetParent()->Id() : 0, (int)mPrivateBrowsingId,
738 (int)mUseRemoteTabs, (int)mUseRemoteSubframes, suffix.get()));
741 MOZ_DIAGNOSTIC_ASSERT(mGroup);
742 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
744 AssertCoherentLoadContext();
746 // Add ourselves either to our parent or BrowsingContextGroup's child list.
747 if (mParentWindow) {
748 if (!aFromIPC) {
749 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow->IsDiscarded(),
750 "local attach in discarded window");
751 MOZ_DIAGNOSTIC_ASSERT(!GetParent()->IsDiscarded(),
752 "local attach call in discarded bc");
753 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild(),
754 "local attach call with oop parent window");
755 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild()->CanSend(),
756 "local attach call with dead parent window");
758 mChildOffset =
759 mCreatedDynamically ? -1 : mParentWindow->Children().Length();
760 mParentWindow->AppendChildBrowsingContext(this);
761 RecomputeCanExecuteScripts();
762 } else {
763 mGroup->Toplevels().AppendElement(this);
766 if (GetIsPopupSpam()) {
767 PopupBlocker::RegisterOpenPopupSpam();
770 if (IsTop() && GetHasSessionHistory() && !mChildSessionHistory) {
771 CreateChildSHistory();
774 // Why the context is being attached. This will always be "attach" in the
775 // content process, but may be "replace" if it's known the context being
776 // replaced in the parent process.
777 const char16_t* why = u"attach";
779 if (XRE_IsContentProcess() && !aFromIPC) {
780 // Send attach to our parent if we need to.
781 ContentChild::GetSingleton()->SendCreateBrowsingContext(
782 mGroup->Id(), GetIPCInitializer());
783 } else if (XRE_IsParentProcess()) {
784 // If this window was created as a subframe by a content process, it must be
785 // being hosted within the same BrowserParent as its mParentWindow.
786 // Toplevel BrowsingContexts created by content have their BrowserParent
787 // configured during `RecvConstructPopupBrowser`.
788 if (mParentWindow && aOriginProcess) {
789 MOZ_DIAGNOSTIC_ASSERT(
790 mParentWindow->Canonical()->GetContentParent() == aOriginProcess,
791 "Creator process isn't the same as our embedder?");
792 Canonical()->SetCurrentBrowserParent(
793 mParentWindow->Canonical()->GetBrowserParent());
796 mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) {
797 MOZ_DIAGNOSTIC_ASSERT(IsContent(),
798 "chrome BCG cannot be synced to content process");
799 if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID())) {
800 Unused << aParent->SendCreateBrowsingContext(mGroup->Id(),
801 GetIPCInitializer());
805 if (IsTop() && IsContent() && Canonical()->GetWebProgress()) {
806 why = u"replace";
809 // We want to create a BrowsingContextWebProgress for all content
810 // BrowsingContexts.
811 if (IsContent() && !Canonical()->mWebProgress) {
812 Canonical()->mWebProgress = new BrowsingContextWebProgress(Canonical());
816 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
817 obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached",
818 why);
821 if (XRE_IsParentProcess()) {
822 Canonical()->CanonicalAttach();
826 void BrowsingContext::Detach(bool aFromIPC) {
827 MOZ_LOG(GetLog(), LogLevel::Debug,
828 ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
829 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
830 GetParent() ? GetParent()->Id() : 0));
832 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
833 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
835 if (XRE_IsParentProcess()) {
836 Canonical()->AddPendingDiscard();
838 auto callListeners =
839 MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] {
840 for (const auto& listener : listeners) {
841 listener(id);
843 if (XRE_IsParentProcess()) {
844 Canonical()->RemovePendingDiscard();
848 nsCOMPtr<nsIRequestContextService> rcsvc =
849 net::RequestContextService::GetOrCreate();
850 if (rcsvc) {
851 rcsvc->RemoveRequestContext(GetRequestContextId());
854 // This will only ever be null if the cycle-collector has unlinked us. Don't
855 // try to detach ourselves in that case.
856 if (NS_WARN_IF(!mGroup)) {
857 MOZ_ASSERT_UNREACHABLE();
858 return;
861 if (mParentWindow) {
862 mParentWindow->RemoveChildBrowsingContext(this);
863 } else {
864 mGroup->Toplevels().RemoveElement(this);
867 if (XRE_IsParentProcess()) {
868 RefPtr<CanonicalBrowsingContext> self{Canonical()};
869 Group()->EachParent([&](ContentParent* aParent) {
870 // Only the embedder process is allowed to initiate a BrowsingContext
871 // detach, so if we've gotten here, the host process already knows we've
872 // been detached, and there's no need to tell it again.
874 // If the owner process is not the same as the embedder process, its
875 // BrowsingContext will be detached when its nsWebBrowser instance is
876 // destroyed.
877 bool doDiscard = !Canonical()->IsEmbeddedInProcess(aParent->ChildID()) &&
878 !Canonical()->IsOwnedByProcess(aParent->ChildID());
880 // Hold a strong reference to ourself, and keep our BrowsingContextGroup
881 // alive, until the responses comes back to ensure we don't die while
882 // messages relating to this context are in-flight.
884 // When the callback is called, the keepalive on our group will be
885 // destroyed, and the reference to the BrowsingContext will be dropped,
886 // which may cause it to be fully destroyed.
887 mGroup->AddKeepAlive();
888 self->AddPendingDiscard();
889 auto callback = [self](auto) {
890 self->mGroup->RemoveKeepAlive();
891 self->RemovePendingDiscard();
894 aParent->SendDiscardBrowsingContext(this, doDiscard, callback, callback);
896 } else {
897 // Hold a strong reference to ourself until the responses come back to
898 // ensure the BrowsingContext isn't cleaned up before the parent process
899 // acknowledges the discard request.
900 auto callback = [self = RefPtr{this}](auto) {};
901 ContentChild::GetSingleton()->SendDiscardBrowsingContext(
902 this, !aFromIPC, callback, callback);
905 mGroup->Unregister(this);
906 UnregisterBrowserId(this);
907 mIsDiscarded = true;
909 if (XRE_IsParentProcess()) {
910 nsFocusManager* fm = nsFocusManager::GetFocusManager();
911 if (fm) {
912 fm->BrowsingContextDetached(this);
916 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
917 // Why the context is being discarded. This will always be "discard" in the
918 // content process, but may be "replace" if it's known the context being
919 // replaced in the parent process.
920 const char16_t* why = u"discard";
921 if (XRE_IsParentProcess() && IsTop() && !Canonical()->GetWebProgress()) {
922 why = u"replace";
924 obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", why);
927 // NOTE: Doesn't use SetClosed, as it will be set in all processes
928 // automatically by calls to Detach()
929 mFields.SetWithoutSyncing<IDX_Closed>(true);
931 if (GetIsPopupSpam()) {
932 PopupBlocker::UnregisterOpenPopupSpam();
933 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
934 // automatically.
935 mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
938 AssertOriginAttributesMatchPrivateBrowsing();
940 if (XRE_IsParentProcess()) {
941 Canonical()->CanonicalDiscard();
945 void BrowsingContext::AddDiscardListener(
946 std::function<void(uint64_t)>&& aListener) {
947 if (mIsDiscarded) {
948 aListener(Id());
949 return;
951 mDiscardListeners.AppendElement(std::move(aListener));
954 void BrowsingContext::PrepareForProcessChange() {
955 MOZ_LOG(GetLog(), LogLevel::Debug,
956 ("%s: Preparing 0x%08" PRIx64 " for a process change",
957 XRE_IsParentProcess() ? "Parent" : "Child", Id()));
959 MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
960 MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
962 mIsInProcess = false;
963 mUserGestureStart = TimeStamp();
965 // NOTE: For now, clear our nsDocShell reference, as we're primarily in a
966 // different process now. This may need to change in the future with
967 // Cross-Process BFCache.
968 mDocShell = nullptr;
969 if (mChildSessionHistory) {
970 // This can be removed once session history is stored exclusively in the
971 // parent process.
972 mChildSessionHistory->SetIsInProcess(false);
975 if (!mWindowProxy) {
976 return;
979 // We have to go through mWindowProxy rather than calling GetDOMWindow() on
980 // mDocShell because the mDocshell reference gets cleared immediately after
981 // the window is closed.
982 nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy);
983 MOZ_ASSERT(!mWindowProxy);
986 bool BrowsingContext::IsTargetable() const {
987 return !GetClosed() && AncestorsAreCurrent();
990 bool BrowsingContext::HasOpener() const {
991 return sBrowsingContexts->Contains(GetOpenerId());
994 bool BrowsingContext::AncestorsAreCurrent() const {
995 const BrowsingContext* bc = this;
996 while (true) {
997 if (bc->IsDiscarded()) {
998 return false;
1001 if (WindowContext* wc = bc->GetParentWindowContext()) {
1002 if (!wc->IsCurrent() || wc->IsDiscarded()) {
1003 return false;
1006 bc = wc->GetBrowsingContext();
1007 } else {
1008 return true;
1013 bool BrowsingContext::IsInBFCache() const {
1014 if (mozilla::SessionHistoryInParent()) {
1015 return mIsInBFCache;
1017 return mParentWindow &&
1018 mParentWindow->TopWindowContext()->GetWindowStateSaved();
1021 Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
1022 if (WindowContext* current = mCurrentWindowContext) {
1023 return current->Children();
1025 return Span<RefPtr<BrowsingContext>>();
1028 void BrowsingContext::GetChildren(
1029 nsTArray<RefPtr<BrowsingContext>>& aChildren) {
1030 aChildren.AppendElements(Children());
1033 void BrowsingContext::GetWindowContexts(
1034 nsTArray<RefPtr<WindowContext>>& aWindows) {
1035 aWindows.AppendElements(mWindowContexts);
1038 void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
1039 MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
1040 "WindowContext already registered!");
1041 MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
1043 mWindowContexts.AppendElement(aWindow);
1045 // If the newly registered WindowContext is for our current inner window ID,
1046 // re-run the `DidSet` handler to re-establish the relationship.
1047 if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
1048 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1049 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow);
1053 void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
1054 MOZ_ASSERT(mWindowContexts.Contains(aWindow),
1055 "WindowContext not registered!");
1056 mWindowContexts.RemoveElement(aWindow);
1058 // If our currently active window was unregistered, clear our reference to it.
1059 if (aWindow == mCurrentWindowContext) {
1060 // Re-read our `CurrentInnerWindowId` value and use it to set
1061 // `mCurrentWindowContext`. As `aWindow` is now unregistered and discarded,
1062 // we won't find it, and the value will be cleared back to `nullptr`.
1063 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1064 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == nullptr);
1068 void BrowsingContext::PreOrderWalkVoid(
1069 const std::function<void(BrowsingContext*)>& aCallback) {
1070 aCallback(this);
1072 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1073 children.AppendElements(Children());
1075 for (auto& child : children) {
1076 child->PreOrderWalkVoid(aCallback);
1080 BrowsingContext::WalkFlag BrowsingContext::PreOrderWalkFlag(
1081 const std::function<WalkFlag(BrowsingContext*)>& aCallback) {
1082 switch (aCallback(this)) {
1083 case WalkFlag::Skip:
1084 return WalkFlag::Next;
1085 case WalkFlag::Stop:
1086 return WalkFlag::Stop;
1087 case WalkFlag::Next:
1088 default:
1089 break;
1092 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1093 children.AppendElements(Children());
1095 for (auto& child : children) {
1096 switch (child->PreOrderWalkFlag(aCallback)) {
1097 case WalkFlag::Stop:
1098 return WalkFlag::Stop;
1099 default:
1100 break;
1104 return WalkFlag::Next;
1107 void BrowsingContext::PostOrderWalk(
1108 const std::function<void(BrowsingContext*)>& aCallback) {
1109 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1110 children.AppendElements(Children());
1112 for (auto& child : children) {
1113 child->PostOrderWalk(aCallback);
1116 aCallback(this);
1119 void BrowsingContext::GetAllBrowsingContextsInSubtree(
1120 nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) {
1121 PreOrderWalk([&](BrowsingContext* aContext) {
1122 aBrowsingContexts.AppendElement(aContext);
1126 // FindWithName follows the rules for choosing a browsing context,
1127 // with the exception of sandboxing for iframes. The implementation
1128 // for arbitrarily choosing between two browsing contexts with the
1129 // same name is as follows:
1131 // 1) The start browsing context, i.e. 'this'
1132 // 2) Descendants in insertion order
1133 // 3) The parent
1134 // 4) Siblings and their children, both in insertion order
1135 // 5) After this we iteratively follow the parent chain, repeating 3
1136 // and 4 until
1137 // 6) If there is no parent, consider all other top level browsing
1138 // contexts and their children, both in insertion order
1140 // See
1141 // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
1142 BrowsingContext* BrowsingContext::FindWithName(
1143 const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
1144 RefPtr<BrowsingContext> requestingContext = this;
1145 if (aUseEntryGlobalForAccessCheck) {
1146 if (nsCOMPtr<nsIDocShell> caller = do_GetInterface(GetEntryGlobal())) {
1147 if (caller->GetBrowsingContext()) {
1148 requestingContext = caller->GetBrowsingContext();
1153 BrowsingContext* found = nullptr;
1154 if (aName.IsEmpty()) {
1155 // You can't find a browsing context with an empty name.
1156 found = nullptr;
1157 } else if (aName.LowerCaseEqualsLiteral("_blank")) {
1158 // Just return null. Caller must handle creating a new window with
1159 // a blank name.
1160 found = nullptr;
1161 } else if (nsContentUtils::IsSpecialName(aName)) {
1162 found = FindWithSpecialName(aName, *requestingContext);
1163 } else if (BrowsingContext* child =
1164 FindWithNameInSubtree(aName, *requestingContext)) {
1165 found = child;
1166 } else {
1167 BrowsingContext* current = this;
1169 do {
1170 Span<RefPtr<BrowsingContext>> siblings;
1171 BrowsingContext* parent = current->GetParent();
1173 if (!parent) {
1174 // We've reached the root of the tree, consider browsing
1175 // contexts in the same browsing context group.
1176 siblings = mGroup->Toplevels();
1177 } else if (parent->NameEquals(aName) &&
1178 requestingContext->CanAccess(parent) &&
1179 parent->IsTargetable()) {
1180 found = parent;
1181 break;
1182 } else {
1183 siblings = parent->Children();
1186 for (BrowsingContext* sibling : siblings) {
1187 if (sibling == current) {
1188 continue;
1191 if (BrowsingContext* relative =
1192 sibling->FindWithNameInSubtree(aName, *requestingContext)) {
1193 found = relative;
1194 // Breaks the outer loop
1195 parent = nullptr;
1196 break;
1200 current = parent;
1201 } while (current);
1204 // Helpers should perform access control checks, which means that we
1205 // only need to assert that we can access found.
1206 MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanAccess(found));
1208 return found;
1211 BrowsingContext* BrowsingContext::FindChildWithName(
1212 const nsAString& aName, BrowsingContext& aRequestingContext) {
1213 if (aName.IsEmpty()) {
1214 // You can't find a browsing context with the empty name.
1215 return nullptr;
1218 for (BrowsingContext* child : Children()) {
1219 if (child->NameEquals(aName) && aRequestingContext.CanAccess(child) &&
1220 child->IsTargetable()) {
1221 return child;
1225 return nullptr;
1228 BrowsingContext* BrowsingContext::FindWithSpecialName(
1229 const nsAString& aName, BrowsingContext& aRequestingContext) {
1230 // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
1231 // browsing context pointed to by a special name is active. Should
1232 // it be? See Bug 1527913.
1233 if (aName.LowerCaseEqualsLiteral("_self")) {
1234 return this;
1237 if (aName.LowerCaseEqualsLiteral("_parent")) {
1238 if (BrowsingContext* parent = GetParent()) {
1239 return aRequestingContext.CanAccess(parent) ? parent : nullptr;
1241 return this;
1244 if (aName.LowerCaseEqualsLiteral("_top")) {
1245 BrowsingContext* top = Top();
1247 return aRequestingContext.CanAccess(top) ? top : nullptr;
1250 return nullptr;
1253 BrowsingContext* BrowsingContext::FindWithNameInSubtree(
1254 const nsAString& aName, BrowsingContext& aRequestingContext) {
1255 MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
1257 if (NameEquals(aName) && aRequestingContext.CanAccess(this) &&
1258 IsTargetable()) {
1259 return this;
1262 for (BrowsingContext* child : Children()) {
1263 if (BrowsingContext* found =
1264 child->FindWithNameInSubtree(aName, aRequestingContext)) {
1265 return found;
1269 return nullptr;
1272 // For historical context, see:
1274 // Bug 13871: Prevent frameset spoofing
1275 // Bug 103638: Targets with same name in different windows open in wrong
1276 // window with javascript
1277 // Bug 408052: Adopt "ancestor" frame navigation policy
1278 // Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
1279 // origin attribute isolation.
1280 bool BrowsingContext::CanAccess(BrowsingContext* aTarget,
1281 bool aConsiderOpener) {
1282 MOZ_ASSERT(
1283 mDocShell,
1284 "CanAccess() may only be called in the process of the accessing window");
1285 MOZ_ASSERT(aTarget, "Must have a target");
1287 MOZ_DIAGNOSTIC_ASSERT(
1288 Group() == aTarget->Group(),
1289 "A BrowsingContext should never see a context from a different group");
1291 // A frame can navigate itself and its own root.
1292 if (aTarget == this || aTarget == Top()) {
1293 return true;
1296 // A frame can navigate any frame with a same-origin ancestor.
1297 for (BrowsingContext* bc = aTarget; bc; bc = bc->GetParent()) {
1298 if (bc->mDocShell && nsDocShell::ValidateOrigin(this, bc)) {
1299 return true;
1303 // If the target is a top-level document, a frame can navigate it if it can
1304 // navigate its opener.
1305 if (aConsiderOpener && !aTarget->GetParent()) {
1306 if (RefPtr<BrowsingContext> opener = aTarget->GetOpener()) {
1307 return CanAccess(opener, false);
1311 return false;
1314 bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
1315 // If no target then not sandboxed.
1316 if (!aTarget) {
1317 return false;
1320 // We cannot be sandboxed from ourselves.
1321 if (aTarget == this) {
1322 return false;
1325 // Default the sandbox flags to our flags, so that if we can't retrieve the
1326 // active document, we will still enforce our own.
1327 uint32_t sandboxFlags = GetSandboxFlags();
1328 if (mDocShell) {
1329 if (RefPtr<Document> doc = mDocShell->GetExtantDocument()) {
1330 sandboxFlags = doc->GetSandboxFlags();
1334 // If no flags, we are not sandboxed at all.
1335 if (!sandboxFlags) {
1336 return false;
1339 // If aTarget has an ancestor, it is not top level.
1340 if (RefPtr<BrowsingContext> ancestorOfTarget = aTarget->GetParent()) {
1341 do {
1342 // We are not sandboxed if we are an ancestor of target.
1343 if (ancestorOfTarget == this) {
1344 return false;
1346 ancestorOfTarget = ancestorOfTarget->GetParent();
1347 } while (ancestorOfTarget);
1349 // Otherwise, we are sandboxed from aTarget.
1350 return true;
1353 // aTarget is top level, are we the "one permitted sandboxed
1354 // navigator", i.e. did we open aTarget?
1355 if (aTarget->GetOnePermittedSandboxedNavigatorId() == Id()) {
1356 return false;
1359 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
1360 // from our top.
1361 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) && aTarget == Top()) {
1362 return false;
1365 // If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not
1366 // sandboxed from our top if we have user interaction. We assume there is a
1367 // valid transient user gesture interaction if this check happens in the
1368 // target process given that we have checked in the triggering process
1369 // already.
1370 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) &&
1371 mCurrentWindowContext &&
1372 (!mCurrentWindowContext->IsInProcess() ||
1373 mCurrentWindowContext->HasValidTransientUserGestureActivation()) &&
1374 aTarget == Top()) {
1375 return false;
1378 // Otherwise, we are sandboxed from aTarget.
1379 return true;
1382 RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
1383 RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager;
1384 if (!manager) {
1385 manager = new SessionStorageManager(this);
1387 return manager;
1390 bool BrowsingContext::CrossOriginIsolated() {
1391 MOZ_ASSERT(NS_IsMainThread());
1393 return StaticPrefs::
1394 dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup() &&
1395 Top()->GetOpenerPolicy() ==
1396 nsILoadInfo::
1397 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
1398 XRE_IsContentProcess() &&
1399 StringBeginsWith(ContentChild::GetSingleton()->GetRemoteType(),
1400 WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
1403 void BrowsingContext::SetTriggeringAndInheritPrincipals(
1404 nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
1405 uint64_t aLoadIdentifier) {
1406 mTriggeringPrincipal = Some(
1407 PrincipalWithLoadIdentifierTuple(aTriggeringPrincipal, aLoadIdentifier));
1408 if (aPrincipalToInherit) {
1409 mPrincipalToInherit = Some(
1410 PrincipalWithLoadIdentifierTuple(aPrincipalToInherit, aLoadIdentifier));
1414 Tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>>
1415 BrowsingContext::GetTriggeringAndInheritPrincipalsForCurrentLoad() {
1416 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
1417 GetSavedPrincipal(mTriggeringPrincipal);
1418 nsCOMPtr<nsIPrincipal> principalToInherit =
1419 GetSavedPrincipal(mPrincipalToInherit);
1420 return MakeTuple(triggeringPrincipal, principalToInherit);
1423 nsIPrincipal* BrowsingContext::GetSavedPrincipal(
1424 Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple) {
1425 if (aPrincipalTuple) {
1426 nsCOMPtr<nsIPrincipal> principal;
1427 uint64_t loadIdentifier;
1428 Tie(principal, loadIdentifier) = *aPrincipalTuple;
1429 // We want to return a principal only if the load identifier for it
1430 // matches the current one for this BC.
1431 if (auto current = GetCurrentLoadIdentifier();
1432 current && *current == loadIdentifier) {
1433 return principal;
1436 return nullptr;
1439 BrowsingContext::~BrowsingContext() {
1440 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow ||
1441 !mParentWindow->mChildren.Contains(this));
1442 MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
1444 mDeprioritizedLoadRunner.clear();
1446 if (sBrowsingContexts) {
1447 sBrowsingContexts->Remove(Id());
1449 UnregisterBrowserId(this);
1452 /* static */
1453 void BrowsingContext::DiscardFromContentParent(ContentParent* aCP) {
1454 MOZ_ASSERT(XRE_IsParentProcess());
1456 if (sBrowsingContexts) {
1457 AutoTArray<RefPtr<BrowsingContext>, 8> toDiscard;
1458 for (const auto& data : sBrowsingContexts->Values()) {
1459 auto* bc = data->Canonical();
1460 if (!bc->IsDiscarded() && bc->IsEmbeddedInProcess(aCP->ChildID())) {
1461 toDiscard.AppendElement(bc);
1465 for (BrowsingContext* bc : toDiscard) {
1466 bc->Detach(/* aFromIPC */ true);
1471 nsISupports* BrowsingContext::GetParentObject() const {
1472 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1475 JSObject* BrowsingContext::WrapObject(JSContext* aCx,
1476 JS::Handle<JSObject*> aGivenProto) {
1477 return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
1480 bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
1481 JSStructuredCloneWriter* aWriter,
1482 StructuredCloneHolder* aHolder) {
1483 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
1484 return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
1485 JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
1488 /* static */
1489 JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
1490 JSStructuredCloneReader* aReader,
1491 StructuredCloneHolder* aHolder) {
1492 uint32_t idLow = 0;
1493 uint32_t idHigh = 0;
1494 if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
1495 return nullptr;
1497 uint64_t id = uint64_t(idHigh) << 32 | idLow;
1499 // Note: Do this check after reading our ID data. Returning null will abort
1500 // the decode operation anyway, but we should at least be as safe as possible.
1501 if (NS_WARN_IF(!NS_IsMainThread())) {
1502 MOZ_DIAGNOSTIC_ASSERT(false,
1503 "We shouldn't be trying to decode a BrowsingContext "
1504 "on a background thread.");
1505 return nullptr;
1508 JS::RootedValue val(aCx, JS::NullValue());
1509 // We'll get rooting hazard errors from the RefPtr destructor if it isn't
1510 // destroyed before we try to return a raw JSObject*, so create it in its own
1511 // scope.
1512 if (RefPtr<BrowsingContext> context = Get(id)) {
1513 if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
1514 return nullptr;
1517 return val.toObjectOrNull();
1520 bool BrowsingContext::CanSetOriginAttributes() {
1521 // A discarded BrowsingContext has already been destroyed, and cannot modify
1522 // its OriginAttributes.
1523 if (NS_WARN_IF(IsDiscarded())) {
1524 return false;
1527 // Before attaching is the safest time to set OriginAttributes, and the only
1528 // allowed time for content BrowsingContexts.
1529 if (!EverAttached()) {
1530 return true;
1533 // Attached content BrowsingContexts may have been synced to other processes.
1534 if (NS_WARN_IF(IsContent())) {
1535 MOZ_CRASH();
1536 return false;
1538 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
1540 // Cannot set OriginAttributes after we've created our child BrowsingContext.
1541 if (NS_WARN_IF(!Children().IsEmpty())) {
1542 return false;
1545 // Only allow setting OriginAttributes if we have no associated document, or
1546 // the document is still `about:blank`.
1547 // TODO: Bug 1273058 - should have no document when setting origin attributes.
1548 if (WindowGlobalParent* window = Canonical()->GetCurrentWindowGlobal()) {
1549 if (nsIURI* uri = window->GetDocumentURI()) {
1550 MOZ_ASSERT(NS_IsAboutBlank(uri));
1551 return NS_IsAboutBlank(uri);
1554 return true;
1557 Nullable<WindowProxyHolder> BrowsingContext::GetAssociatedWindow() {
1558 // nsILoadContext usually only returns same-process windows,
1559 // so we intentionally return nullptr if this BC is out of
1560 // process.
1561 if (IsInProcess()) {
1562 return WindowProxyHolder(this);
1564 return nullptr;
1567 Nullable<WindowProxyHolder> BrowsingContext::GetTopWindow() {
1568 return Top()->GetAssociatedWindow();
1571 Element* BrowsingContext::GetTopFrameElement() {
1572 return Top()->GetEmbedderElement();
1575 void BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing,
1576 ErrorResult& aError) {
1577 nsresult rv = SetUsePrivateBrowsing(aUsePrivateBrowsing);
1578 if (NS_FAILED(rv)) {
1579 aError.Throw(rv);
1583 void BrowsingContext::SetUseTrackingProtectionWebIDL(
1584 bool aUseTrackingProtection, ErrorResult& aRv) {
1585 SetForceEnableTrackingProtection(aUseTrackingProtection, aRv);
1588 void BrowsingContext::GetOriginAttributes(JSContext* aCx,
1589 JS::MutableHandle<JS::Value> aVal,
1590 ErrorResult& aError) {
1591 AssertOriginAttributesMatchPrivateBrowsing();
1593 if (!ToJSValue(aCx, mOriginAttributes, aVal)) {
1594 aError.NoteJSContextException(aCx);
1598 NS_IMETHODIMP BrowsingContext::GetAssociatedWindow(
1599 mozIDOMWindowProxy** aAssociatedWindow) {
1600 nsCOMPtr<mozIDOMWindowProxy> win = GetDOMWindow();
1601 win.forget(aAssociatedWindow);
1602 return NS_OK;
1605 NS_IMETHODIMP BrowsingContext::GetTopWindow(mozIDOMWindowProxy** aTopWindow) {
1606 return Top()->GetAssociatedWindow(aTopWindow);
1609 NS_IMETHODIMP BrowsingContext::GetTopFrameElement(Element** aTopFrameElement) {
1610 RefPtr<Element> topFrameElement = GetTopFrameElement();
1611 topFrameElement.forget(aTopFrameElement);
1612 return NS_OK;
1615 NS_IMETHODIMP BrowsingContext::GetIsContent(bool* aIsContent) {
1616 *aIsContent = IsContent();
1617 return NS_OK;
1620 NS_IMETHODIMP BrowsingContext::GetUsePrivateBrowsing(
1621 bool* aUsePrivateBrowsing) {
1622 *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
1623 return NS_OK;
1626 NS_IMETHODIMP BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1627 if (!CanSetOriginAttributes()) {
1628 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1629 if (changed) {
1630 NS_WARNING("SetUsePrivateBrowsing when !CanSetOriginAttributes()");
1632 return changed ? NS_ERROR_FAILURE : NS_OK;
1635 return SetPrivateBrowsing(aUsePrivateBrowsing);
1638 NS_IMETHODIMP BrowsingContext::SetPrivateBrowsing(bool aPrivateBrowsing) {
1639 if (!CanSetOriginAttributes()) {
1640 NS_WARNING("Attempt to set PrivateBrowsing when !CanSetOriginAttributes");
1641 return NS_ERROR_FAILURE;
1644 bool changed = aPrivateBrowsing != (mPrivateBrowsingId > 0);
1645 if (changed) {
1646 mPrivateBrowsingId = aPrivateBrowsing ? 1 : 0;
1647 if (IsContent()) {
1648 mOriginAttributes.SyncAttributesWithPrivateBrowsing(aPrivateBrowsing);
1651 if (XRE_IsParentProcess()) {
1652 Canonical()->AdjustPrivateBrowsingCount(aPrivateBrowsing);
1655 AssertOriginAttributesMatchPrivateBrowsing();
1657 if (changed && mDocShell) {
1658 nsDocShell::Cast(mDocShell)->NotifyPrivateBrowsingChanged();
1660 return NS_OK;
1663 NS_IMETHODIMP BrowsingContext::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1664 *aUseRemoteTabs = mUseRemoteTabs;
1665 return NS_OK;
1668 NS_IMETHODIMP BrowsingContext::SetRemoteTabs(bool aUseRemoteTabs) {
1669 if (!CanSetOriginAttributes()) {
1670 NS_WARNING("Attempt to set RemoteTabs when !CanSetOriginAttributes");
1671 return NS_ERROR_FAILURE;
1674 static bool annotated = false;
1675 if (aUseRemoteTabs && !annotated) {
1676 annotated = true;
1677 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled,
1678 true);
1681 // Don't allow non-remote tabs with remote subframes.
1682 if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
1683 return NS_ERROR_UNEXPECTED;
1686 mUseRemoteTabs = aUseRemoteTabs;
1687 return NS_OK;
1690 NS_IMETHODIMP BrowsingContext::GetUseRemoteSubframes(
1691 bool* aUseRemoteSubframes) {
1692 *aUseRemoteSubframes = mUseRemoteSubframes;
1693 return NS_OK;
1696 NS_IMETHODIMP BrowsingContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
1697 if (!CanSetOriginAttributes()) {
1698 NS_WARNING("Attempt to set RemoteSubframes when !CanSetOriginAttributes");
1699 return NS_ERROR_FAILURE;
1702 static bool annotated = false;
1703 if (aUseRemoteSubframes && !annotated) {
1704 annotated = true;
1705 CrashReporter::AnnotateCrashReport(
1706 CrashReporter::Annotation::DOMFissionEnabled, true);
1709 // Don't allow non-remote tabs with remote subframes.
1710 if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
1711 return NS_ERROR_UNEXPECTED;
1714 mUseRemoteSubframes = aUseRemoteSubframes;
1715 return NS_OK;
1718 NS_IMETHODIMP BrowsingContext::GetUseTrackingProtection(
1719 bool* aUseTrackingProtection) {
1720 *aUseTrackingProtection = false;
1722 if (GetForceEnableTrackingProtection() ||
1723 StaticPrefs::privacy_trackingprotection_enabled() ||
1724 (UsePrivateBrowsing() &&
1725 StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
1726 *aUseTrackingProtection = true;
1727 return NS_OK;
1730 if (GetParent()) {
1731 return GetParent()->GetUseTrackingProtection(aUseTrackingProtection);
1734 return NS_OK;
1737 NS_IMETHODIMP BrowsingContext::SetUseTrackingProtection(
1738 bool aUseTrackingProtection) {
1739 return SetForceEnableTrackingProtection(aUseTrackingProtection);
1742 NS_IMETHODIMP BrowsingContext::GetScriptableOriginAttributes(
1743 JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
1744 AssertOriginAttributesMatchPrivateBrowsing();
1746 bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
1747 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1748 return NS_OK;
1751 NS_IMETHODIMP_(void)
1752 BrowsingContext::GetOriginAttributes(OriginAttributes& aAttrs) {
1753 aAttrs = mOriginAttributes;
1754 AssertOriginAttributesMatchPrivateBrowsing();
1757 nsresult BrowsingContext::SetOriginAttributes(const OriginAttributes& aAttrs) {
1758 if (!CanSetOriginAttributes()) {
1759 NS_WARNING("Attempt to set OriginAttributes when !CanSetOriginAttributes");
1760 return NS_ERROR_FAILURE;
1763 AssertOriginAttributesMatchPrivateBrowsing();
1764 mOriginAttributes = aAttrs;
1766 bool isPrivate = mOriginAttributes.mPrivateBrowsingId !=
1767 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1768 // Chrome Browsing Context can not contain OriginAttributes.mPrivateBrowsingId
1769 if (IsChrome() && isPrivate) {
1770 mOriginAttributes.mPrivateBrowsingId =
1771 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1773 SetPrivateBrowsing(isPrivate);
1774 AssertOriginAttributesMatchPrivateBrowsing();
1776 return NS_OK;
1779 void BrowsingContext::AssertCoherentLoadContext() {
1780 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1781 // LoadContext should generally match our opener or parent.
1782 if (IsContent()) {
1783 if (RefPtr<BrowsingContext> opener = GetOpener()) {
1784 MOZ_DIAGNOSTIC_ASSERT(opener->mType == mType);
1785 MOZ_DIAGNOSTIC_ASSERT(opener->mGroup == mGroup);
1786 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteTabs == mUseRemoteTabs);
1787 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteSubframes == mUseRemoteSubframes);
1788 MOZ_DIAGNOSTIC_ASSERT(opener->mPrivateBrowsingId == mPrivateBrowsingId);
1789 MOZ_DIAGNOSTIC_ASSERT(
1790 opener->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1793 if (RefPtr<BrowsingContext> parent = GetParent()) {
1794 MOZ_DIAGNOSTIC_ASSERT(parent->mType == mType);
1795 MOZ_DIAGNOSTIC_ASSERT(parent->mGroup == mGroup);
1796 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteTabs == mUseRemoteTabs);
1797 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteSubframes == mUseRemoteSubframes);
1798 MOZ_DIAGNOSTIC_ASSERT(parent->mPrivateBrowsingId == mPrivateBrowsingId);
1799 MOZ_DIAGNOSTIC_ASSERT(
1800 parent->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1803 // UseRemoteSubframes and UseRemoteTabs must match.
1804 MOZ_DIAGNOSTIC_ASSERT(
1805 !mUseRemoteSubframes || mUseRemoteTabs,
1806 "Cannot set useRemoteSubframes without also setting useRemoteTabs");
1808 // Double-check OriginAttributes/Private Browsing
1809 AssertOriginAttributesMatchPrivateBrowsing();
1810 #endif
1813 void BrowsingContext::AssertOriginAttributesMatchPrivateBrowsing() {
1814 // Chrome browsing contexts must not have a private browsing OriginAttribute
1815 // Content browsing contexts must maintain the equality:
1816 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
1817 if (IsChrome()) {
1818 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
1819 } else {
1820 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
1821 mPrivateBrowsingId);
1825 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext)
1826 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1827 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
1828 NS_INTERFACE_MAP_ENTRY(nsISupports)
1829 NS_INTERFACE_MAP_END
1831 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
1833 NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext)
1834 NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext)
1836 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext)
1837 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1838 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1840 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
1841 if (sBrowsingContexts) {
1842 sBrowsingContexts->Remove(tmp->Id());
1844 UnregisterBrowserId(tmp);
1846 if (tmp->GetIsPopupSpam()) {
1847 PopupBlocker::UnregisterOpenPopupSpam();
1848 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1849 // automatically.
1850 tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1853 NS_IMPL_CYCLE_COLLECTION_UNLINK(
1854 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1855 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1856 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1857 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1859 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
1860 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
1861 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1862 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1863 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1865 static bool IsCertainlyAliveForCC(BrowsingContext* aContext) {
1866 return aContext->HasKnownLiveWrapper() ||
1867 (AppShutdown::GetCurrentShutdownPhase() ==
1868 ShutdownPhase::NotInShutdown &&
1869 aContext->EverAttached() && !aContext->IsDiscarded());
1872 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(BrowsingContext)
1873 if (IsCertainlyAliveForCC(tmp)) {
1874 if (tmp->PreservingWrapper()) {
1875 tmp->MarkWrapperLive();
1877 return true;
1879 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1881 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(BrowsingContext)
1882 return IsCertainlyAliveForCC(tmp) && tmp->HasNothingToTrace(tmp);
1883 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1885 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
1886 return IsCertainlyAliveForCC(tmp);
1887 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1889 class RemoteLocationProxy
1890 : public RemoteObjectProxy<BrowsingContext::LocationProxy,
1891 Location_Binding::sCrossOriginProperties> {
1892 public:
1893 typedef RemoteObjectProxy Base;
1895 constexpr RemoteLocationProxy()
1896 : RemoteObjectProxy(prototypes::id::Location) {}
1898 void NoteChildren(JSObject* aProxy,
1899 nsCycleCollectionTraversalCallback& aCb) const override {
1900 auto location =
1901 static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
1902 CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
1903 "JS::GetPrivate(obj)->GetBrowsingContext()");
1907 static const RemoteLocationProxy sSingleton;
1909 // Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
1910 // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
1911 // malloc.
1912 template <>
1913 const JSClass RemoteLocationProxy::Base::sClass =
1914 PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
1916 void BrowsingContext::Location(JSContext* aCx,
1917 JS::MutableHandle<JSObject*> aLocation,
1918 ErrorResult& aError) {
1919 aError.MightThrowJSException();
1920 sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
1921 aLocation);
1922 if (!aLocation) {
1923 aError.StealExceptionFromJSContext(aCx);
1927 bool BrowsingContext::RemoveRootFromBFCacheSync() {
1928 if (WindowContext* wc = GetParentWindowContext()) {
1929 if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) {
1930 return doc->RemoveFromBFCacheSync();
1933 return false;
1936 nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) {
1937 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1938 if (sourceBC.IsNull()) {
1939 return NS_OK;
1942 // We might be called after the source BC has been discarded, but before we've
1943 // destroyed our in-process instance of the BrowsingContext object in some
1944 // situations (e.g. after creating a new pop-up with window.open while the
1945 // window is being closed). In these situations we want to still perform the
1946 // sandboxing check against our in-process copy. If we've forgotten about the
1947 // context already, assume it is sanboxed. (bug 1643450)
1948 BrowsingContext* bc = sourceBC.GetMaybeDiscarded();
1949 if (!bc || bc->IsSandboxedFrom(this)) {
1950 return NS_ERROR_DOM_SECURITY_ERR;
1952 return NS_OK;
1955 nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
1956 bool aSetNavigating) {
1957 // Per spec, most load attempts are silently ignored when a BrowsingContext is
1958 // null (which in our code corresponds to discarded), so we simply fail
1959 // silently in those cases. Regardless, we cannot trigger loads in/from
1960 // discarded BrowsingContexts via IPC, so we need to abort in any case.
1961 if (IsDiscarded()) {
1962 return NS_OK;
1965 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
1966 "Targeting occurs in InternalLoad");
1968 if (mDocShell) {
1969 return mDocShell->LoadURI(aLoadState, aSetNavigating);
1972 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
1973 // document-specific sandbox flags are only available in the process
1974 // triggering the load, and we don't want the target process to have to trust
1975 // the triggering process to do the appropriate checks for the
1976 // BrowsingContext's sandbox flags.
1977 MOZ_TRY(CheckSandboxFlags(aLoadState));
1978 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
1979 aLoadState->PrincipalToInherit(),
1980 aLoadState->GetLoadIdentifier());
1982 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1984 if (net::SchemeIsJavascript(aLoadState->URI())) {
1985 if (!XRE_IsParentProcess()) {
1986 // Web content should only be able to load javascript: URIs into documents
1987 // whose principals the caller principal subsumes, which by definition
1988 // excludes any document in a cross-process BrowsingContext.
1989 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
1991 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
1992 "Should never see a cross-process javascript: load "
1993 "triggered from content");
1996 MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
1997 if (sourceBC && sourceBC->IsInProcess()) {
1998 if (!sourceBC->CanAccess(this)) {
1999 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2002 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
2003 if (WindowGlobalChild* wgc =
2004 win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
2005 wgc->SendLoadURI(this, aLoadState, aSetNavigating);
2007 } else if (XRE_IsParentProcess()) {
2008 // Strip the target query parameters before loading the URI in the parent.
2009 // The loading in content will be handled in nsDocShell.
2010 aLoadState->MaybeStripTrackerQueryStrings(this);
2012 if (Canonical()->LoadInParent(aLoadState, aSetNavigating)) {
2013 return NS_OK;
2016 if (ContentParent* cp = Canonical()->GetContentParent()) {
2017 // Attempt to initiate this load immediately in the parent, if it succeeds
2018 // it'll return a unique identifier so that we can find it later.
2019 uint64_t loadIdentifier = 0;
2020 if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) {
2021 MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome());
2022 loadIdentifier = GetCurrentLoadIdentifier().value();
2023 aLoadState->SetChannelInitialized(true);
2026 cp->TransmitBlobDataIfBlobURL(aLoadState->URI());
2028 // Setup a confirmation callback once the content process receives this
2029 // load. Normally we'd expect a PDocumentChannel actor to have been
2030 // created to claim the load identifier by that time. If not, then it
2031 // won't be coming, so make sure we clean up and deregister.
2032 cp->SendLoadURI(this, aLoadState, aSetNavigating)
2033 ->Then(GetMainThreadSerialEventTarget(), __func__,
2034 [loadIdentifier](
2035 const PContentParent::LoadURIPromise::ResolveOrRejectValue&
2036 aValue) {
2037 if (loadIdentifier) {
2038 net::DocumentLoadListener::CleanupParentLoadAttempt(
2039 loadIdentifier);
2043 } else {
2044 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2045 if (!sourceBC) {
2046 return NS_ERROR_UNEXPECTED;
2048 // If we're in a content process and the source BC is no longer in-process,
2049 // just fail silently.
2051 return NS_OK;
2054 nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
2055 if (IsDiscarded()) {
2056 return NS_OK;
2058 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
2059 aLoadState->PrincipalToInherit(),
2060 aLoadState->GetLoadIdentifier());
2062 MOZ_DIAGNOSTIC_ASSERT(aLoadState->Target().IsEmpty(),
2063 "should already have retargeted");
2064 MOZ_DIAGNOSTIC_ASSERT(!aLoadState->TargetBrowsingContext().IsNull(),
2065 "should have target bc set");
2066 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext() == this,
2067 "must be targeting this BrowsingContext");
2069 if (mDocShell) {
2070 return nsDocShell::Cast(mDocShell)->InternalLoad(aLoadState);
2073 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
2074 // document-specific sandbox flags are only available in the process
2075 // triggering the load, and we don't want the target process to have to trust
2076 // the triggering process to do the appropriate checks for the
2077 // BrowsingContext's sandbox flags.
2078 MOZ_TRY(CheckSandboxFlags(aLoadState));
2080 const auto& sourceBC = aLoadState->SourceBrowsingContext();
2082 if (net::SchemeIsJavascript(aLoadState->URI())) {
2083 if (!XRE_IsParentProcess()) {
2084 // Web content should only be able to load javascript: URIs into documents
2085 // whose principals the caller principal subsumes, which by definition
2086 // excludes any document in a cross-process BrowsingContext.
2087 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
2089 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
2090 "Should never see a cross-process javascript: load "
2091 "triggered from content");
2094 if (XRE_IsParentProcess()) {
2095 ContentParent* cp = Canonical()->GetContentParent();
2096 if (!cp || !cp->CanSend()) {
2097 return NS_ERROR_FAILURE;
2100 MOZ_ALWAYS_SUCCEEDS(
2101 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2102 Unused << cp->SendInternalLoad(aLoadState);
2103 } else {
2104 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2105 MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
2107 if (!sourceBC->CanAccess(this)) {
2108 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2111 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
2112 WindowGlobalChild* wgc =
2113 win->GetCurrentInnerWindow()->GetWindowGlobalChild();
2114 if (!wgc || !wgc->CanSend()) {
2115 return NS_ERROR_FAILURE;
2118 MOZ_ALWAYS_SUCCEEDS(
2119 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2120 wgc->SendInternalLoad(aLoadState);
2123 return NS_OK;
2126 void BrowsingContext::DisplayLoadError(const nsAString& aURI) {
2127 MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError"));
2128 MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
2129 MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess());
2131 if (mDocShell) {
2132 bool didDisplayLoadError = false;
2133 mDocShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr,
2134 PromiseFlatString(aURI).get(), nullptr,
2135 &didDisplayLoadError);
2136 } else {
2137 if (ContentParent* cp = Canonical()->GetContentParent()) {
2138 Unused << cp->SendDisplayLoadError(this, PromiseFlatString(aURI));
2143 WindowProxyHolder BrowsingContext::Window() {
2144 return WindowProxyHolder(Self());
2147 WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) {
2148 return Window();
2151 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
2152 if (mIsDiscarded) {
2153 return;
2156 if (IsSubframe()) {
2157 // .close() on frames is a no-op.
2158 return;
2161 if (GetDOMWindow()) {
2162 nsGlobalWindowOuter::Cast(GetDOMWindow())
2163 ->CloseOuter(aCallerType == CallerType::System);
2164 return;
2167 // This is a bit of a hack for webcompat. Content needs to see an updated
2168 // |window.closed| value as early as possible, so we set this before we
2169 // actually send the DOMWindowClose event, which happens in the process where
2170 // the document for this browsing context is loaded.
2171 MOZ_ALWAYS_SUCCEEDS(SetClosed(true));
2173 if (ContentChild* cc = ContentChild::GetSingleton()) {
2174 cc->SendWindowClose(this, aCallerType == CallerType::System);
2175 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2176 Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
2181 * Examine the current document state to see if we're in a way that is
2182 * typically abused by web designers. The window.open code uses this
2183 * routine to determine whether to allow the new window.
2184 * Returns a value from the PopupControlState enum.
2186 PopupBlocker::PopupControlState BrowsingContext::RevisePopupAbuseLevel(
2187 PopupBlocker::PopupControlState aControl) {
2188 if (!IsContent()) {
2189 return PopupBlocker::openAllowed;
2192 RefPtr<Document> doc = GetExtantDocument();
2193 PopupBlocker::PopupControlState abuse = aControl;
2194 switch (abuse) {
2195 case PopupBlocker::openControlled:
2196 case PopupBlocker::openBlocked:
2197 case PopupBlocker::openOverridden:
2198 if (IsPopupAllowed()) {
2199 abuse = PopupBlocker::PopupControlState(abuse - 1);
2201 break;
2202 case PopupBlocker::openAbused:
2203 if (IsPopupAllowed() ||
2204 (doc && doc->HasValidTransientUserGestureActivation())) {
2205 // Skip PopupBlocker::openBlocked
2206 abuse = PopupBlocker::openControlled;
2208 break;
2209 case PopupBlocker::openAllowed:
2210 break;
2211 default:
2212 NS_WARNING("Strange PopupControlState!");
2215 // limit the number of simultaneously open popups
2216 if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked ||
2217 abuse == PopupBlocker::openControlled) {
2218 int32_t popupMax = StaticPrefs::dom_popup_maximum();
2219 if (popupMax >= 0 &&
2220 PopupBlocker::GetOpenPopupSpamCount() >= (uint32_t)popupMax) {
2221 abuse = PopupBlocker::openOverridden;
2225 // If we're currently in-process, attempt to consume transient user gesture
2226 // activations.
2227 if (doc) {
2228 // HACK: Some pages using bogus library + UA sniffing call window.open()
2229 // from a blank iframe, only on Firefox, see bug 1685056.
2231 // This is a hack-around to preserve behavior in that particular and
2232 // specific case, by consuming activation on the parent document, so we
2233 // don't care about the InProcessParent bits not being fission-safe or what
2234 // not.
2235 auto ConsumeTransientUserActivationForMultiplePopupBlocking =
2236 [&]() -> bool {
2237 if (doc->ConsumeTransientUserGestureActivation()) {
2238 return true;
2240 if (!doc->IsInitialDocument()) {
2241 return false;
2243 Document* parentDoc = doc->GetInProcessParentDocument();
2244 if (!parentDoc ||
2245 !parentDoc->NodePrincipal()->Equals(doc->NodePrincipal())) {
2246 return false;
2248 return parentDoc->ConsumeTransientUserGestureActivation();
2251 // If this popup is allowed, let's block any other for this event, forcing
2252 // PopupBlocker::openBlocked state.
2253 if ((abuse == PopupBlocker::openAllowed ||
2254 abuse == PopupBlocker::openControlled) &&
2255 StaticPrefs::dom_block_multiple_popups() && !IsPopupAllowed() &&
2256 !ConsumeTransientUserActivationForMultiplePopupBlocking()) {
2257 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
2258 doc, nsContentUtils::eDOM_PROPERTIES,
2259 "MultiplePopupsBlockedNoUserActivation");
2260 abuse = PopupBlocker::openBlocked;
2264 return abuse;
2267 void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
2268 Unused << SetHistoryEntryCount(GetHistoryEntryCount() + 1);
2271 void BrowsingContext::FlushSessionStore() {
2272 nsTArray<RefPtr<BrowserChild>> nestedBrowserChilds;
2274 PreOrderWalk([&](BrowsingContext* aContext) {
2275 BrowserChild* browserChild = BrowserChild::GetFrom(aContext->GetDocShell());
2276 if (browserChild && browserChild->GetBrowsingContext() == aContext) {
2277 nestedBrowserChilds.AppendElement(browserChild);
2280 if (aContext->CreatedDynamically()) {
2281 return WalkFlag::Skip;
2284 WindowContext* windowContext = aContext->GetCurrentWindowContext();
2285 if (!windowContext) {
2286 return WalkFlag::Skip;
2289 WindowGlobalChild* windowChild = windowContext->GetWindowGlobalChild();
2290 if (!windowChild) {
2291 return WalkFlag::Next;
2294 RefPtr<SessionStoreDataCollector> collector =
2295 windowChild->GetSessionStoreDataCollector();
2296 if (!collector) {
2297 return WalkFlag::Next;
2300 collector->Flush();
2301 return WalkFlag::Next;
2304 for (auto& child : nestedBrowserChilds) {
2305 child->UpdateSessionStore();
2309 std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) {
2310 nsFocusManager* fm = nsFocusManager::GetFocusManager();
2311 if (!fm) {
2312 return {false, false};
2315 nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
2316 BrowsingContext* callerBC = caller ? caller->GetBrowsingContext() : nullptr;
2317 RefPtr<BrowsingContext> openerBC = GetOpener();
2318 MOZ_DIAGNOSTIC_ASSERT(!openerBC || openerBC->Group() == Group());
2320 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
2321 // window which opened us to raise us at times when popups are allowed
2322 // (bugs 355482 and 369306).
2323 bool canFocus = aCallerType == CallerType::System ||
2324 !Preferences::GetBool("dom.disable_window_flip", true);
2325 if (!canFocus && openerBC == callerBC) {
2326 canFocus =
2327 (callerBC ? callerBC : this)
2328 ->RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
2329 PopupBlocker::openBlocked;
2332 bool isActive = false;
2333 if (XRE_IsParentProcess()) {
2334 RefPtr<CanonicalBrowsingContext> chromeTop =
2335 Canonical()->TopCrossChromeBoundary();
2336 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = fm->GetActiveWindow();
2337 isActive = (activeWindow == chromeTop->GetDOMWindow());
2338 } else {
2339 isActive = (fm->GetActiveBrowsingContext() == Top());
2342 return {canFocus, isActive};
2345 void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
2346 // These checks need to happen before the RequestFrameFocus call, which
2347 // is why they are done in an untrusted process. If we wanted to enforce
2348 // these in the parent, we'd need to do the checks there _also_.
2349 // These should be kept in sync with nsGlobalWindowOuter::FocusOuter.
2351 auto [canFocus, isActive] = CanFocusCheck(aCallerType);
2353 if (!(canFocus || isActive)) {
2354 return;
2357 // Permission check passed
2359 if (mEmbedderElement) {
2360 // Make the activeElement in this process update synchronously.
2361 nsContentUtils::RequestFrameFocus(*mEmbedderElement, true, aCallerType);
2363 uint64_t actionId = nsFocusManager::GenerateFocusActionId();
2364 if (ContentChild* cc = ContentChild::GetSingleton()) {
2365 cc->SendWindowFocus(this, aCallerType, actionId);
2366 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2367 Unused << cp->SendWindowFocus(this, aCallerType, actionId);
2371 bool BrowsingContext::CanBlurCheck(CallerType aCallerType) {
2372 // If dom.disable_window_flip == true, then content should not be allowed
2373 // to do blur (this would allow popunders, bug 369306)
2374 return aCallerType == CallerType::System ||
2375 !Preferences::GetBool("dom.disable_window_flip", true);
2378 void BrowsingContext::Blur(CallerType aCallerType, ErrorResult& aError) {
2379 if (!CanBlurCheck(aCallerType)) {
2380 return;
2383 if (ContentChild* cc = ContentChild::GetSingleton()) {
2384 cc->SendWindowBlur(this, aCallerType);
2385 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2386 Unused << cp->SendWindowBlur(this, aCallerType);
2390 Nullable<WindowProxyHolder> BrowsingContext::GetWindow() {
2391 if (XRE_IsParentProcess() && !IsInProcess()) {
2392 return nullptr;
2394 return WindowProxyHolder(this);
2397 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
2398 if (mIsDiscarded) {
2399 return nullptr;
2402 // We never return null or throw an error, but the implementation in
2403 // nsGlobalWindow does and we need to use the same signature.
2404 return WindowProxyHolder(Top());
2407 void BrowsingContext::GetOpener(JSContext* aCx,
2408 JS::MutableHandle<JS::Value> aOpener,
2409 ErrorResult& aError) const {
2410 RefPtr<BrowsingContext> opener = GetOpener();
2411 if (!opener) {
2412 aOpener.setNull();
2413 return;
2416 if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
2417 aError.NoteJSContextException(aCx);
2421 // We never throw an error, but the implementation in nsGlobalWindow does and
2422 // we need to use the same signature.
2423 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
2424 if (mIsDiscarded) {
2425 return nullptr;
2428 if (GetParent()) {
2429 return WindowProxyHolder(GetParent());
2431 return WindowProxyHolder(this);
2434 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2435 JS::Handle<JS::Value> aMessage,
2436 const nsAString& aTargetOrigin,
2437 const Sequence<JSObject*>& aTransfer,
2438 nsIPrincipal& aSubjectPrincipal,
2439 ErrorResult& aError) {
2440 if (mIsDiscarded) {
2441 return;
2444 RefPtr<BrowsingContext> sourceBc;
2445 PostMessageData data;
2446 data.targetOrigin() = aTargetOrigin;
2447 data.subjectPrincipal() = &aSubjectPrincipal;
2448 RefPtr<nsGlobalWindowInner> callerInnerWindow;
2449 nsAutoCString scriptLocation;
2450 // We don't need to get the caller's agentClusterId since that is used for
2451 // checking whether it's okay to sharing memory (and it's not allowed to share
2452 // memory cross processes)
2453 if (!nsGlobalWindowOuter::GatherPostMessageData(
2454 aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
2455 getter_AddRefs(data.targetOriginURI()),
2456 getter_AddRefs(data.callerPrincipal()),
2457 getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()),
2458 /* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) {
2459 return;
2461 if (sourceBc && sourceBc->IsDiscarded()) {
2462 return;
2464 data.source() = sourceBc;
2465 data.isFromPrivateWindow() =
2466 callerInnerWindow &&
2467 nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
2468 data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0;
2469 data.scriptLocation() = scriptLocation;
2470 JS::Rooted<JS::Value> transferArray(aCx);
2471 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
2472 &transferArray);
2473 if (NS_WARN_IF(aError.Failed())) {
2474 return;
2477 JS::CloneDataPolicy clonePolicy;
2478 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
2479 clonePolicy.allowSharedMemoryObjects();
2482 // We will see if the message is required to be in the same process or it can
2483 // be in the different process after Write().
2484 ipc::StructuredCloneData message = ipc::StructuredCloneData(
2485 StructuredCloneHolder::StructuredCloneScope::UnknownDestination,
2486 StructuredCloneHolder::TransferringSupported);
2487 message.Write(aCx, aMessage, transferArray, clonePolicy, aError);
2488 if (NS_WARN_IF(aError.Failed())) {
2489 return;
2492 ClonedOrErrorMessageData messageData;
2493 if (ContentChild* cc = ContentChild::GetSingleton()) {
2494 // The clone scope gets set when we write the message data based on the
2495 // requirements of that data that we're writing.
2496 // If the message data contins a shared memory object, then CloneScope would
2497 // return SameProcess. Otherwise, it returns DifferentProcess.
2498 if (message.CloneScope() ==
2499 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2500 ClonedMessageData clonedMessageData;
2501 if (!message.BuildClonedMessageDataForChild(cc, clonedMessageData)) {
2502 aError.Throw(NS_ERROR_FAILURE);
2503 return;
2506 messageData = std::move(clonedMessageData);
2507 } else {
2508 MOZ_ASSERT(message.CloneScope() ==
2509 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2511 messageData = ErrorMessageData();
2513 nsContentUtils::ReportToConsole(
2514 nsIScriptError::warningFlag, "DOM Window"_ns,
2515 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2516 nsContentUtils::eDOM_PROPERTIES,
2517 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2520 cc->SendWindowPostMessage(this, messageData, data);
2521 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2522 if (message.CloneScope() ==
2523 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2524 ClonedMessageData clonedMessageData;
2525 if (!message.BuildClonedMessageDataForParent(cp, clonedMessageData)) {
2526 aError.Throw(NS_ERROR_FAILURE);
2527 return;
2530 messageData = std::move(clonedMessageData);
2531 } else {
2532 MOZ_ASSERT(message.CloneScope() ==
2533 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2535 messageData = ErrorMessageData();
2537 nsContentUtils::ReportToConsole(
2538 nsIScriptError::warningFlag, "DOM Window"_ns,
2539 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2540 nsContentUtils::eDOM_PROPERTIES,
2541 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2544 Unused << cp->SendWindowPostMessage(this, messageData, data);
2548 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2549 JS::Handle<JS::Value> aMessage,
2550 const WindowPostMessageOptions& aOptions,
2551 nsIPrincipal& aSubjectPrincipal,
2552 ErrorResult& aError) {
2553 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
2554 aSubjectPrincipal, aError);
2557 void BrowsingContext::SendCommitTransaction(ContentParent* aParent,
2558 const BaseTransaction& aTxn,
2559 uint64_t aEpoch) {
2560 Unused << aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2563 void BrowsingContext::SendCommitTransaction(ContentChild* aChild,
2564 const BaseTransaction& aTxn,
2565 uint64_t aEpoch) {
2566 aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2569 BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
2570 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
2571 MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content);
2573 IPCInitializer init;
2574 init.mId = Id();
2575 init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
2576 init.mWindowless = mWindowless;
2577 init.mUseRemoteTabs = mUseRemoteTabs;
2578 init.mUseRemoteSubframes = mUseRemoteSubframes;
2579 init.mCreatedDynamically = mCreatedDynamically;
2580 init.mChildOffset = mChildOffset;
2581 init.mOriginAttributes = mOriginAttributes;
2582 if (mChildSessionHistory && mozilla::SessionHistoryInParent()) {
2583 init.mSessionHistoryIndex = mChildSessionHistory->Index();
2584 init.mSessionHistoryCount = mChildSessionHistory->Count();
2586 init.mRequestContextId = mRequestContextId;
2587 init.mFields = mFields.RawValues();
2588 return init;
2591 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
2592 RefPtr<WindowContext> parent;
2593 if (mParentId != 0) {
2594 parent = WindowContext::GetById(mParentId);
2595 MOZ_RELEASE_ASSERT(parent);
2597 return parent.forget();
2600 already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
2601 RefPtr<BrowsingContext> opener;
2602 if (GetOpenerId() != 0) {
2603 opener = BrowsingContext::Get(GetOpenerId());
2604 MOZ_RELEASE_ASSERT(opener);
2606 return opener.forget();
2609 void BrowsingContext::StartDelayedAutoplayMediaComponents() {
2610 if (!mDocShell) {
2611 return;
2613 AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
2614 XRE_IsParentProcess() ? "Parent" : "Child", Id());
2615 mDocShell->StartDelayedAutoplayMediaComponents();
2618 nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
2619 MOZ_ASSERT(IsTop(),
2620 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2621 "browsing context");
2623 Transaction txn;
2624 txn.SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2625 txn.SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2626 return txn.Commit(this);
2629 void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
2630 MOZ_ASSERT(IsTop(),
2631 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2632 "browsing context");
2635 void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
2636 MOZ_ASSERT(IsTop(),
2637 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2638 "browsing context");
2641 void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
2642 ExplicitActiveStatus aOldValue) {
2643 const bool isActive = IsActive();
2644 const bool wasActive = [&] {
2645 if (aOldValue != ExplicitActiveStatus::None) {
2646 return aOldValue == ExplicitActiveStatus::Active;
2648 return GetParent() && GetParent()->IsActive();
2649 }();
2651 if (isActive == wasActive) {
2652 return;
2655 if (IsTop()) {
2656 Group()->UpdateToplevelsSuspendedIfNeeded();
2658 #if defined(XP_WIN) && defined(ACCESSIBILITY)
2659 if (XRE_IsParentProcess() && a11y::Compatibility::IsDolphin()) {
2660 // update active accessible documents on windows
2661 if (BrowserParent* bp = Canonical()->GetBrowserParent()) {
2662 if (a11y::DocAccessibleParent* tabDoc =
2663 bp->GetTopLevelDocAccessible()) {
2664 HWND window = tabDoc->GetEmulatedWindowHandle();
2665 MOZ_ASSERT(window);
2666 if (window) {
2667 if (isActive) {
2668 a11y::nsWinUtils::ShowNativeWindow(window);
2669 } else {
2670 a11y::nsWinUtils::HideNativeWindow(window);
2676 #endif
2679 PreOrderWalk([&](BrowsingContext* aContext) {
2680 if (nsCOMPtr<nsIDocShell> ds = aContext->GetDocShell()) {
2681 nsDocShell::Cast(ds)->ActivenessMaybeChanged();
2686 void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
2687 MOZ_ASSERT(IsTop(),
2688 "Should only set InRDMPane in the top-level browsing context");
2689 if (GetInRDMPane() == aOldValue) {
2690 return;
2693 PreOrderWalk([&](BrowsingContext* aContext) {
2694 if (nsIDocShell* shell = aContext->GetDocShell()) {
2695 if (nsPresContext* pc = shell->GetPresContext()) {
2696 pc->RecomputeTheme();
2698 // This is a bit of a lie, but this affects the overlay-scrollbars
2699 // media query and it's the code-path that gets taken for regular system
2700 // metrics changes via ThemeChanged().
2701 pc->MediaFeatureValuesChanged(
2702 {MediaFeatureChangeReason::SystemMetricsChange},
2703 MediaFeatureChangePropagation::JustThisDocument);
2709 bool BrowsingContext::CanSet(FieldIndex<IDX_PageAwakeRequestCount>,
2710 uint32_t aNewValue, ContentParent* aSource) {
2711 return IsTop() && XRE_IsParentProcess() && !aSource;
2714 void BrowsingContext::DidSet(FieldIndex<IDX_PageAwakeRequestCount>,
2715 uint32_t aOldValue) {
2716 if (!IsTop() || aOldValue == GetPageAwakeRequestCount()) {
2717 return;
2719 Group()->UpdateToplevelsSuspendedIfNeeded();
2722 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
2723 ContentParent* aSource) -> CanSetResult {
2724 if (mozilla::SessionHistoryInParent()) {
2725 return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow
2726 : CanSetResult::Deny;
2729 // Without Session History in Parent, session restore code still needs to set
2730 // this from content processes.
2731 return LegacyRevertIfNotOwningOrParentProcess(aSource);
2734 void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
2735 RecomputeCanExecuteScripts();
2738 void BrowsingContext::RecomputeCanExecuteScripts() {
2739 const bool old = mCanExecuteScripts;
2740 if (!AllowJavascript()) {
2741 // Scripting has been explicitly disabled on our BrowsingContext.
2742 mCanExecuteScripts = false;
2743 } else if (GetParentWindowContext()) {
2744 // Otherwise, inherit parent.
2745 mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts();
2746 } else {
2747 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2748 // script. Allow.
2749 mCanExecuteScripts = true;
2752 if (old != mCanExecuteScripts) {
2753 for (WindowContext* wc : GetWindowContexts()) {
2754 wc->RecomputeCanExecuteScripts();
2759 bool BrowsingContext::InactiveForSuspend() const {
2760 if (!StaticPrefs::dom_suspend_inactive_enabled()) {
2761 return false;
2763 // We should suspend a page only when it's inactive and doesn't have any awake
2764 // request that is used to prevent page from being suspended because web page
2765 // might still need to run their script. Eg. waiting for media keys to resume
2766 // media, playing web audio, waiting in a video call conference room.
2767 return !IsActive() && GetPageAwakeRequestCount() == 0;
2770 bool BrowsingContext::CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2771 dom::TouchEventsOverride, ContentParent* aSource) {
2772 return XRE_IsParentProcess() && !aSource;
2775 void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
2776 dom::PrefersColorSchemeOverride aOldValue) {
2777 MOZ_ASSERT(IsTop());
2778 if (PrefersColorSchemeOverride() == aOldValue) {
2779 return;
2781 PreOrderWalk([&](BrowsingContext* aContext) {
2782 if (nsIDocShell* shell = aContext->GetDocShell()) {
2783 if (nsPresContext* pc = shell->GetPresContext()) {
2784 pc->RecomputeBrowsingContextDependentData();
2790 void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
2791 nsString&& aOldValue) {
2792 MOZ_ASSERT(IsTop());
2793 if (GetMediumOverride() == aOldValue) {
2794 return;
2796 PreOrderWalk([&](BrowsingContext* aContext) {
2797 if (nsIDocShell* shell = aContext->GetDocShell()) {
2798 if (nsPresContext* pc = shell->GetPresContext()) {
2799 pc->RecomputeBrowsingContextDependentData();
2805 void BrowsingContext::DidSet(FieldIndex<IDX_DisplayMode>,
2806 enum DisplayMode aOldValue) {
2807 MOZ_ASSERT(IsTop());
2809 if (GetDisplayMode() == aOldValue) {
2810 return;
2813 PreOrderWalk([&](BrowsingContext* aContext) {
2814 if (nsIDocShell* shell = aContext->GetDocShell()) {
2815 if (nsPresContext* pc = shell->GetPresContext()) {
2816 pc->MediaFeatureValuesChanged(
2817 {MediaFeatureChangeReason::DisplayModeChange},
2818 // We're already iterating through sub documents, so we don't need
2819 // to propagate the change again.
2821 // Images and other resources don't change their display-mode
2822 // evaluation, display-mode is a property of the browsing context.
2823 MediaFeatureChangePropagation::JustThisDocument);
2829 void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) {
2830 MOZ_ASSERT(IsTop(), "Set muted flag on non top-level context!");
2831 USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64,
2832 GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child",
2833 Id());
2834 PreOrderWalk([&](BrowsingContext* aContext) {
2835 nsPIDOMWindowOuter* win = aContext->GetDOMWindow();
2836 if (win) {
2837 win->RefreshMediaElementsVolume();
2842 bool BrowsingContext::CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2843 const bool& aValue, ContentParent* aSource) {
2844 return IsTop();
2847 void BrowsingContext::DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2848 bool aOldValue) {
2849 MOZ_ASSERT(IsTop(), "Set attribute on non top-level context!");
2850 if (aOldValue == GetShouldDelayMediaFromStart()) {
2851 return;
2853 if (!GetShouldDelayMediaFromStart()) {
2854 PreOrderWalk([&](BrowsingContext* aContext) {
2855 if (nsPIDOMWindowOuter* win = aContext->GetDOMWindow()) {
2856 win->ActivateMediaComponents();
2862 bool BrowsingContext::CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue,
2863 ContentParent* aSource) {
2864 return XRE_IsParentProcess() && !aSource && IsTop();
2867 void BrowsingContext::DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue) {
2868 MOZ_ASSERT(IsTop());
2869 if (GetOverrideDPPX() == aOldValue) {
2870 return;
2873 PreOrderWalk([&](BrowsingContext* aContext) {
2874 if (nsIDocShell* shell = aContext->GetDocShell()) {
2875 if (nsPresContext* pc = shell->GetPresContext()) {
2876 pc->RecomputeBrowsingContextDependentData();
2882 void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent,
2883 ErrorResult& aRv) {
2884 Top()->SetUserAgentOverride(aUserAgent, aRv);
2887 nsresult BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) {
2888 return Top()->SetUserAgentOverride(aUserAgent);
2891 void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
2892 MOZ_ASSERT(IsTop());
2894 PreOrderWalk([&](BrowsingContext* aContext) {
2895 nsIDocShell* shell = aContext->GetDocShell();
2896 if (shell) {
2897 shell->ClearCachedUserAgent();
2902 bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool,
2903 ContentParent* aSource) {
2904 return IsTop() && !aSource && mozilla::BFCacheInParent();
2907 void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
2908 MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent());
2909 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2911 const bool isInBFCache = GetIsInBFCache();
2912 if (!isInBFCache) {
2913 PreOrderWalk([&](BrowsingContext* aContext) {
2914 aContext->mIsInBFCache = false;
2915 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2916 if (shell) {
2917 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true);
2922 if (isInBFCache && XRE_IsContentProcess() && mDocShell) {
2923 nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide();
2926 if (isInBFCache) {
2927 PreOrderWalk([&](BrowsingContext* aContext) {
2928 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2929 if (shell) {
2930 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false);
2933 } else {
2934 PostOrderWalk([&](BrowsingContext* aContext) {
2935 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2936 if (shell) {
2937 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true);
2942 if (isInBFCache) {
2943 PreOrderWalk([&](BrowsingContext* aContext) {
2944 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2945 if (shell) {
2946 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false);
2948 aContext->mIsInBFCache = true;
2949 Document* doc = aContext->GetDocument();
2950 if (doc) {
2951 // Notifying needs to happen after mIsInBFCache is set to true.
2952 doc->NotifyActivityChanged();
2958 void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
2959 ErrorResult& aRv) {
2960 Top()->SetPlatformOverride(aPlatform, aRv);
2963 void BrowsingContext::DidSet(FieldIndex<IDX_PlatformOverride>) {
2964 MOZ_ASSERT(IsTop());
2966 PreOrderWalk([&](BrowsingContext* aContext) {
2967 nsIDocShell* shell = aContext->GetDocShell();
2968 if (shell) {
2969 shell->ClearCachedPlatform();
2974 auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
2975 ContentParent* aSource) -> CanSetResult {
2976 if (aSource) {
2977 MOZ_ASSERT(XRE_IsParentProcess());
2979 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
2980 return CanSetResult::Revert;
2982 } else if (!IsInProcess() && !XRE_IsParentProcess()) {
2983 // Don't allow this to be set from content processes that
2984 // don't own the BrowsingContext.
2985 return CanSetResult::Deny;
2988 return CanSetResult::Allow;
2991 bool BrowsingContext::CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
2992 const bool& aValue, ContentParent* aSource) {
2993 // Should only be set in the parent process.
2994 return XRE_IsParentProcess() && !aSource && IsTop();
2997 void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
2998 bool aOldValue) {
2999 bool isActivateEvent = GetIsActiveBrowserWindowInternal();
3000 // The browser window containing this context has changed
3001 // activation state so update window inactive document states
3002 // for all in-process documents.
3003 PreOrderWalk([isActivateEvent](BrowsingContext* aContext) {
3004 if (RefPtr<Document> doc = aContext->GetExtantDocument()) {
3005 doc->UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, true);
3007 RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
3008 RefPtr<MediaDevices> devices;
3009 if (isActivateEvent && (devices = win->GetExtantMediaDevices())) {
3010 devices->BrowserWindowBecameActive();
3013 if (XRE_IsContentProcess() &&
3014 (!aContext->GetParent() || !aContext->GetParent()->IsInProcess())) {
3015 // Send the inner window an activate/deactivate event if
3016 // the context is the top of a sub-tree of in-process
3017 // contexts.
3018 nsContentUtils::DispatchEventOnlyToChrome(
3019 doc, win, isActivateEvent ? u"activate"_ns : u"deactivate"_ns,
3020 CanBubble::eYes, Cancelable::eYes, nullptr);
3026 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
3027 const bool& aAllowContentRetargeting,
3028 ContentParent* aSource) -> CanSetResult {
3029 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3032 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
3033 const bool& aAllowContentRetargetingOnChildren,
3034 ContentParent* aSource) -> CanSetResult {
3035 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3038 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowPlugins>,
3039 const bool& aAllowPlugins, ContentParent* aSource)
3040 -> CanSetResult {
3041 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3044 bool BrowsingContext::CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>,
3045 const bool& aAllowed, ContentParent* aSource) {
3046 return CheckOnlyEmbedderCanSet(aSource);
3049 bool BrowsingContext::CanSet(FieldIndex<IDX_UseErrorPages>,
3050 const bool& aUseErrorPages,
3051 ContentParent* aSource) {
3052 return CheckOnlyEmbedderCanSet(aSource);
3055 mozilla::dom::TouchEventsOverride BrowsingContext::TouchEventsOverride() const {
3056 const BrowsingContext* bc = this;
3057 while (bc) {
3058 mozilla::dom::TouchEventsOverride tev =
3059 bc->GetTouchEventsOverrideInternal();
3060 if (tev != mozilla::dom::TouchEventsOverride::None) {
3061 return tev;
3064 bc = bc->GetParent();
3067 return mozilla::dom::TouchEventsOverride::None;
3070 // We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
3071 // BC field. And we map it to the top level BrowsingContext.
3072 bool BrowsingContext::WatchedByDevTools() {
3073 return Top()->GetWatchedByDevToolsInternal();
3076 // Enforce that the watchedByDevTools BC field can only be set on the top level
3077 // Browsing Context.
3078 bool BrowsingContext::CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>,
3079 const bool& aWatchedByDevTools,
3080 ContentParent* aSource) {
3081 return IsTop();
3083 void BrowsingContext::SetWatchedByDevTools(bool aWatchedByDevTools,
3084 ErrorResult& aRv) {
3085 if (!IsTop()) {
3086 aRv.ThrowInvalidModificationError(
3087 "watchedByDevTools can only be set on top BrowsingContext");
3088 return;
3090 SetWatchedByDevToolsInternal(aWatchedByDevTools, aRv);
3093 auto BrowsingContext::CanSet(FieldIndex<IDX_DefaultLoadFlags>,
3094 const uint32_t& aDefaultLoadFlags,
3095 ContentParent* aSource) -> CanSetResult {
3096 // Bug 1623565 - Are these flags only used by the debugger, which makes it
3097 // possible that this field can only be settable by the parent process?
3098 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3101 void BrowsingContext::DidSet(FieldIndex<IDX_DefaultLoadFlags>) {
3102 auto loadFlags = GetDefaultLoadFlags();
3103 if (GetDocShell()) {
3104 nsDocShell::Cast(GetDocShell())->SetLoadGroupDefaultLoadFlags(loadFlags);
3107 if (XRE_IsParentProcess()) {
3108 PreOrderWalk([&](BrowsingContext* aContext) {
3109 if (aContext != this) {
3110 // Setting load flags on a discarded context has no effect.
3111 Unused << aContext->SetDefaultLoadFlags(loadFlags);
3117 bool BrowsingContext::CanSet(FieldIndex<IDX_UseGlobalHistory>,
3118 const bool& aUseGlobalHistory,
3119 ContentParent* aSource) {
3120 // Should only be set in the parent process.
3121 // return XRE_IsParentProcess() && !aSource;
3122 return true;
3125 auto BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>,
3126 const nsString& aUserAgent, ContentParent* aSource)
3127 -> CanSetResult {
3128 if (!IsTop()) {
3129 return CanSetResult::Deny;
3132 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3135 auto BrowsingContext::CanSet(FieldIndex<IDX_PlatformOverride>,
3136 const nsString& aPlatform, ContentParent* aSource)
3137 -> CanSetResult {
3138 if (!IsTop()) {
3139 return CanSetResult::Deny;
3142 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3145 bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
3146 if (XRE_IsParentProcess()) {
3147 uint64_t childId = aSource ? aSource->ChildID() : 0;
3148 return Canonical()->IsEmbeddedInProcess(childId);
3150 return mEmbeddedByThisProcess;
3153 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
3154 const uint64_t& aValue, ContentParent* aSource) {
3155 // If we have a parent window, our embedder inner window ID must match it.
3156 if (mParentWindow) {
3157 return mParentWindow->Id() == aValue;
3160 // For toplevel BrowsingContext instances, this value may only be set by the
3161 // parent process, or initialized to `0`.
3162 return CheckOnlyEmbedderCanSet(aSource);
3165 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
3166 const Maybe<nsString>&, ContentParent* aSource) {
3167 return CheckOnlyEmbedderCanSet(aSource);
3170 auto BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
3171 const uint64_t& aValue, ContentParent* aSource)
3172 -> CanSetResult {
3173 // Generally allow clearing this. We may want to be more precise about this
3174 // check in the future.
3175 if (aValue == 0) {
3176 return CanSetResult::Allow;
3179 // We must have access to the specified context.
3180 RefPtr<WindowContext> window = WindowContext::GetById(aValue);
3181 if (!window || window->GetBrowsingContext() != this) {
3182 return CanSetResult::Deny;
3185 if (aSource) {
3186 // If the sending process is no longer the current owner, revert
3187 MOZ_ASSERT(XRE_IsParentProcess());
3188 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
3189 return CanSetResult::Revert;
3191 } else if (XRE_IsContentProcess() && !IsOwnedByProcess()) {
3192 return CanSetResult::Deny;
3195 return CanSetResult::Allow;
3198 bool BrowsingContext::CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>,
3199 const uint64_t& aValue, ContentParent* aSource) {
3200 return XRE_IsParentProcess() && !aSource;
3203 void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
3204 RefPtr<WindowContext> prevWindowContext = mCurrentWindowContext.forget();
3205 mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
3206 MOZ_ASSERT(
3207 !mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext),
3208 "WindowContext not registered?");
3210 // Clear our cached `children` value, to ensure that JS sees the up-to-date
3211 // value.
3212 BrowsingContext_Binding::ClearCachedChildrenValue(this);
3214 if (XRE_IsParentProcess()) {
3215 if (prevWindowContext != mCurrentWindowContext) {
3216 if (prevWindowContext) {
3217 prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false);
3219 if (mCurrentWindowContext) {
3220 // We set a timer when we set the current inner window. This
3221 // will then flush the session storage to session store to
3222 // make sure that we don't miss to store session storage to
3223 // session store that is a result of navigation. This is due
3224 // to Bug 1700623. We wish to fix this in Bug 1711886, where
3225 // making sure to store everything would make this timer
3226 // unnecessary.
3227 Canonical()->MaybeScheduleSessionStoreUpdate();
3228 mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true);
3231 BrowserParent::UpdateFocusFromBrowsingContext();
3235 bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
3236 ContentParent* aSource) {
3237 // Ensure that we only mark a browsing context as popup spam once and never
3238 // unmark it.
3239 return aValue && !GetIsPopupSpam();
3242 void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) {
3243 if (GetIsPopupSpam()) {
3244 PopupBlocker::RegisterOpenPopupSpam();
3248 bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>,
3249 const nsString& aMessageManagerGroup,
3250 ContentParent* aSource) {
3251 // Should only be set in the parent process on toplevel.
3252 return XRE_IsParentProcess() && !aSource && IsTopContent();
3255 bool BrowsingContext::CanSet(
3256 FieldIndex<IDX_OrientationLock>,
3257 const mozilla::hal::ScreenOrientation& aOrientationLock,
3258 ContentParent* aSource) {
3259 return IsTop();
3262 bool BrowsingContext::IsLoading() {
3263 if (GetLoading()) {
3264 return true;
3267 // If we're in the same process as the page, we're possibly just
3268 // updating the flag.
3269 nsIDocShell* shell = GetDocShell();
3270 if (shell) {
3271 Document* doc = shell->GetDocument();
3272 return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
3275 return false;
3278 void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) {
3279 if (mFields.Get<IDX_Loading>()) {
3280 return;
3283 while (!mDeprioritizedLoadRunner.isEmpty()) {
3284 nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
3285 NS_DispatchToCurrentThread(runner.forget());
3288 if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
3289 Top() == this) {
3290 Group()->FlushPostMessageEvents();
3294 // Inform the Document for this context of the (potential) change in
3295 // loading state
3296 void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) {
3297 nsPIDOMWindowOuter* outer = GetDOMWindow();
3298 if (!outer) {
3299 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3300 ("DidSetAncestorLoading BC: %p -- No outer window", (void*)this));
3301 return;
3303 Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc();
3304 if (document) {
3305 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3306 ("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)",
3307 (void*)this, GetAncestorLoading(), document->GetReadyStateEnum(),
3308 document->GetReadyStateEnum()));
3309 document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(),
3310 document->GetReadyStateEnum());
3314 void BrowsingContext::DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>) {
3315 MOZ_ASSERT(IsTop(),
3316 "Should only set AuthorStyleDisabledDefault in the top "
3317 "browsing context");
3319 // We don't need to handle changes to this field, since PageStyleChild.jsm
3320 // will respond to the PageStyle:Disable message in all content processes.
3322 // But we store the state here on the top BrowsingContext so that the
3323 // docshell has somewhere to look for the current author style disabling
3324 // state when new iframes are inserted.
3327 void BrowsingContext::DidSet(FieldIndex<IDX_TextZoom>, float aOldValue) {
3328 if (GetTextZoom() == aOldValue) {
3329 return;
3332 if (IsInProcess()) {
3333 if (nsIDocShell* shell = GetDocShell()) {
3334 if (nsPresContext* pc = shell->GetPresContext()) {
3335 pc->RecomputeBrowsingContextDependentData();
3339 for (BrowsingContext* child : Children()) {
3340 // Setting text zoom on a discarded context has no effect.
3341 Unused << child->SetTextZoom(GetTextZoom());
3345 if (IsTop() && XRE_IsParentProcess()) {
3346 if (Element* element = GetEmbedderElement()) {
3347 auto dispatcher = MakeRefPtr<AsyncEventDispatcher>(
3348 element, u"TextZoomChange"_ns, CanBubble::eYes,
3349 ChromeOnlyDispatch::eYes);
3350 dispatcher->RunDOMEventWhenSafe();
3355 // TODO(emilio): It'd be potentially nicer and cheaper to allow to set this only
3356 // on the Top() browsing context, but there are a lot of tests that rely on
3357 // zooming a subframe so...
3358 void BrowsingContext::DidSet(FieldIndex<IDX_FullZoom>, float aOldValue) {
3359 if (GetFullZoom() == aOldValue) {
3360 return;
3363 if (IsInProcess()) {
3364 if (nsIDocShell* shell = GetDocShell()) {
3365 if (nsPresContext* pc = shell->GetPresContext()) {
3366 pc->RecomputeBrowsingContextDependentData();
3370 for (BrowsingContext* child : Children()) {
3371 // Setting full zoom on a discarded context has no effect.
3372 Unused << child->SetFullZoom(GetFullZoom());
3376 if (IsTop() && XRE_IsParentProcess()) {
3377 if (Element* element = GetEmbedderElement()) {
3378 auto dispatcher = MakeRefPtr<AsyncEventDispatcher>(
3379 element, u"FullZoomChange"_ns, CanBubble::eYes,
3380 ChromeOnlyDispatch::eYes);
3381 dispatcher->RunDOMEventWhenSafe();
3386 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
3387 MOZ_ASSERT(IsLoading());
3388 MOZ_ASSERT(Top() == this);
3390 RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
3391 mDeprioritizedLoadRunner.insertBack(runner);
3392 NS_DispatchToCurrentThreadQueue(
3393 runner.forget(), StaticPrefs::page_load_deprioritization_period(),
3394 EventQueuePriority::Idle);
3397 bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const {
3398 for (const BrowsingContext* current = this; current && current->GetParent();
3399 current = current->GetParent()) {
3400 if (current->CreatedDynamically()) {
3401 return false;
3403 aPath.AppendElement(current->ChildOffset());
3405 return true;
3408 void BrowsingContext::GetHistoryID(JSContext* aCx,
3409 JS::MutableHandle<JS::Value> aVal,
3410 ErrorResult& aError) {
3411 if (!xpc::ID2JSValue(aCx, GetHistoryID(), aVal)) {
3412 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
3416 void BrowsingContext::InitSessionHistory() {
3417 MOZ_ASSERT(!IsDiscarded());
3418 MOZ_ASSERT(IsTop());
3419 MOZ_ASSERT(EverAttached());
3421 if (!GetHasSessionHistory()) {
3422 MOZ_ALWAYS_SUCCEEDS(SetHasSessionHistory(true));
3426 ChildSHistory* BrowsingContext::GetChildSessionHistory() {
3427 if (!mozilla::SessionHistoryInParent()) {
3428 // For now we're checking that the session history object for the child
3429 // process is available before returning the ChildSHistory object, because
3430 // it is the actual implementation that ChildSHistory forwards to. This can
3431 // be removed once session history is stored exclusively in the parent
3432 // process.
3433 return mChildSessionHistory && mChildSessionHistory->IsInProcess()
3434 ? mChildSessionHistory.get()
3435 : nullptr;
3438 return mChildSessionHistory;
3441 void BrowsingContext::CreateChildSHistory() {
3442 MOZ_ASSERT(IsTop());
3443 MOZ_ASSERT(GetHasSessionHistory());
3444 MOZ_DIAGNOSTIC_ASSERT(!mChildSessionHistory);
3446 // Because session history is global in a browsing context tree, every process
3447 // that has access to a browsing context tree needs access to its session
3448 // history. That is why we create the ChildSHistory object in every process
3449 // where we have access to this browsing context (which is the top one).
3450 mChildSessionHistory = new ChildSHistory(this);
3452 // If the top browsing context (this one) is loaded in this process then we
3453 // also create the session history implementation for the child process.
3454 // This can be removed once session history is stored exclusively in the
3455 // parent process.
3456 mChildSessionHistory->SetIsInProcess(IsInProcess());
3459 void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
3460 bool aOldValue) {
3461 MOZ_ASSERT(GetHasSessionHistory() || !aOldValue,
3462 "We don't support turning off session history.");
3464 if (GetHasSessionHistory() && !aOldValue) {
3465 CreateChildSHistory();
3469 bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
3470 ContentParent* aSource) {
3471 // We should only be able to set this for toplevel contexts which don't have
3472 // an ID yet.
3473 return GetBrowserId() == 0 && IsTop() && Children().IsEmpty();
3476 bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
3477 bool aNewValue, ContentParent* aSource) {
3478 // Can only be cleared from `true` to `false`, and should only ever be set on
3479 // the toplevel BrowsingContext.
3480 return IsTop() && GetPendingInitialization() && !aNewValue;
3483 bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
3484 ContentParent* aSource) {
3485 return IsTop();
3488 bool BrowsingContext::IsPopupAllowed() {
3489 for (auto* context = GetCurrentWindowContext(); context;
3490 context = context->GetParentWindowContext()) {
3491 if (context->CanShowPopup()) {
3492 return true;
3496 return false;
3499 /* static */
3500 bool BrowsingContext::ShouldAddEntryForRefresh(
3501 nsIURI* aCurrentURI, const SessionHistoryInfo& aInfo) {
3502 return ShouldAddEntryForRefresh(aCurrentURI, aInfo.GetURI(),
3503 aInfo.GetPostData());
3506 /* static */
3507 bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aCurrentURI,
3508 nsIURI* aNewURI,
3509 bool aHasPostData) {
3510 if (aHasPostData) {
3511 return true;
3514 bool equalsURI = false;
3515 if (aCurrentURI) {
3516 aCurrentURI->Equals(aNewURI, &equalsURI);
3518 return !equalsURI;
3521 void BrowsingContext::SessionHistoryCommit(
3522 const LoadingSessionHistoryInfo& aInfo, uint32_t aLoadType,
3523 nsIURI* aCurrentURI, bool aHadActiveEntry, bool aPersist,
3524 bool aCloneEntryChildren, bool aChannelExpired, uint32_t aCacheKey) {
3525 nsID changeID = {};
3526 if (XRE_IsContentProcess()) {
3527 RefPtr<ChildSHistory> rootSH = Top()->GetChildSessionHistory();
3528 if (rootSH) {
3529 if (!aInfo.mLoadIsFromSessionHistory) {
3530 // We try to mimic as closely as possible what will happen in
3531 // CanonicalBrowsingContext::SessionHistoryCommit. We'll be
3532 // incrementing the session history length if we're not replacing,
3533 // this is a top-level load or it's not the initial load in an iframe,
3534 // ShouldUpdateSessionHistory(loadType) returns true and it's not a
3535 // refresh for which ShouldAddEntryForRefresh returns false.
3536 // It is possible that this leads to wrong length temporarily, but
3537 // so would not having the check for replace.
3538 if (!LOAD_TYPE_HAS_FLAGS(
3539 aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) &&
3540 (IsTop() || aHadActiveEntry) &&
3541 ShouldUpdateSessionHistory(aLoadType) &&
3542 (!LOAD_TYPE_HAS_FLAGS(aLoadType,
3543 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) ||
3544 ShouldAddEntryForRefresh(aCurrentURI, aInfo.mInfo))) {
3545 changeID = rootSH->AddPendingHistoryChange();
3547 } else {
3548 // History load doesn't change the length, only index.
3549 changeID = rootSH->AddPendingHistoryChange(aInfo.mOffset, 0);
3552 ContentChild* cc = ContentChild::GetSingleton();
3553 mozilla::Unused << cc->SendHistoryCommit(
3554 this, aInfo.mLoadId, changeID, aLoadType, aPersist, aCloneEntryChildren,
3555 aChannelExpired, aCacheKey);
3556 } else {
3557 Canonical()->SessionHistoryCommit(aInfo.mLoadId, changeID, aLoadType,
3558 aPersist, aCloneEntryChildren,
3559 aChannelExpired, aCacheKey);
3563 void BrowsingContext::SetActiveSessionHistoryEntry(
3564 const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
3565 uint32_t aLoadType, uint32_t aUpdatedCacheKey) {
3566 if (XRE_IsContentProcess()) {
3567 // XXX Why we update cache key only in content process case?
3568 if (aUpdatedCacheKey != 0) {
3569 aInfo->SetCacheKey(aUpdatedCacheKey);
3572 nsID changeID = {};
3573 RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
3574 if (shistory) {
3575 changeID = shistory->AddPendingHistoryChange();
3577 ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry(
3578 this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey,
3579 changeID);
3580 } else {
3581 Canonical()->SetActiveSessionHistoryEntry(
3582 aPreviousScrollPos, aInfo, aLoadType, aUpdatedCacheKey, nsID());
3586 void BrowsingContext::ReplaceActiveSessionHistoryEntry(
3587 SessionHistoryInfo* aInfo) {
3588 if (XRE_IsContentProcess()) {
3589 ContentChild::GetSingleton()->SendReplaceActiveSessionHistoryEntry(this,
3590 *aInfo);
3591 } else {
3592 Canonical()->ReplaceActiveSessionHistoryEntry(aInfo);
3596 void BrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
3597 if (XRE_IsContentProcess()) {
3598 ContentChild::GetSingleton()
3599 ->SendRemoveDynEntriesFromActiveSessionHistoryEntry(this);
3600 } else {
3601 Canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
3605 void BrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
3606 if (XRE_IsContentProcess()) {
3607 ContentChild::GetSingleton()->SendRemoveFromSessionHistory(this, aChangeID);
3608 } else {
3609 Canonical()->RemoveFromSessionHistory(aChangeID);
3613 void BrowsingContext::HistoryGo(int32_t aOffset, uint64_t aHistoryEpoch,
3614 bool aRequireUserInteraction,
3615 bool aUserActivation,
3616 std::function<void(int32_t&&)>&& aResolver) {
3617 if (XRE_IsContentProcess()) {
3618 ContentChild::GetSingleton()->SendHistoryGo(
3619 this, aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3620 std::move(aResolver),
3621 [](mozilla::ipc::
3622 ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
3623 } else {
3624 Canonical()->HistoryGo(
3625 aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3626 Canonical()->GetContentParent()
3627 ? Some(Canonical()->GetContentParent()->ChildID())
3628 : Nothing(),
3629 std::move(aResolver));
3633 void BrowsingContext::SetChildSHistory(ChildSHistory* aChildSHistory) {
3634 mChildSessionHistory = aChildSHistory;
3635 mChildSessionHistory->SetBrowsingContext(this);
3636 mFields.SetWithoutSyncing<IDX_HasSessionHistory>(true);
3639 bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) {
3640 // We don't update session history on reload unless we're loading
3641 // an iframe in shift-reload case.
3642 return nsDocShell::ShouldUpdateGlobalHistory(aLoadType) &&
3643 (!(aLoadType & nsIDocShell::LOAD_CMD_RELOAD) ||
3644 (IsForceReloadType(aLoadType) && IsSubframe()));
3647 nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
3648 // We only rate limit non system callers
3649 if (aCallerType == CallerType::System) {
3650 return NS_OK;
3653 // Fetch rate limiting preferences
3654 uint32_t limitCount =
3655 StaticPrefs::dom_navigation_locationChangeRateLimit_count();
3656 uint32_t timeSpanSeconds =
3657 StaticPrefs::dom_navigation_locationChangeRateLimit_timespan();
3659 // Disable throttling if either of the preferences is set to 0.
3660 if (limitCount == 0 || timeSpanSeconds == 0) {
3661 return NS_OK;
3664 TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds);
3666 if (mLocationChangeRateLimitSpanStart.IsNull() ||
3667 ((TimeStamp::Now() - mLocationChangeRateLimitSpanStart) > throttleSpan)) {
3668 // Initial call or timespan exceeded, reset counter and timespan.
3669 mLocationChangeRateLimitSpanStart = TimeStamp::Now();
3670 mLocationChangeRateLimitCount = 1;
3671 return NS_OK;
3674 if (mLocationChangeRateLimitCount >= limitCount) {
3675 // Rate limit reached
3677 Document* doc = GetDocument();
3678 if (doc) {
3679 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc,
3680 nsContentUtils::eDOM_PROPERTIES,
3681 "LocChangeFloodingPrevented");
3684 return NS_ERROR_DOM_SECURITY_ERR;
3687 mLocationChangeRateLimitCount++;
3688 return NS_OK;
3691 void BrowsingContext::ResetLocationChangeRateLimit() {
3692 // Resetting the timestamp object will cause the check function to
3693 // init again and reset the rate limit.
3694 mLocationChangeRateLimitSpanStart = TimeStamp();
3697 } // namespace dom
3699 namespace ipc {
3701 void IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Write(
3702 IPC::Message* aMsg, IProtocol* aActor,
3703 const dom::MaybeDiscarded<dom::BrowsingContext>& aParam) {
3704 MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
3705 aParam.GetMaybeDiscarded()->EverAttached());
3706 uint64_t id = aParam.ContextId();
3707 WriteIPDLParam(aMsg, aActor, id);
3710 bool IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Read(
3711 const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
3712 dom::MaybeDiscarded<dom::BrowsingContext>* aResult) {
3713 uint64_t id = 0;
3714 if (!ReadIPDLParam(aMsg, aIter, aActor, &id)) {
3715 return false;
3718 if (id == 0) {
3719 *aResult = nullptr;
3720 } else if (RefPtr<dom::BrowsingContext> bc = dom::BrowsingContext::Get(id)) {
3721 *aResult = std::move(bc);
3722 } else {
3723 aResult->SetDiscarded(id);
3725 return true;
3728 void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
3729 IPC::Message* aMessage, IProtocol* aActor,
3730 const dom::BrowsingContext::IPCInitializer& aInit) {
3731 // Write actor ID parameters.
3732 WriteIPDLParam(aMessage, aActor, aInit.mId);
3733 WriteIPDLParam(aMessage, aActor, aInit.mParentId);
3734 WriteIPDLParam(aMessage, aActor, aInit.mWindowless);
3735 WriteIPDLParam(aMessage, aActor, aInit.mUseRemoteTabs);
3736 WriteIPDLParam(aMessage, aActor, aInit.mUseRemoteSubframes);
3737 WriteIPDLParam(aMessage, aActor, aInit.mCreatedDynamically);
3738 WriteIPDLParam(aMessage, aActor, aInit.mChildOffset);
3739 WriteIPDLParam(aMessage, aActor, aInit.mOriginAttributes);
3740 WriteIPDLParam(aMessage, aActor, aInit.mRequestContextId);
3741 WriteIPDLParam(aMessage, aActor, aInit.mSessionHistoryIndex);
3742 WriteIPDLParam(aMessage, aActor, aInit.mSessionHistoryCount);
3743 WriteIPDLParam(aMessage, aActor, aInit.mFields);
3746 bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
3747 const IPC::Message* aMessage, PickleIterator* aIterator, IProtocol* aActor,
3748 dom::BrowsingContext::IPCInitializer* aInit) {
3749 // Read actor ID parameters.
3750 if (!ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mId) ||
3751 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mParentId) ||
3752 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mWindowless) ||
3753 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mUseRemoteTabs) ||
3754 !ReadIPDLParam(aMessage, aIterator, aActor,
3755 &aInit->mUseRemoteSubframes) ||
3756 !ReadIPDLParam(aMessage, aIterator, aActor,
3757 &aInit->mCreatedDynamically) ||
3758 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mChildOffset) ||
3759 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mOriginAttributes) ||
3760 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mRequestContextId) ||
3761 !ReadIPDLParam(aMessage, aIterator, aActor,
3762 &aInit->mSessionHistoryIndex) ||
3763 !ReadIPDLParam(aMessage, aIterator, aActor,
3764 &aInit->mSessionHistoryCount) ||
3765 !ReadIPDLParam(aMessage, aIterator, aActor, &aInit->mFields)) {
3766 return false;
3768 return true;
3771 template struct IPDLParamTraits<dom::BrowsingContext::BaseTransaction>;
3773 } // namespace ipc
3774 } // namespace mozilla