Backed out changeset cfe0bbc666b8 (bug 1784757) in order to wait some more for a...
[gecko.git] / docshell / base / BrowsingContext.cpp
blob211f83a476caa08281b3a0158da42cb08b01a374
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 "nsAccessibilityService.h"
15 # if defined(XP_WIN)
16 # include "mozilla/a11y/AccessibleWrap.h"
17 # include "mozilla/a11y/Compatibility.h"
18 # include "mozilla/a11y/nsWinUtils.h"
19 # endif
20 #endif
21 #include "mozilla/AppShutdown.h"
22 #include "mozilla/dom/CanonicalBrowsingContext.h"
23 #include "mozilla/dom/BrowserHost.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/SessionStoreChild.h"
40 #include "mozilla/dom/SessionStorageManager.h"
41 #include "mozilla/dom/StructuredCloneTags.h"
42 #include "mozilla/dom/UserActivationIPCUtils.h"
43 #include "mozilla/dom/WindowBinding.h"
44 #include "mozilla/dom/WindowContext.h"
45 #include "mozilla/dom/WindowGlobalChild.h"
46 #include "mozilla/dom/WindowGlobalParent.h"
47 #include "mozilla/dom/WindowProxyHolder.h"
48 #include "mozilla/dom/SyncedContextInlines.h"
49 #include "mozilla/dom/XULFrameElement.h"
50 #include "mozilla/ipc/ProtocolUtils.h"
51 #include "mozilla/net/DocumentLoadListener.h"
52 #include "mozilla/net/RequestContextService.h"
53 #include "mozilla/Assertions.h"
54 #include "mozilla/AsyncEventDispatcher.h"
55 #include "mozilla/ClearOnShutdown.h"
56 #include "mozilla/Components.h"
57 #include "mozilla/HashTable.h"
58 #include "mozilla/Logging.h"
59 #include "mozilla/MediaFeatureChange.h"
60 #include "mozilla/ResultExtensions.h"
61 #include "mozilla/Services.h"
62 #include "mozilla/StaticPrefs_fission.h"
63 #include "mozilla/StaticPrefs_media.h"
64 #include "mozilla/StaticPrefs_page_load.h"
65 #include "mozilla/StaticPtr.h"
66 #include "mozilla/URLQueryStringStripper.h"
67 #include "mozilla/EventStateManager.h"
68 #include "nsIURIFixup.h"
69 #include "nsIXULRuntime.h"
71 #include "nsDocShell.h"
72 #include "nsDocShellLoadState.h"
73 #include "nsFocusManager.h"
74 #include "nsGlobalWindowInner.h"
75 #include "nsGlobalWindowOuter.h"
76 #include "PresShell.h"
77 #include "nsIObserverService.h"
78 #include "nsISHistory.h"
79 #include "nsContentUtils.h"
80 #include "nsQueryObject.h"
81 #include "nsSandboxFlags.h"
82 #include "nsScriptError.h"
83 #include "nsThreadUtils.h"
84 #include "xpcprivate.h"
86 #include "AutoplayPolicy.h"
87 #include "GVAutoplayRequestStatusIPC.h"
89 extern mozilla::LazyLogModule gAutoplayPermissionLog;
90 extern mozilla::LazyLogModule gTimeoutDeferralLog;
92 #define AUTOPLAY_LOG(msg, ...) \
93 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
95 namespace IPC {
96 // Allow serialization and deserialization of OrientationType over IPC
97 template <>
98 struct ParamTraits<mozilla::dom::OrientationType>
99 : public ContiguousEnumSerializer<
100 mozilla::dom::OrientationType,
101 mozilla::dom::OrientationType::Portrait_primary,
102 mozilla::dom::OrientationType::EndGuard_> {};
104 template <>
105 struct ParamTraits<mozilla::dom::DisplayMode>
106 : public ContiguousEnumSerializer<mozilla::dom::DisplayMode,
107 mozilla::dom::DisplayMode::Browser,
108 mozilla::dom::DisplayMode::EndGuard_> {};
110 template <>
111 struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
112 : public ContiguousEnumSerializer<
113 mozilla::dom::PrefersColorSchemeOverride,
114 mozilla::dom::PrefersColorSchemeOverride::None,
115 mozilla::dom::PrefersColorSchemeOverride::EndGuard_> {};
117 template <>
118 struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
119 : public ContiguousEnumSerializer<
120 mozilla::dom::ExplicitActiveStatus,
121 mozilla::dom::ExplicitActiveStatus::None,
122 mozilla::dom::ExplicitActiveStatus::EndGuard_> {};
124 // Allow serialization and deserialization of TouchEventsOverride over IPC
125 template <>
126 struct ParamTraits<mozilla::dom::TouchEventsOverride>
127 : public ContiguousEnumSerializer<
128 mozilla::dom::TouchEventsOverride,
129 mozilla::dom::TouchEventsOverride::Disabled,
130 mozilla::dom::TouchEventsOverride::EndGuard_> {};
132 template <>
133 struct ParamTraits<mozilla::dom::EmbedderColorSchemes> {
134 using paramType = mozilla::dom::EmbedderColorSchemes;
136 static void Write(MessageWriter* aWriter, const paramType& aParam) {
137 WriteParam(aWriter, aParam.mUsed);
138 WriteParam(aWriter, aParam.mPreferred);
141 static bool Read(MessageReader* aReader, paramType* aResult) {
142 return ReadParam(aReader, &aResult->mUsed) &&
143 ReadParam(aReader, &aResult->mPreferred);
147 } // namespace IPC
149 namespace mozilla {
150 namespace dom {
152 // Explicit specialization of the `Transaction` type. Required by the `extern
153 // template class` declaration in the header.
154 template class syncedcontext::Transaction<BrowsingContext>;
156 extern mozilla::LazyLogModule gUserInteractionPRLog;
158 #define USER_ACTIVATION_LOG(msg, ...) \
159 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
161 static LazyLogModule gBrowsingContextLog("BrowsingContext");
162 static LazyLogModule gBrowsingContextSyncLog("BrowsingContextSync");
164 typedef nsTHashMap<nsUint64HashKey, BrowsingContext*> BrowsingContextMap;
166 // All BrowsingContexts indexed by Id
167 static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts;
168 // Top-level Content BrowsingContexts only, indexed by BrowserId instead of Id
169 static StaticAutoPtr<BrowsingContextMap> sCurrentTopByBrowserId;
171 static void UnregisterBrowserId(BrowsingContext* aBrowsingContext) {
172 if (!aBrowsingContext->IsTopContent() || !sCurrentTopByBrowserId) {
173 return;
176 // Avoids an extra lookup
177 auto browserIdEntry =
178 sCurrentTopByBrowserId->Lookup(aBrowsingContext->BrowserId());
179 if (browserIdEntry && browserIdEntry.Data() == aBrowsingContext) {
180 browserIdEntry.Remove();
184 static void Register(BrowsingContext* aBrowsingContext) {
185 sBrowsingContexts->InsertOrUpdate(aBrowsingContext->Id(), aBrowsingContext);
186 if (aBrowsingContext->IsTopContent()) {
187 sCurrentTopByBrowserId->InsertOrUpdate(aBrowsingContext->BrowserId(),
188 aBrowsingContext);
191 aBrowsingContext->Group()->Register(aBrowsingContext);
194 // static
195 void BrowsingContext::UpdateCurrentTopByBrowserId(
196 BrowsingContext* aNewBrowsingContext) {
197 if (aNewBrowsingContext->IsTopContent()) {
198 sCurrentTopByBrowserId->InsertOrUpdate(aNewBrowsingContext->BrowserId(),
199 aNewBrowsingContext);
203 BrowsingContext* BrowsingContext::GetParent() const {
204 return mParentWindow ? mParentWindow->GetBrowsingContext() : nullptr;
207 bool BrowsingContext::IsInSubtreeOf(BrowsingContext* aContext) {
208 BrowsingContext* bc = this;
209 do {
210 if (bc == aContext) {
211 return true;
213 } while ((bc = bc->GetParent()));
214 return false;
217 BrowsingContext* BrowsingContext::Top() {
218 BrowsingContext* bc = this;
219 while (bc->mParentWindow) {
220 bc = bc->GetParent();
222 return bc;
225 const BrowsingContext* BrowsingContext::Top() const {
226 const BrowsingContext* bc = this;
227 while (bc->mParentWindow) {
228 bc = bc->GetParent();
230 return bc;
233 int32_t BrowsingContext::IndexOf(BrowsingContext* aChild) {
234 int32_t index = -1;
235 for (BrowsingContext* child : Children()) {
236 ++index;
237 if (child == aChild) {
238 break;
241 return index;
244 WindowContext* BrowsingContext::GetTopWindowContext() const {
245 if (mParentWindow) {
246 return mParentWindow->TopWindowContext();
248 return mCurrentWindowContext;
251 /* static */
252 void BrowsingContext::Init() {
253 if (!sBrowsingContexts) {
254 sBrowsingContexts = new BrowsingContextMap();
255 sCurrentTopByBrowserId = new BrowsingContextMap();
256 ClearOnShutdown(&sBrowsingContexts);
257 ClearOnShutdown(&sCurrentTopByBrowserId);
261 /* static */
262 LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; }
264 /* static */
265 LogModule* BrowsingContext::GetSyncLog() { return gBrowsingContextSyncLog; }
267 /* static */
268 already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) {
269 return do_AddRef(sBrowsingContexts->Get(aId));
272 /* static */
273 already_AddRefed<BrowsingContext> BrowsingContext::GetCurrentTopByBrowserId(
274 uint64_t aBrowserId) {
275 return do_AddRef(sCurrentTopByBrowserId->Get(aBrowserId));
278 /* static */
279 already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow(
280 WindowProxyHolder& aProxy) {
281 return do_AddRef(aProxy.get());
284 CanonicalBrowsingContext* BrowsingContext::Canonical() {
285 return CanonicalBrowsingContext::Cast(this);
288 bool BrowsingContext::IsOwnedByProcess() const {
289 return mIsInProcess && mDocShell &&
290 !nsDocShell::Cast(mDocShell)->WillChangeProcess();
293 bool BrowsingContext::SameOriginWithTop() {
294 MOZ_ASSERT(IsInProcess());
295 // If the top BrowsingContext is not same-process to us, it is cross-origin
296 if (!Top()->IsInProcess()) {
297 return false;
300 nsIDocShell* docShell = GetDocShell();
301 if (!docShell) {
302 return false;
304 Document* doc = docShell->GetDocument();
305 if (!doc) {
306 return false;
308 nsIPrincipal* principal = doc->NodePrincipal();
310 nsIDocShell* topDocShell = Top()->GetDocShell();
311 if (!topDocShell) {
312 return false;
314 Document* topDoc = topDocShell->GetDocument();
315 if (!topDoc) {
316 return false;
318 nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
320 return principal->Equals(topPrincipal);
323 /* static */
324 already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
325 nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
326 BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType,
327 bool aIsPopupRequested, bool aCreatedDynamically) {
328 if (aParent) {
329 MOZ_DIAGNOSTIC_ASSERT(aParent->GetWindowContext());
330 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->mType == aType);
331 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->GetBrowserId() != 0);
334 MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess());
336 uint64_t id = nsContentUtils::GenerateBrowsingContextId();
338 MOZ_LOG(GetLog(), LogLevel::Debug,
339 ("Creating 0x%08" PRIx64 " in %s", id,
340 XRE_IsParentProcess() ? "Parent" : "Child"));
342 RefPtr<BrowsingContext> parentBC =
343 aParent ? aParent->GetBrowsingContext() : nullptr;
344 RefPtr<WindowContext> parentWC =
345 aParent ? aParent->GetWindowContext() : nullptr;
346 BrowsingContext* inherit = parentBC ? parentBC.get() : aOpener;
348 // Determine which BrowsingContextGroup this context should be created in.
349 RefPtr<BrowsingContextGroup> group = aSpecificGroup;
350 if (aType == Type::Chrome) {
351 MOZ_DIAGNOSTIC_ASSERT(!group);
352 group = BrowsingContextGroup::GetChromeGroup();
353 } else if (!group) {
354 group = BrowsingContextGroup::Select(parentWC, aOpener);
357 // Configure initial values for synced fields.
358 FieldValues fields;
359 fields.Get<IDX_Name>() = aName;
361 if (aOpener) {
362 MOZ_DIAGNOSTIC_ASSERT(!aParent,
363 "new BC with both initial opener and parent");
364 MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == group);
365 MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == aType);
366 fields.Get<IDX_OpenerId>() = aOpener->Id();
367 fields.Get<IDX_HadOriginalOpener>() = true;
369 if (aType == Type::Chrome && !aParent) {
370 // See SetOpener for why we do this inheritance.
371 fields.Get<IDX_PrefersColorSchemeOverride>() =
372 aOpener->Top()->GetPrefersColorSchemeOverride();
376 if (aParent) {
377 MOZ_DIAGNOSTIC_ASSERT(parentBC->Group() == group);
378 MOZ_DIAGNOSTIC_ASSERT(parentBC->mType == aType);
379 fields.Get<IDX_EmbedderInnerWindowId>() = aParent->WindowID();
380 // Non-toplevel content documents are always embededed within content.
381 fields.Get<IDX_EmbeddedInContentDocument>() =
382 parentBC->mType == Type::Content;
384 // XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
385 // 1608448) Check if the parent was itself loading already
386 auto readystate = aParent->GetDocument()->GetReadyStateEnum();
387 fields.Get<IDX_AncestorLoading>() =
388 parentBC->GetAncestorLoading() ||
389 readystate == Document::ReadyState::READYSTATE_LOADING ||
390 readystate == Document::ReadyState::READYSTATE_INTERACTIVE;
393 fields.Get<IDX_BrowserId>() =
394 parentBC ? parentBC->GetBrowserId() : nsContentUtils::GenerateBrowserId();
396 fields.Get<IDX_OpenerPolicy>() = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
397 if (aOpener && aOpener->SameOriginWithTop()) {
398 // We inherit the opener policy if there is a creator and if the creator's
399 // origin is same origin with the creator's top-level origin.
400 // If it is cross origin we should not inherit the CrossOriginOpenerPolicy
401 fields.Get<IDX_OpenerPolicy>() = aOpener->Top()->GetOpenerPolicy();
403 // If we inherit a policy which is potentially cross-origin isolated, we
404 // must be in a potentially cross-origin isolated BCG.
405 bool isPotentiallyCrossOriginIsolated =
406 fields.Get<IDX_OpenerPolicy>() ==
407 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
408 MOZ_RELEASE_ASSERT(isPotentiallyCrossOriginIsolated ==
409 group->IsPotentiallyCrossOriginIsolated());
410 } else if (aOpener) {
411 // They are not same origin
412 auto topPolicy = aOpener->Top()->GetOpenerPolicy();
413 MOZ_RELEASE_ASSERT(topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
414 topPolicy ==
415 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS);
416 } else if (!aParent && group->IsPotentiallyCrossOriginIsolated()) {
417 // If we're creating a brand-new toplevel BC in a potentially cross-origin
418 // isolated group, it should start out with a strict opener policy.
419 fields.Get<IDX_OpenerPolicy>() =
420 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
423 fields.Get<IDX_HistoryID>() = nsID::GenerateUUID();
424 fields.Get<IDX_ExplicitActive>() = [&] {
425 if (parentBC) {
426 // Non-root browsing-contexts inherit their status from its parent.
427 return ExplicitActiveStatus::None;
429 if (aType == Type::Content) {
430 // Content gets managed by the chrome front-end / embedder element and
431 // starts as inactive.
432 return ExplicitActiveStatus::Inactive;
434 // Chrome starts as active.
435 return ExplicitActiveStatus::Active;
436 }();
438 fields.Get<IDX_FullZoom>() = parentBC ? parentBC->FullZoom() : 1.0f;
439 fields.Get<IDX_TextZoom>() = parentBC ? parentBC->TextZoom() : 1.0f;
441 bool allowContentRetargeting =
442 inherit ? inherit->GetAllowContentRetargetingOnChildren() : true;
443 fields.Get<IDX_AllowContentRetargeting>() = allowContentRetargeting;
444 fields.Get<IDX_AllowContentRetargetingOnChildren>() = allowContentRetargeting;
446 // Assume top allows fullscreen for its children unless otherwise stated.
447 // Subframes start with it false unless otherwise noted in SetEmbedderElement.
448 fields.Get<IDX_FullscreenAllowedByOwner>() = !aParent;
450 fields.Get<IDX_AllowPlugins>() = inherit ? inherit->GetAllowPlugins() : true;
452 fields.Get<IDX_DefaultLoadFlags>() =
453 inherit ? inherit->GetDefaultLoadFlags() : nsIRequest::LOAD_NORMAL;
455 fields.Get<IDX_OrientationLock>() = mozilla::hal::ScreenOrientation::None;
457 fields.Get<IDX_UseGlobalHistory>() =
458 inherit ? inherit->GetUseGlobalHistory() : false;
460 fields.Get<IDX_UseErrorPages>() = true;
462 fields.Get<IDX_TouchEventsOverrideInternal>() = TouchEventsOverride::None;
464 fields.Get<IDX_AllowJavascript>() =
465 inherit ? inherit->GetAllowJavascript() : true;
467 fields.Get<IDX_IsPopupRequested>() = aIsPopupRequested;
469 if (!parentBC) {
470 fields.Get<IDX_ShouldDelayMediaFromStart>() =
471 StaticPrefs::media_block_autoplay_until_in_foreground();
474 RefPtr<BrowsingContext> context;
475 if (XRE_IsParentProcess()) {
476 context = new CanonicalBrowsingContext(parentWC, group, id,
477 /* aOwnerProcessId */ 0,
478 /* aEmbedderProcessId */ 0, aType,
479 std::move(fields));
480 } else {
481 context =
482 new BrowsingContext(parentWC, group, id, aType, std::move(fields));
485 context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent;
486 context->mCreatedDynamically = aCreatedDynamically;
487 if (inherit) {
488 context->mPrivateBrowsingId = inherit->mPrivateBrowsingId;
489 context->mUseRemoteTabs = inherit->mUseRemoteTabs;
490 context->mUseRemoteSubframes = inherit->mUseRemoteSubframes;
491 context->mOriginAttributes = inherit->mOriginAttributes;
494 nsCOMPtr<nsIRequestContextService> rcsvc =
495 net::RequestContextService::GetOrCreate();
496 if (rcsvc) {
497 nsCOMPtr<nsIRequestContext> requestContext;
498 nsresult rv = rcsvc->NewRequestContext(getter_AddRefs(requestContext));
499 if (NS_SUCCEEDED(rv) && requestContext) {
500 context->mRequestContextId = requestContext->GetID();
504 return context.forget();
507 already_AddRefed<BrowsingContext> BrowsingContext::CreateIndependent(
508 Type aType) {
509 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
510 "BCs created in the content process must be related to "
511 "some BrowserChild");
512 RefPtr<BrowsingContext> bc(
513 CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, false));
514 bc->mWindowless = bc->IsContent();
515 bc->mEmbeddedByThisProcess = true;
516 bc->EnsureAttached();
517 return bc.forget();
520 void BrowsingContext::EnsureAttached() {
521 if (!mEverAttached) {
522 Register(this);
524 // Attach the browsing context to the tree.
525 Attach(/* aFromIPC */ false, /* aOriginProcess */ nullptr);
529 /* static */
530 mozilla::ipc::IPCResult BrowsingContext::CreateFromIPC(
531 BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
532 ContentParent* aOriginProcess) {
533 MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
534 MOZ_DIAGNOSTIC_ASSERT(aGroup);
536 uint64_t originId = 0;
537 if (aOriginProcess) {
538 originId = aOriginProcess->ChildID();
539 aGroup->EnsureHostProcess(aOriginProcess);
542 MOZ_LOG(GetLog(), LogLevel::Debug,
543 ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
544 aInit.mId, originId));
546 RefPtr<WindowContext> parent = aInit.GetParent();
548 RefPtr<BrowsingContext> context;
549 if (XRE_IsParentProcess()) {
550 // If the new BrowsingContext has a parent, it is a sub-frame embedded in
551 // whatever process sent the message. If it doesn't, and is not windowless,
552 // it is a new window or tab, and will be embedded in the parent process.
553 uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0;
554 context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
555 embedderProcessId, Type::Content,
556 std::move(aInit.mFields));
557 } else {
558 context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
559 std::move(aInit.mFields));
562 context->mWindowless = aInit.mWindowless;
563 context->mCreatedDynamically = aInit.mCreatedDynamically;
564 context->mChildOffset = aInit.mChildOffset;
565 if (context->GetHasSessionHistory()) {
566 context->CreateChildSHistory();
567 if (mozilla::SessionHistoryInParent()) {
568 context->GetChildSessionHistory()->SetIndexAndLength(
569 aInit.mSessionHistoryIndex, aInit.mSessionHistoryCount, nsID());
573 // NOTE: Call through the `Set` methods for these values to ensure that any
574 // relevant process-local state is also updated.
575 context->SetOriginAttributes(aInit.mOriginAttributes);
576 context->SetRemoteTabs(aInit.mUseRemoteTabs);
577 context->SetRemoteSubframes(aInit.mUseRemoteSubframes);
578 context->mRequestContextId = aInit.mRequestContextId;
579 // NOTE: Private browsing ID is set by `SetOriginAttributes`.
581 Register(context);
583 return context->Attach(/* aFromIPC */ true, aOriginProcess);
586 BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
587 BrowsingContextGroup* aGroup,
588 uint64_t aBrowsingContextId, Type aType,
589 FieldValues&& aInit)
590 : mFields(std::move(aInit)),
591 mType(aType),
592 mBrowsingContextId(aBrowsingContextId),
593 mGroup(aGroup),
594 mParentWindow(aParentWindow),
595 mPrivateBrowsingId(0),
596 mEverAttached(false),
597 mIsInProcess(false),
598 mIsDiscarded(false),
599 mWindowless(false),
600 mDanglingRemoteOuterProxies(false),
601 mEmbeddedByThisProcess(false),
602 mUseRemoteTabs(false),
603 mUseRemoteSubframes(false),
604 mCreatedDynamically(false),
605 mIsInBFCache(false),
606 mCanExecuteScripts(true),
607 mChildOffset(0) {
608 MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup);
609 MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
610 MOZ_RELEASE_ASSERT(mGroup);
613 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
614 // XXX(nika): We should communicate that we are now an active BrowsingContext
615 // process to the parent & do other validation here.
616 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
617 MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
618 mDocShell = aDocShell;
619 mDanglingRemoteOuterProxies = !mIsInProcess;
620 mIsInProcess = true;
621 if (mChildSessionHistory) {
622 mChildSessionHistory->SetIsInProcess(true);
625 RecomputeCanExecuteScripts();
626 ClearCachedValuesOfLocations();
629 // This class implements a callback that will return the remote window proxy for
630 // mBrowsingContext in that compartment, if it has one. It also removes the
631 // proxy from the map, because the object will be transplanted into another kind
632 // of object.
633 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
634 : public js::CompartmentTransplantCallback {
635 public:
636 explicit CompartmentRemoteProxyTransplantCallback(
637 BrowsingContext* aBrowsingContext)
638 : mBrowsingContext(aBrowsingContext) {}
640 virtual JSObject* getObjectToTransplant(
641 JS::Compartment* compartment) override {
642 auto* priv = xpc::CompartmentPrivate::Get(compartment);
643 if (!priv) {
644 return nullptr;
647 auto& map = priv->GetRemoteProxyMap();
648 auto result = map.lookup(mBrowsingContext);
649 if (!result) {
650 return nullptr;
652 JSObject* resultObject = result->value();
653 map.remove(result);
655 return resultObject;
658 private:
659 BrowsingContext* mBrowsingContext;
662 void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies(
663 JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) {
664 if (!mDanglingRemoteOuterProxies) {
665 return;
667 mDanglingRemoteOuterProxies = false;
669 CompartmentRemoteProxyTransplantCallback cb(this);
670 js::RemapRemoteWindowProxies(aCx, &cb, aOuter);
673 bool BrowsingContext::IsActive() const {
674 const BrowsingContext* current = this;
675 do {
676 auto explicit_ = current->GetExplicitActive();
677 if (explicit_ != ExplicitActiveStatus::None) {
678 return explicit_ == ExplicitActiveStatus::Active;
680 if (mParentWindow && !mParentWindow->IsCurrent()) {
681 return false;
683 } while ((current = current->GetParent()));
685 return false;
688 bool BrowsingContext::GetIsActiveBrowserWindow() {
689 if (!XRE_IsParentProcess()) {
690 return Top()->GetIsActiveBrowserWindowInternal();
693 // chrome:// urls loaded in the parent won't receive
694 // their own activation so we defer to the top chrome
695 // Browsing Context when in the parent process.
696 RefPtr<CanonicalBrowsingContext> chromeTop =
697 Canonical()->TopCrossChromeBoundary();
698 return chromeTop->GetIsActiveBrowserWindowInternal();
701 void BrowsingContext::SetIsActiveBrowserWindow(bool aActive) {
702 Unused << SetIsActiveBrowserWindowInternal(aActive);
705 bool BrowsingContext::FullscreenAllowed() const {
706 for (auto* current = this; current; current = current->GetParent()) {
707 if (!current->GetFullscreenAllowedByOwner()) {
708 return false;
711 return true;
714 static bool OwnerAllowsFullscreen(const Element& aEmbedder) {
715 if (aEmbedder.IsXULElement()) {
716 return !aEmbedder.HasAttr(nsGkAtoms::disablefullscreen);
718 if (aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) {
719 // This is controlled by feature policy.
720 return true;
722 if (const auto* embed = HTMLEmbedElement::FromNode(aEmbedder)) {
723 return embed->AllowFullscreen();
725 return false;
728 void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
729 mEmbeddedByThisProcess = true;
731 // Update embedder-element-specific fields in a shared transaction.
732 // Don't do this when clearing our embedder, as we're being destroyed either
733 // way.
734 if (aEmbedder) {
735 Transaction txn;
736 txn.SetEmbedderElementType(Some(aEmbedder->LocalName()));
737 txn.SetEmbeddedInContentDocument(
738 aEmbedder->OwnerDoc()->IsContentDocument());
739 if (nsCOMPtr<nsPIDOMWindowInner> inner =
740 do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
741 txn.SetEmbedderInnerWindowId(inner->WindowID());
743 txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder));
744 if (XRE_IsParentProcess() && IsTopContent()) {
745 nsAutoString messageManagerGroup;
746 if (aEmbedder->IsXULElement()) {
747 aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
748 if (!aEmbedder->AttrValueIs(kNameSpaceID_None,
749 nsGkAtoms::initiallyactive,
750 nsGkAtoms::_false, eIgnoreCase)) {
751 txn.SetExplicitActive(ExplicitActiveStatus::Active);
754 txn.SetMessageManagerGroup(messageManagerGroup);
756 bool useGlobalHistory =
757 !aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory);
758 txn.SetUseGlobalHistory(useGlobalHistory);
761 MOZ_ALWAYS_SUCCEEDS(txn.Commit(this));
764 if (XRE_IsParentProcess() && IsTopContent()) {
765 Canonical()->MaybeSetPermanentKey(aEmbedder);
768 mEmbedderElement = aEmbedder;
770 if (mEmbedderElement) {
771 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
772 obs->NotifyWhenScriptSafe(ToSupports(this),
773 "browsing-context-did-set-embedder", nullptr);
776 if (nsContentUtils::ShouldHideObjectOrEmbedImageDocument() &&
777 IsEmbedderTypeObjectOrEmbed()) {
778 Unused << SetSyntheticDocumentContainer(true);
783 bool BrowsingContext::IsEmbedderTypeObjectOrEmbed() {
784 if (const Maybe<nsString>& type = GetEmbedderElementType()) {
785 return nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type);
787 return false;
790 void BrowsingContext::Embed() {
791 if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
792 frame->BindToBrowsingContext(this);
796 mozilla::ipc::IPCResult BrowsingContext::Attach(bool aFromIPC,
797 ContentParent* aOriginProcess) {
798 MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
799 MOZ_DIAGNOSTIC_ASSERT_IF(aFromIPC, aOriginProcess || XRE_IsContentProcess());
800 mEverAttached = true;
802 if (MOZ_LOG_TEST(GetLog(), LogLevel::Debug)) {
803 nsAutoCString suffix;
804 mOriginAttributes.CreateSuffix(suffix);
805 MOZ_LOG(GetLog(), LogLevel::Debug,
806 ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64
807 " (private=%d, remote=%d, fission=%d, oa=%s)",
808 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
809 GetParent() ? GetParent()->Id() : 0, (int)mPrivateBrowsingId,
810 (int)mUseRemoteTabs, (int)mUseRemoteSubframes, suffix.get()));
813 MOZ_DIAGNOSTIC_ASSERT(mGroup);
814 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
816 if (mGroup->IsPotentiallyCrossOriginIsolated() !=
817 (Top()->GetOpenerPolicy() ==
818 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP)) {
819 MOZ_DIAGNOSTIC_ASSERT(aFromIPC);
820 if (aFromIPC) {
821 auto* actor = aOriginProcess
822 ? static_cast<mozilla::ipc::IProtocol*>(aOriginProcess)
823 : static_cast<mozilla::ipc::IProtocol*>(
824 ContentChild::GetSingleton());
825 return IPC_FAIL(
826 actor,
827 "Invalid CrossOriginIsolated state in BrowsingContext::Attach call");
828 } else {
829 MOZ_CRASH(
830 "Invalid CrossOriginIsolated state in BrowsingContext::Attach call");
834 AssertCoherentLoadContext();
836 // Add ourselves either to our parent or BrowsingContextGroup's child list.
837 // Important: We shouldn't return IPC_FAIL after this point, since the
838 // BrowsingContext will have already been added to the tree.
839 if (mParentWindow) {
840 if (!aFromIPC) {
841 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow->IsDiscarded(),
842 "local attach in discarded window");
843 MOZ_DIAGNOSTIC_ASSERT(!GetParent()->IsDiscarded(),
844 "local attach call in discarded bc");
845 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild(),
846 "local attach call with oop parent window");
847 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild()->CanSend(),
848 "local attach call with dead parent window");
850 mChildOffset =
851 mCreatedDynamically ? -1 : mParentWindow->Children().Length();
852 mParentWindow->AppendChildBrowsingContext(this);
853 RecomputeCanExecuteScripts();
854 } else {
855 mGroup->Toplevels().AppendElement(this);
858 if (GetIsPopupSpam()) {
859 PopupBlocker::RegisterOpenPopupSpam();
862 if (IsTop() && GetHasSessionHistory() && !mChildSessionHistory) {
863 CreateChildSHistory();
866 // Why the context is being attached. This will always be "attach" in the
867 // content process, but may be "replace" if it's known the context being
868 // replaced in the parent process.
869 const char16_t* why = u"attach";
871 if (XRE_IsContentProcess() && !aFromIPC) {
872 // Send attach to our parent if we need to.
873 ContentChild::GetSingleton()->SendCreateBrowsingContext(
874 mGroup->Id(), GetIPCInitializer());
875 } else if (XRE_IsParentProcess()) {
876 // If this window was created as a subframe by a content process, it must be
877 // being hosted within the same BrowserParent as its mParentWindow.
878 // Toplevel BrowsingContexts created by content have their BrowserParent
879 // configured during `RecvConstructPopupBrowser`.
880 if (mParentWindow && aOriginProcess) {
881 MOZ_DIAGNOSTIC_ASSERT(
882 mParentWindow->Canonical()->GetContentParent() == aOriginProcess,
883 "Creator process isn't the same as our embedder?");
884 Canonical()->SetCurrentBrowserParent(
885 mParentWindow->Canonical()->GetBrowserParent());
888 mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) {
889 MOZ_DIAGNOSTIC_ASSERT(IsContent(),
890 "chrome BCG cannot be synced to content process");
891 if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID())) {
892 Unused << aParent->SendCreateBrowsingContext(mGroup->Id(),
893 GetIPCInitializer());
897 if (IsTop() && IsContent() && Canonical()->GetWebProgress()) {
898 why = u"replace";
901 // We want to create a BrowsingContextWebProgress for all content
902 // BrowsingContexts.
903 if (IsContent() && !Canonical()->mWebProgress) {
904 Canonical()->mWebProgress = new BrowsingContextWebProgress(Canonical());
908 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
909 obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached",
910 why);
913 if (XRE_IsParentProcess()) {
914 Canonical()->CanonicalAttach();
916 return IPC_OK();
919 void BrowsingContext::Detach(bool aFromIPC) {
920 MOZ_LOG(GetLog(), LogLevel::Debug,
921 ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
922 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
923 GetParent() ? GetParent()->Id() : 0));
925 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
926 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
928 if (XRE_IsParentProcess()) {
929 Canonical()->AddPendingDiscard();
931 auto callListeners =
932 MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] {
933 for (const auto& listener : listeners) {
934 listener(id);
936 if (XRE_IsParentProcess()) {
937 Canonical()->RemovePendingDiscard();
941 nsCOMPtr<nsIRequestContextService> rcsvc =
942 net::RequestContextService::GetOrCreate();
943 if (rcsvc) {
944 rcsvc->RemoveRequestContext(GetRequestContextId());
947 // This will only ever be null if the cycle-collector has unlinked us. Don't
948 // try to detach ourselves in that case.
949 if (NS_WARN_IF(!mGroup)) {
950 MOZ_ASSERT_UNREACHABLE();
951 return;
954 if (mParentWindow) {
955 mParentWindow->RemoveChildBrowsingContext(this);
956 } else {
957 mGroup->Toplevels().RemoveElement(this);
960 if (XRE_IsParentProcess()) {
961 RefPtr<CanonicalBrowsingContext> self{Canonical()};
962 Group()->EachParent([&](ContentParent* aParent) {
963 // Only the embedder process is allowed to initiate a BrowsingContext
964 // detach, so if we've gotten here, the host process already knows we've
965 // been detached, and there's no need to tell it again.
967 // If the owner process is not the same as the embedder process, its
968 // BrowsingContext will be detached when its nsWebBrowser instance is
969 // destroyed.
970 bool doDiscard = !Canonical()->IsEmbeddedInProcess(aParent->ChildID()) &&
971 !Canonical()->IsOwnedByProcess(aParent->ChildID());
973 // Hold a strong reference to ourself, and keep our BrowsingContextGroup
974 // alive, until the responses comes back to ensure we don't die while
975 // messages relating to this context are in-flight.
977 // When the callback is called, the keepalive on our group will be
978 // destroyed, and the reference to the BrowsingContext will be dropped,
979 // which may cause it to be fully destroyed.
980 mGroup->AddKeepAlive();
981 self->AddPendingDiscard();
982 auto callback = [self](auto) {
983 self->mGroup->RemoveKeepAlive();
984 self->RemovePendingDiscard();
987 aParent->SendDiscardBrowsingContext(this, doDiscard, callback, callback);
989 } else {
990 // Hold a strong reference to ourself until the responses come back to
991 // ensure the BrowsingContext isn't cleaned up before the parent process
992 // acknowledges the discard request.
993 auto callback = [self = RefPtr{this}](auto) {};
994 ContentChild::GetSingleton()->SendDiscardBrowsingContext(
995 this, !aFromIPC, callback, callback);
998 mGroup->Unregister(this);
999 UnregisterBrowserId(this);
1000 mIsDiscarded = true;
1002 if (XRE_IsParentProcess()) {
1003 nsFocusManager* fm = nsFocusManager::GetFocusManager();
1004 if (fm) {
1005 fm->BrowsingContextDetached(this);
1009 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1010 // Why the context is being discarded. This will always be "discard" in the
1011 // content process, but may be "replace" if it's known the context being
1012 // replaced in the parent process.
1013 const char16_t* why = u"discard";
1014 if (XRE_IsParentProcess() && IsTop() && !Canonical()->GetWebProgress()) {
1015 why = u"replace";
1017 obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", why);
1020 // NOTE: Doesn't use SetClosed, as it will be set in all processes
1021 // automatically by calls to Detach()
1022 mFields.SetWithoutSyncing<IDX_Closed>(true);
1024 if (GetIsPopupSpam()) {
1025 PopupBlocker::UnregisterOpenPopupSpam();
1026 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1027 // automatically.
1028 mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1031 AssertOriginAttributesMatchPrivateBrowsing();
1033 if (XRE_IsParentProcess()) {
1034 Canonical()->CanonicalDiscard();
1038 void BrowsingContext::AddDiscardListener(
1039 std::function<void(uint64_t)>&& aListener) {
1040 if (mIsDiscarded) {
1041 aListener(Id());
1042 return;
1044 mDiscardListeners.AppendElement(std::move(aListener));
1047 void BrowsingContext::PrepareForProcessChange() {
1048 MOZ_LOG(GetLog(), LogLevel::Debug,
1049 ("%s: Preparing 0x%08" PRIx64 " for a process change",
1050 XRE_IsParentProcess() ? "Parent" : "Child", Id()));
1052 MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
1053 MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
1055 mIsInProcess = false;
1056 mUserGestureStart = TimeStamp();
1058 ClearCachedValuesOfLocations();
1060 // NOTE: For now, clear our nsDocShell reference, as we're primarily in a
1061 // different process now. This may need to change in the future with
1062 // Cross-Process BFCache.
1063 mDocShell = nullptr;
1064 if (mChildSessionHistory) {
1065 // This can be removed once session history is stored exclusively in the
1066 // parent process.
1067 mChildSessionHistory->SetIsInProcess(false);
1070 if (!mWindowProxy) {
1071 return;
1074 // We have to go through mWindowProxy rather than calling GetDOMWindow() on
1075 // mDocShell because the mDocshell reference gets cleared immediately after
1076 // the window is closed.
1077 nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy);
1078 MOZ_ASSERT(!mWindowProxy);
1081 bool BrowsingContext::IsTargetable() const {
1082 return !GetClosed() && AncestorsAreCurrent();
1085 void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
1086 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->Group() == Group());
1087 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->mType == mType);
1089 MOZ_ALWAYS_SUCCEEDS(SetOpenerId(aOpener ? aOpener->Id() : 0));
1091 if (IsChrome() && IsTop() && aOpener) {
1092 // Inherit color scheme overrides from parent window. This is to inherit the
1093 // color scheme of dark themed PBM windows in dialogs opened by such
1094 // windows.
1095 auto openerOverride = aOpener->Top()->PrefersColorSchemeOverride();
1096 if (openerOverride != PrefersColorSchemeOverride()) {
1097 MOZ_ALWAYS_SUCCEEDS(SetPrefersColorSchemeOverride(openerOverride));
1102 bool BrowsingContext::HasOpener() const {
1103 return sBrowsingContexts->Contains(GetOpenerId());
1106 bool BrowsingContext::AncestorsAreCurrent() const {
1107 const BrowsingContext* bc = this;
1108 while (true) {
1109 if (bc->IsDiscarded()) {
1110 return false;
1113 if (WindowContext* wc = bc->GetParentWindowContext()) {
1114 if (!wc->IsCurrent() || wc->IsDiscarded()) {
1115 return false;
1118 bc = wc->GetBrowsingContext();
1119 } else {
1120 return true;
1125 bool BrowsingContext::IsInBFCache() const {
1126 if (mozilla::SessionHistoryInParent()) {
1127 return mIsInBFCache;
1129 return mParentWindow &&
1130 mParentWindow->TopWindowContext()->GetWindowStateSaved();
1133 Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
1134 if (WindowContext* current = mCurrentWindowContext) {
1135 return current->Children();
1137 return Span<RefPtr<BrowsingContext>>();
1140 void BrowsingContext::GetChildren(
1141 nsTArray<RefPtr<BrowsingContext>>& aChildren) {
1142 aChildren.AppendElements(Children());
1145 Span<RefPtr<BrowsingContext>> BrowsingContext::NonSyntheticChildren() const {
1146 if (WindowContext* current = mCurrentWindowContext) {
1147 return current->NonSyntheticChildren();
1149 return Span<RefPtr<BrowsingContext>>();
1152 void BrowsingContext::GetWindowContexts(
1153 nsTArray<RefPtr<WindowContext>>& aWindows) {
1154 aWindows.AppendElements(mWindowContexts);
1157 void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
1158 MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
1159 "WindowContext already registered!");
1160 MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
1162 mWindowContexts.AppendElement(aWindow);
1164 // If the newly registered WindowContext is for our current inner window ID,
1165 // re-run the `DidSet` handler to re-establish the relationship.
1166 if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
1167 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1168 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow);
1172 void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
1173 MOZ_ASSERT(mWindowContexts.Contains(aWindow),
1174 "WindowContext not registered!");
1175 mWindowContexts.RemoveElement(aWindow);
1177 // If our currently active window was unregistered, clear our reference to it.
1178 if (aWindow == mCurrentWindowContext) {
1179 // Re-read our `CurrentInnerWindowId` value and use it to set
1180 // `mCurrentWindowContext`. As `aWindow` is now unregistered and discarded,
1181 // we won't find it, and the value will be cleared back to `nullptr`.
1182 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1183 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == nullptr);
1187 void BrowsingContext::PreOrderWalkVoid(
1188 const std::function<void(BrowsingContext*)>& aCallback) {
1189 aCallback(this);
1191 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1192 children.AppendElements(Children());
1194 for (auto& child : children) {
1195 child->PreOrderWalkVoid(aCallback);
1199 BrowsingContext::WalkFlag BrowsingContext::PreOrderWalkFlag(
1200 const std::function<WalkFlag(BrowsingContext*)>& aCallback) {
1201 switch (aCallback(this)) {
1202 case WalkFlag::Skip:
1203 return WalkFlag::Next;
1204 case WalkFlag::Stop:
1205 return WalkFlag::Stop;
1206 case WalkFlag::Next:
1207 default:
1208 break;
1211 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1212 children.AppendElements(Children());
1214 for (auto& child : children) {
1215 switch (child->PreOrderWalkFlag(aCallback)) {
1216 case WalkFlag::Stop:
1217 return WalkFlag::Stop;
1218 default:
1219 break;
1223 return WalkFlag::Next;
1226 void BrowsingContext::PostOrderWalk(
1227 const std::function<void(BrowsingContext*)>& aCallback) {
1228 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1229 children.AppendElements(Children());
1231 for (auto& child : children) {
1232 child->PostOrderWalk(aCallback);
1235 aCallback(this);
1238 void BrowsingContext::GetAllBrowsingContextsInSubtree(
1239 nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) {
1240 PreOrderWalk([&](BrowsingContext* aContext) {
1241 aBrowsingContexts.AppendElement(aContext);
1245 BrowsingContext* BrowsingContext::FindChildWithName(
1246 const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
1247 if (aName.IsEmpty()) {
1248 // You can't find a browsing context with the empty name.
1249 return nullptr;
1252 for (BrowsingContext* child : NonSyntheticChildren()) {
1253 if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) &&
1254 child->IsTargetable()) {
1255 return child;
1259 return nullptr;
1262 BrowsingContext* BrowsingContext::FindWithSpecialName(
1263 const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
1264 // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
1265 // browsing context pointed to by a special name is active. Should
1266 // it be? See Bug 1527913.
1267 if (aName.LowerCaseEqualsLiteral("_self")) {
1268 return this;
1271 if (aName.LowerCaseEqualsLiteral("_parent")) {
1272 if (BrowsingContext* parent = GetParent()) {
1273 return aRequestingWindow.CanNavigate(parent) ? parent : nullptr;
1275 return this;
1278 if (aName.LowerCaseEqualsLiteral("_top")) {
1279 BrowsingContext* top = Top();
1281 return aRequestingWindow.CanNavigate(top) ? top : nullptr;
1284 return nullptr;
1287 BrowsingContext* BrowsingContext::FindWithNameInSubtree(
1288 const nsAString& aName, WindowGlobalChild* aRequestingWindow) {
1289 MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
1291 if (NameEquals(aName) &&
1292 (!aRequestingWindow || aRequestingWindow->CanNavigate(this)) &&
1293 IsTargetable()) {
1294 return this;
1297 for (BrowsingContext* child : NonSyntheticChildren()) {
1298 if (BrowsingContext* found =
1299 child->FindWithNameInSubtree(aName, aRequestingWindow)) {
1300 return found;
1304 return nullptr;
1307 bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
1308 // If no target then not sandboxed.
1309 if (!aTarget) {
1310 return false;
1313 // We cannot be sandboxed from ourselves.
1314 if (aTarget == this) {
1315 return false;
1318 // Default the sandbox flags to our flags, so that if we can't retrieve the
1319 // active document, we will still enforce our own.
1320 uint32_t sandboxFlags = GetSandboxFlags();
1321 if (mDocShell) {
1322 if (RefPtr<Document> doc = mDocShell->GetExtantDocument()) {
1323 sandboxFlags = doc->GetSandboxFlags();
1327 // If no flags, we are not sandboxed at all.
1328 if (!sandboxFlags) {
1329 return false;
1332 // If aTarget has an ancestor, it is not top level.
1333 if (RefPtr<BrowsingContext> ancestorOfTarget = aTarget->GetParent()) {
1334 do {
1335 // We are not sandboxed if we are an ancestor of target.
1336 if (ancestorOfTarget == this) {
1337 return false;
1339 ancestorOfTarget = ancestorOfTarget->GetParent();
1340 } while (ancestorOfTarget);
1342 // Otherwise, we are sandboxed from aTarget.
1343 return true;
1346 // aTarget is top level, are we the "one permitted sandboxed
1347 // navigator", i.e. did we open aTarget?
1348 if (aTarget->GetOnePermittedSandboxedNavigatorId() == Id()) {
1349 return false;
1352 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
1353 // from our top.
1354 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) && aTarget == Top()) {
1355 return false;
1358 // If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not
1359 // sandboxed from our top if we have user interaction. We assume there is a
1360 // valid transient user gesture interaction if this check happens in the
1361 // target process given that we have checked in the triggering process
1362 // already.
1363 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) &&
1364 mCurrentWindowContext &&
1365 (!mCurrentWindowContext->IsInProcess() ||
1366 mCurrentWindowContext->HasValidTransientUserGestureActivation()) &&
1367 aTarget == Top()) {
1368 return false;
1371 // Otherwise, we are sandboxed from aTarget.
1372 return true;
1375 RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
1376 RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager;
1377 if (!manager) {
1378 manager = new SessionStorageManager(this);
1380 return manager;
1383 bool BrowsingContext::CrossOriginIsolated() {
1384 MOZ_ASSERT(NS_IsMainThread());
1386 return StaticPrefs::
1387 dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup() &&
1388 Top()->GetOpenerPolicy() ==
1389 nsILoadInfo::
1390 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
1391 XRE_IsContentProcess() &&
1392 StringBeginsWith(ContentChild::GetSingleton()->GetRemoteType(),
1393 WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
1396 void BrowsingContext::SetTriggeringAndInheritPrincipals(
1397 nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
1398 uint64_t aLoadIdentifier) {
1399 mTriggeringPrincipal = Some(
1400 PrincipalWithLoadIdentifierTuple(aTriggeringPrincipal, aLoadIdentifier));
1401 if (aPrincipalToInherit) {
1402 mPrincipalToInherit = Some(
1403 PrincipalWithLoadIdentifierTuple(aPrincipalToInherit, aLoadIdentifier));
1407 std::tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>>
1408 BrowsingContext::GetTriggeringAndInheritPrincipalsForCurrentLoad() {
1409 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
1410 GetSavedPrincipal(mTriggeringPrincipal);
1411 nsCOMPtr<nsIPrincipal> principalToInherit =
1412 GetSavedPrincipal(mPrincipalToInherit);
1413 return std::make_tuple(triggeringPrincipal, principalToInherit);
1416 nsIPrincipal* BrowsingContext::GetSavedPrincipal(
1417 Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple) {
1418 if (aPrincipalTuple) {
1419 nsCOMPtr<nsIPrincipal> principal;
1420 uint64_t loadIdentifier;
1421 std::tie(principal, loadIdentifier) = *aPrincipalTuple;
1422 // We want to return a principal only if the load identifier for it
1423 // matches the current one for this BC.
1424 if (auto current = GetCurrentLoadIdentifier();
1425 current && *current == loadIdentifier) {
1426 return principal;
1429 return nullptr;
1432 BrowsingContext::~BrowsingContext() {
1433 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow ||
1434 !mParentWindow->mChildren.Contains(this));
1435 MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
1437 mDeprioritizedLoadRunner.clear();
1439 if (sBrowsingContexts) {
1440 sBrowsingContexts->Remove(Id());
1442 UnregisterBrowserId(this);
1444 ClearCachedValuesOfLocations();
1445 mLocations.clear();
1448 /* static */
1449 void BrowsingContext::DiscardFromContentParent(ContentParent* aCP) {
1450 MOZ_ASSERT(XRE_IsParentProcess());
1452 if (sBrowsingContexts) {
1453 AutoTArray<RefPtr<BrowsingContext>, 8> toDiscard;
1454 for (const auto& data : sBrowsingContexts->Values()) {
1455 auto* bc = data->Canonical();
1456 if (!bc->IsDiscarded() && bc->IsEmbeddedInProcess(aCP->ChildID())) {
1457 toDiscard.AppendElement(bc);
1461 for (BrowsingContext* bc : toDiscard) {
1462 bc->Detach(/* aFromIPC */ true);
1467 nsISupports* BrowsingContext::GetParentObject() const {
1468 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1471 JSObject* BrowsingContext::WrapObject(JSContext* aCx,
1472 JS::Handle<JSObject*> aGivenProto) {
1473 return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
1476 bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
1477 JSStructuredCloneWriter* aWriter,
1478 StructuredCloneHolder* aHolder) {
1479 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
1480 return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
1481 JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
1484 /* static */
1485 JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
1486 JSStructuredCloneReader* aReader,
1487 StructuredCloneHolder* aHolder) {
1488 uint32_t idLow = 0;
1489 uint32_t idHigh = 0;
1490 if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
1491 return nullptr;
1493 uint64_t id = uint64_t(idHigh) << 32 | idLow;
1495 // Note: Do this check after reading our ID data. Returning null will abort
1496 // the decode operation anyway, but we should at least be as safe as possible.
1497 if (NS_WARN_IF(!NS_IsMainThread())) {
1498 MOZ_DIAGNOSTIC_ASSERT(false,
1499 "We shouldn't be trying to decode a BrowsingContext "
1500 "on a background thread.");
1501 return nullptr;
1504 JS::Rooted<JS::Value> val(aCx, JS::NullValue());
1505 // We'll get rooting hazard errors from the RefPtr destructor if it isn't
1506 // destroyed before we try to return a raw JSObject*, so create it in its own
1507 // scope.
1508 if (RefPtr<BrowsingContext> context = Get(id)) {
1509 if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
1510 return nullptr;
1513 return val.toObjectOrNull();
1516 bool BrowsingContext::CanSetOriginAttributes() {
1517 // A discarded BrowsingContext has already been destroyed, and cannot modify
1518 // its OriginAttributes.
1519 if (NS_WARN_IF(IsDiscarded())) {
1520 return false;
1523 // Before attaching is the safest time to set OriginAttributes, and the only
1524 // allowed time for content BrowsingContexts.
1525 if (!EverAttached()) {
1526 return true;
1529 // Attached content BrowsingContexts may have been synced to other processes.
1530 if (NS_WARN_IF(IsContent())) {
1531 MOZ_CRASH();
1532 return false;
1534 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
1536 // Cannot set OriginAttributes after we've created our child BrowsingContext.
1537 if (NS_WARN_IF(!Children().IsEmpty())) {
1538 return false;
1541 // Only allow setting OriginAttributes if we have no associated document, or
1542 // the document is still `about:blank`.
1543 // TODO: Bug 1273058 - should have no document when setting origin attributes.
1544 if (WindowGlobalParent* window = Canonical()->GetCurrentWindowGlobal()) {
1545 if (nsIURI* uri = window->GetDocumentURI()) {
1546 MOZ_ASSERT(NS_IsAboutBlank(uri));
1547 return NS_IsAboutBlank(uri);
1550 return true;
1553 Nullable<WindowProxyHolder> BrowsingContext::GetAssociatedWindow() {
1554 // nsILoadContext usually only returns same-process windows,
1555 // so we intentionally return nullptr if this BC is out of
1556 // process.
1557 if (IsInProcess()) {
1558 return WindowProxyHolder(this);
1560 return nullptr;
1563 Nullable<WindowProxyHolder> BrowsingContext::GetTopWindow() {
1564 return Top()->GetAssociatedWindow();
1567 Element* BrowsingContext::GetTopFrameElement() {
1568 return Top()->GetEmbedderElement();
1571 void BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing,
1572 ErrorResult& aError) {
1573 nsresult rv = SetUsePrivateBrowsing(aUsePrivateBrowsing);
1574 if (NS_FAILED(rv)) {
1575 aError.Throw(rv);
1579 void BrowsingContext::SetUseTrackingProtectionWebIDL(
1580 bool aUseTrackingProtection, ErrorResult& aRv) {
1581 SetForceEnableTrackingProtection(aUseTrackingProtection, aRv);
1584 void BrowsingContext::GetOriginAttributes(JSContext* aCx,
1585 JS::MutableHandle<JS::Value> aVal,
1586 ErrorResult& aError) {
1587 AssertOriginAttributesMatchPrivateBrowsing();
1589 if (!ToJSValue(aCx, mOriginAttributes, aVal)) {
1590 aError.NoteJSContextException(aCx);
1594 NS_IMETHODIMP BrowsingContext::GetAssociatedWindow(
1595 mozIDOMWindowProxy** aAssociatedWindow) {
1596 nsCOMPtr<mozIDOMWindowProxy> win = GetDOMWindow();
1597 win.forget(aAssociatedWindow);
1598 return NS_OK;
1601 NS_IMETHODIMP BrowsingContext::GetTopWindow(mozIDOMWindowProxy** aTopWindow) {
1602 return Top()->GetAssociatedWindow(aTopWindow);
1605 NS_IMETHODIMP BrowsingContext::GetTopFrameElement(Element** aTopFrameElement) {
1606 RefPtr<Element> topFrameElement = GetTopFrameElement();
1607 topFrameElement.forget(aTopFrameElement);
1608 return NS_OK;
1611 NS_IMETHODIMP BrowsingContext::GetIsContent(bool* aIsContent) {
1612 *aIsContent = IsContent();
1613 return NS_OK;
1616 NS_IMETHODIMP BrowsingContext::GetUsePrivateBrowsing(
1617 bool* aUsePrivateBrowsing) {
1618 *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
1619 return NS_OK;
1622 NS_IMETHODIMP BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1623 if (!CanSetOriginAttributes()) {
1624 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1625 if (changed) {
1626 NS_WARNING("SetUsePrivateBrowsing when !CanSetOriginAttributes()");
1628 return changed ? NS_ERROR_FAILURE : NS_OK;
1631 return SetPrivateBrowsing(aUsePrivateBrowsing);
1634 NS_IMETHODIMP BrowsingContext::SetPrivateBrowsing(bool aPrivateBrowsing) {
1635 if (!CanSetOriginAttributes()) {
1636 NS_WARNING("Attempt to set PrivateBrowsing when !CanSetOriginAttributes");
1637 return NS_ERROR_FAILURE;
1640 bool changed = aPrivateBrowsing != (mPrivateBrowsingId > 0);
1641 if (changed) {
1642 mPrivateBrowsingId = aPrivateBrowsing ? 1 : 0;
1643 if (IsContent()) {
1644 mOriginAttributes.SyncAttributesWithPrivateBrowsing(aPrivateBrowsing);
1647 if (XRE_IsParentProcess()) {
1648 Canonical()->AdjustPrivateBrowsingCount(aPrivateBrowsing);
1651 AssertOriginAttributesMatchPrivateBrowsing();
1653 if (changed && mDocShell) {
1654 nsDocShell::Cast(mDocShell)->NotifyPrivateBrowsingChanged();
1656 return NS_OK;
1659 NS_IMETHODIMP BrowsingContext::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1660 *aUseRemoteTabs = mUseRemoteTabs;
1661 return NS_OK;
1664 NS_IMETHODIMP BrowsingContext::SetRemoteTabs(bool aUseRemoteTabs) {
1665 if (!CanSetOriginAttributes()) {
1666 NS_WARNING("Attempt to set RemoteTabs when !CanSetOriginAttributes");
1667 return NS_ERROR_FAILURE;
1670 static bool annotated = false;
1671 if (aUseRemoteTabs && !annotated) {
1672 annotated = true;
1673 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled,
1674 true);
1677 // Don't allow non-remote tabs with remote subframes.
1678 if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
1679 return NS_ERROR_UNEXPECTED;
1682 mUseRemoteTabs = aUseRemoteTabs;
1683 return NS_OK;
1686 NS_IMETHODIMP BrowsingContext::GetUseRemoteSubframes(
1687 bool* aUseRemoteSubframes) {
1688 *aUseRemoteSubframes = mUseRemoteSubframes;
1689 return NS_OK;
1692 NS_IMETHODIMP BrowsingContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
1693 if (!CanSetOriginAttributes()) {
1694 NS_WARNING("Attempt to set RemoteSubframes when !CanSetOriginAttributes");
1695 return NS_ERROR_FAILURE;
1698 static bool annotated = false;
1699 if (aUseRemoteSubframes && !annotated) {
1700 annotated = true;
1701 CrashReporter::AnnotateCrashReport(
1702 CrashReporter::Annotation::DOMFissionEnabled, true);
1705 // Don't allow non-remote tabs with remote subframes.
1706 if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
1707 return NS_ERROR_UNEXPECTED;
1710 mUseRemoteSubframes = aUseRemoteSubframes;
1711 return NS_OK;
1714 NS_IMETHODIMP BrowsingContext::GetUseTrackingProtection(
1715 bool* aUseTrackingProtection) {
1716 *aUseTrackingProtection = false;
1718 if (GetForceEnableTrackingProtection() ||
1719 StaticPrefs::privacy_trackingprotection_enabled() ||
1720 (UsePrivateBrowsing() &&
1721 StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
1722 *aUseTrackingProtection = true;
1723 return NS_OK;
1726 if (GetParent()) {
1727 return GetParent()->GetUseTrackingProtection(aUseTrackingProtection);
1730 return NS_OK;
1733 NS_IMETHODIMP BrowsingContext::SetUseTrackingProtection(
1734 bool aUseTrackingProtection) {
1735 return SetForceEnableTrackingProtection(aUseTrackingProtection);
1738 NS_IMETHODIMP BrowsingContext::GetScriptableOriginAttributes(
1739 JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
1740 AssertOriginAttributesMatchPrivateBrowsing();
1742 bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
1743 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1744 return NS_OK;
1747 NS_IMETHODIMP_(void)
1748 BrowsingContext::GetOriginAttributes(OriginAttributes& aAttrs) {
1749 aAttrs = mOriginAttributes;
1750 AssertOriginAttributesMatchPrivateBrowsing();
1753 nsresult BrowsingContext::SetOriginAttributes(const OriginAttributes& aAttrs) {
1754 if (!CanSetOriginAttributes()) {
1755 NS_WARNING("Attempt to set OriginAttributes when !CanSetOriginAttributes");
1756 return NS_ERROR_FAILURE;
1759 AssertOriginAttributesMatchPrivateBrowsing();
1760 mOriginAttributes = aAttrs;
1762 bool isPrivate = mOriginAttributes.mPrivateBrowsingId !=
1763 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1764 // Chrome Browsing Context can not contain OriginAttributes.mPrivateBrowsingId
1765 if (IsChrome() && isPrivate) {
1766 mOriginAttributes.mPrivateBrowsingId =
1767 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1769 SetPrivateBrowsing(isPrivate);
1770 AssertOriginAttributesMatchPrivateBrowsing();
1772 return NS_OK;
1775 void BrowsingContext::AssertCoherentLoadContext() {
1776 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1777 // LoadContext should generally match our opener or parent.
1778 if (IsContent()) {
1779 if (RefPtr<BrowsingContext> opener = GetOpener()) {
1780 MOZ_DIAGNOSTIC_ASSERT(opener->mType == mType);
1781 MOZ_DIAGNOSTIC_ASSERT(opener->mGroup == mGroup);
1782 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteTabs == mUseRemoteTabs);
1783 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteSubframes == mUseRemoteSubframes);
1784 MOZ_DIAGNOSTIC_ASSERT(opener->mPrivateBrowsingId == mPrivateBrowsingId);
1785 MOZ_DIAGNOSTIC_ASSERT(
1786 opener->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1789 if (RefPtr<BrowsingContext> parent = GetParent()) {
1790 MOZ_DIAGNOSTIC_ASSERT(parent->mType == mType);
1791 MOZ_DIAGNOSTIC_ASSERT(parent->mGroup == mGroup);
1792 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteTabs == mUseRemoteTabs);
1793 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteSubframes == mUseRemoteSubframes);
1794 MOZ_DIAGNOSTIC_ASSERT(parent->mPrivateBrowsingId == mPrivateBrowsingId);
1795 MOZ_DIAGNOSTIC_ASSERT(
1796 parent->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1799 // UseRemoteSubframes and UseRemoteTabs must match.
1800 MOZ_DIAGNOSTIC_ASSERT(
1801 !mUseRemoteSubframes || mUseRemoteTabs,
1802 "Cannot set useRemoteSubframes without also setting useRemoteTabs");
1804 // Double-check OriginAttributes/Private Browsing
1805 AssertOriginAttributesMatchPrivateBrowsing();
1806 #endif
1809 void BrowsingContext::AssertOriginAttributesMatchPrivateBrowsing() {
1810 // Chrome browsing contexts must not have a private browsing OriginAttribute
1811 // Content browsing contexts must maintain the equality:
1812 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
1813 if (IsChrome()) {
1814 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
1815 } else {
1816 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
1817 mPrivateBrowsingId);
1821 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext)
1822 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1823 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
1824 NS_INTERFACE_MAP_ENTRY(nsISupports)
1825 NS_INTERFACE_MAP_END
1827 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
1829 NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext)
1830 NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext)
1832 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext)
1833 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1834 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1836 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
1837 if (sBrowsingContexts) {
1838 sBrowsingContexts->Remove(tmp->Id());
1840 UnregisterBrowserId(tmp);
1842 if (tmp->GetIsPopupSpam()) {
1843 PopupBlocker::UnregisterOpenPopupSpam();
1844 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1845 // automatically.
1846 tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1849 NS_IMPL_CYCLE_COLLECTION_UNLINK(
1850 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1851 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1852 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1853 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1855 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
1856 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
1857 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1858 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1859 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1861 static bool IsCertainlyAliveForCC(BrowsingContext* aContext) {
1862 return aContext->HasKnownLiveWrapper() ||
1863 (AppShutdown::GetCurrentShutdownPhase() ==
1864 ShutdownPhase::NotInShutdown &&
1865 aContext->EverAttached() && !aContext->IsDiscarded());
1868 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(BrowsingContext)
1869 if (IsCertainlyAliveForCC(tmp)) {
1870 if (tmp->PreservingWrapper()) {
1871 tmp->MarkWrapperLive();
1873 return true;
1875 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1877 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(BrowsingContext)
1878 return IsCertainlyAliveForCC(tmp) && tmp->HasNothingToTrace(tmp);
1879 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1881 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
1882 return IsCertainlyAliveForCC(tmp);
1883 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1885 class RemoteLocationProxy
1886 : public RemoteObjectProxy<BrowsingContext::LocationProxy,
1887 Location_Binding::sCrossOriginProperties> {
1888 public:
1889 typedef RemoteObjectProxy Base;
1891 constexpr RemoteLocationProxy()
1892 : RemoteObjectProxy(prototypes::id::Location) {}
1894 void NoteChildren(JSObject* aProxy,
1895 nsCycleCollectionTraversalCallback& aCb) const override {
1896 auto location =
1897 static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
1898 CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
1899 "JS::GetPrivate(obj)->GetBrowsingContext()");
1903 static const RemoteLocationProxy sSingleton;
1905 // Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
1906 // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
1907 // malloc.
1908 template <>
1909 const JSClass RemoteLocationProxy::Base::sClass =
1910 PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
1912 void BrowsingContext::Location(JSContext* aCx,
1913 JS::MutableHandle<JSObject*> aLocation,
1914 ErrorResult& aError) {
1915 aError.MightThrowJSException();
1916 sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
1917 aLocation);
1918 if (!aLocation) {
1919 aError.StealExceptionFromJSContext(aCx);
1923 bool BrowsingContext::RemoveRootFromBFCacheSync() {
1924 if (WindowContext* wc = GetParentWindowContext()) {
1925 if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) {
1926 return doc->RemoveFromBFCacheSync();
1929 return false;
1932 nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) {
1933 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1934 if (sourceBC.IsNull()) {
1935 return NS_OK;
1938 // We might be called after the source BC has been discarded, but before we've
1939 // destroyed our in-process instance of the BrowsingContext object in some
1940 // situations (e.g. after creating a new pop-up with window.open while the
1941 // window is being closed). In these situations we want to still perform the
1942 // sandboxing check against our in-process copy. If we've forgotten about the
1943 // context already, assume it is sanboxed. (bug 1643450)
1944 BrowsingContext* bc = sourceBC.GetMaybeDiscarded();
1945 if (!bc || bc->IsSandboxedFrom(this)) {
1946 return NS_ERROR_DOM_SECURITY_ERR;
1948 return NS_OK;
1951 nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
1952 bool aSetNavigating) {
1953 // Per spec, most load attempts are silently ignored when a BrowsingContext is
1954 // null (which in our code corresponds to discarded), so we simply fail
1955 // silently in those cases. Regardless, we cannot trigger loads in/from
1956 // discarded BrowsingContexts via IPC, so we need to abort in any case.
1957 if (IsDiscarded()) {
1958 return NS_OK;
1961 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
1962 "Targeting occurs in InternalLoad");
1963 aLoadState->AssertProcessCouldTriggerLoadIfSystem();
1965 if (mDocShell) {
1966 nsCOMPtr<nsIDocShell> docShell = mDocShell;
1967 return docShell->LoadURI(aLoadState, aSetNavigating);
1970 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
1971 // document-specific sandbox flags are only available in the process
1972 // triggering the load, and we don't want the target process to have to trust
1973 // the triggering process to do the appropriate checks for the
1974 // BrowsingContext's sandbox flags.
1975 MOZ_TRY(CheckSandboxFlags(aLoadState));
1976 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
1977 aLoadState->PrincipalToInherit(),
1978 aLoadState->GetLoadIdentifier());
1980 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1982 if (net::SchemeIsJavascript(aLoadState->URI())) {
1983 if (!XRE_IsParentProcess()) {
1984 // Web content should only be able to load javascript: URIs into documents
1985 // whose principals the caller principal subsumes, which by definition
1986 // excludes any document in a cross-process BrowsingContext.
1987 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
1989 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
1990 "Should never see a cross-process javascript: load "
1991 "triggered from content");
1994 MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
1995 if (sourceBC && sourceBC->IsInProcess()) {
1996 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
1997 if (WindowGlobalChild* wgc =
1998 win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
1999 if (!wgc->CanNavigate(this)) {
2000 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2002 wgc->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating);
2004 } else if (XRE_IsParentProcess()) {
2005 if (Canonical()->LoadInParent(aLoadState, aSetNavigating)) {
2006 return NS_OK;
2009 if (ContentParent* cp = Canonical()->GetContentParent()) {
2010 // Attempt to initiate this load immediately in the parent, if it succeeds
2011 // it'll return a unique identifier so that we can find it later.
2012 uint64_t loadIdentifier = 0;
2013 if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) {
2014 MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome());
2015 loadIdentifier = GetCurrentLoadIdentifier().value();
2016 aLoadState->SetChannelInitialized(true);
2019 cp->TransmitBlobDataIfBlobURL(aLoadState->URI());
2021 // Setup a confirmation callback once the content process receives this
2022 // load. Normally we'd expect a PDocumentChannel actor to have been
2023 // created to claim the load identifier by that time. If not, then it
2024 // won't be coming, so make sure we clean up and deregister.
2025 cp->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating)
2026 ->Then(GetMainThreadSerialEventTarget(), __func__,
2027 [loadIdentifier](
2028 const PContentParent::LoadURIPromise::ResolveOrRejectValue&
2029 aValue) {
2030 if (loadIdentifier) {
2031 net::DocumentLoadListener::CleanupParentLoadAttempt(
2032 loadIdentifier);
2036 } else {
2037 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2038 if (!sourceBC) {
2039 return NS_ERROR_UNEXPECTED;
2041 // If we're in a content process and the source BC is no longer in-process,
2042 // just fail silently.
2044 return NS_OK;
2047 nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
2048 if (IsDiscarded()) {
2049 return NS_OK;
2051 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
2052 aLoadState->PrincipalToInherit(),
2053 aLoadState->GetLoadIdentifier());
2055 MOZ_DIAGNOSTIC_ASSERT(aLoadState->Target().IsEmpty(),
2056 "should already have retargeted");
2057 MOZ_DIAGNOSTIC_ASSERT(!aLoadState->TargetBrowsingContext().IsNull(),
2058 "should have target bc set");
2059 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext() == this,
2060 "must be targeting this BrowsingContext");
2061 aLoadState->AssertProcessCouldTriggerLoadIfSystem();
2063 if (mDocShell) {
2064 RefPtr<nsDocShell> docShell = nsDocShell::Cast(mDocShell);
2065 return docShell->InternalLoad(aLoadState);
2068 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
2069 // document-specific sandbox flags are only available in the process
2070 // triggering the load, and we don't want the target process to have to trust
2071 // the triggering process to do the appropriate checks for the
2072 // BrowsingContext's sandbox flags.
2073 MOZ_TRY(CheckSandboxFlags(aLoadState));
2075 const auto& sourceBC = aLoadState->SourceBrowsingContext();
2077 if (net::SchemeIsJavascript(aLoadState->URI())) {
2078 if (!XRE_IsParentProcess()) {
2079 // Web content should only be able to load javascript: URIs into documents
2080 // whose principals the caller principal subsumes, which by definition
2081 // excludes any document in a cross-process BrowsingContext.
2082 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
2084 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
2085 "Should never see a cross-process javascript: load "
2086 "triggered from content");
2089 if (XRE_IsParentProcess()) {
2090 ContentParent* cp = Canonical()->GetContentParent();
2091 if (!cp || !cp->CanSend()) {
2092 return NS_ERROR_FAILURE;
2095 MOZ_ALWAYS_SUCCEEDS(
2096 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2097 Unused << cp->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
2098 } else {
2099 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2100 MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
2102 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
2103 WindowGlobalChild* wgc =
2104 win->GetCurrentInnerWindow()->GetWindowGlobalChild();
2105 if (!wgc || !wgc->CanSend()) {
2106 return NS_ERROR_FAILURE;
2108 if (!wgc->CanNavigate(this)) {
2109 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2112 MOZ_ALWAYS_SUCCEEDS(
2113 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2114 wgc->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
2117 return NS_OK;
2120 void BrowsingContext::DisplayLoadError(const nsAString& aURI) {
2121 MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError"));
2122 MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
2123 MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess());
2125 if (mDocShell) {
2126 bool didDisplayLoadError = false;
2127 nsCOMPtr<nsIDocShell> docShell = mDocShell;
2128 docShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr,
2129 PromiseFlatString(aURI).get(), nullptr,
2130 &didDisplayLoadError);
2131 } else {
2132 if (ContentParent* cp = Canonical()->GetContentParent()) {
2133 Unused << cp->SendDisplayLoadError(this, PromiseFlatString(aURI));
2138 WindowProxyHolder BrowsingContext::Window() {
2139 return WindowProxyHolder(Self());
2142 WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) {
2143 return Window();
2146 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
2147 if (mIsDiscarded) {
2148 return;
2151 if (IsSubframe()) {
2152 // .close() on frames is a no-op.
2153 return;
2156 if (GetDOMWindow()) {
2157 nsGlobalWindowOuter::Cast(GetDOMWindow())
2158 ->CloseOuter(aCallerType == CallerType::System);
2159 return;
2162 // This is a bit of a hack for webcompat. Content needs to see an updated
2163 // |window.closed| value as early as possible, so we set this before we
2164 // actually send the DOMWindowClose event, which happens in the process where
2165 // the document for this browsing context is loaded.
2166 MOZ_ALWAYS_SUCCEEDS(SetClosed(true));
2168 if (ContentChild* cc = ContentChild::GetSingleton()) {
2169 cc->SendWindowClose(this, aCallerType == CallerType::System);
2170 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2171 Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
2176 * Examine the current document state to see if we're in a way that is
2177 * typically abused by web designers. The window.open code uses this
2178 * routine to determine whether to allow the new window.
2179 * Returns a value from the PopupControlState enum.
2181 PopupBlocker::PopupControlState BrowsingContext::RevisePopupAbuseLevel(
2182 PopupBlocker::PopupControlState aControl) {
2183 if (!IsContent()) {
2184 return PopupBlocker::openAllowed;
2187 RefPtr<Document> doc = GetExtantDocument();
2188 PopupBlocker::PopupControlState abuse = aControl;
2189 switch (abuse) {
2190 case PopupBlocker::openControlled:
2191 case PopupBlocker::openBlocked:
2192 case PopupBlocker::openOverridden:
2193 if (IsPopupAllowed()) {
2194 abuse = PopupBlocker::PopupControlState(abuse - 1);
2196 break;
2197 case PopupBlocker::openAbused:
2198 if (IsPopupAllowed() ||
2199 (doc && doc->HasValidTransientUserGestureActivation())) {
2200 // Skip PopupBlocker::openBlocked
2201 abuse = PopupBlocker::openControlled;
2203 break;
2204 case PopupBlocker::openAllowed:
2205 break;
2206 default:
2207 NS_WARNING("Strange PopupControlState!");
2210 // limit the number of simultaneously open popups
2211 if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked ||
2212 abuse == PopupBlocker::openControlled) {
2213 int32_t popupMax = StaticPrefs::dom_popup_maximum();
2214 if (popupMax >= 0 &&
2215 PopupBlocker::GetOpenPopupSpamCount() >= (uint32_t)popupMax) {
2216 abuse = PopupBlocker::openOverridden;
2220 // If we're currently in-process, attempt to consume transient user gesture
2221 // activations.
2222 if (doc) {
2223 // HACK: Some pages using bogus library + UA sniffing call window.open()
2224 // from a blank iframe, only on Firefox, see bug 1685056.
2226 // This is a hack-around to preserve behavior in that particular and
2227 // specific case, by consuming activation on the parent document, so we
2228 // don't care about the InProcessParent bits not being fission-safe or what
2229 // not.
2230 auto ConsumeTransientUserActivationForMultiplePopupBlocking =
2231 [&]() -> bool {
2232 if (doc->ConsumeTransientUserGestureActivation()) {
2233 return true;
2235 if (!doc->IsInitialDocument()) {
2236 return false;
2238 Document* parentDoc = doc->GetInProcessParentDocument();
2239 if (!parentDoc ||
2240 !parentDoc->NodePrincipal()->Equals(doc->NodePrincipal())) {
2241 return false;
2243 return parentDoc->ConsumeTransientUserGestureActivation();
2246 // If this popup is allowed, let's block any other for this event, forcing
2247 // PopupBlocker::openBlocked state.
2248 if ((abuse == PopupBlocker::openAllowed ||
2249 abuse == PopupBlocker::openControlled) &&
2250 StaticPrefs::dom_block_multiple_popups() && !IsPopupAllowed() &&
2251 !ConsumeTransientUserActivationForMultiplePopupBlocking()) {
2252 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
2253 doc, nsContentUtils::eDOM_PROPERTIES,
2254 "MultiplePopupsBlockedNoUserActivation");
2255 abuse = PopupBlocker::openBlocked;
2259 return abuse;
2262 void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
2263 Unused << SetHistoryEntryCount(GetHistoryEntryCount() + 1);
2266 std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) {
2267 nsFocusManager* fm = nsFocusManager::GetFocusManager();
2268 if (!fm) {
2269 return {false, false};
2272 nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
2273 BrowsingContext* callerBC = caller ? caller->GetBrowsingContext() : nullptr;
2274 RefPtr<BrowsingContext> openerBC = GetOpener();
2275 MOZ_DIAGNOSTIC_ASSERT(!openerBC || openerBC->Group() == Group());
2277 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
2278 // window which opened us to raise us at times when popups are allowed
2279 // (bugs 355482 and 369306).
2280 bool canFocus = aCallerType == CallerType::System ||
2281 !Preferences::GetBool("dom.disable_window_flip", true);
2282 if (!canFocus && openerBC == callerBC) {
2283 canFocus =
2284 (callerBC ? callerBC : this)
2285 ->RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
2286 PopupBlocker::openBlocked;
2289 bool isActive = false;
2290 if (XRE_IsParentProcess()) {
2291 RefPtr<CanonicalBrowsingContext> chromeTop =
2292 Canonical()->TopCrossChromeBoundary();
2293 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = fm->GetActiveWindow();
2294 isActive = (activeWindow == chromeTop->GetDOMWindow());
2295 } else {
2296 isActive = (fm->GetActiveBrowsingContext() == Top());
2299 return {canFocus, isActive};
2302 void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
2303 // These checks need to happen before the RequestFrameFocus call, which
2304 // is why they are done in an untrusted process. If we wanted to enforce
2305 // these in the parent, we'd need to do the checks there _also_.
2306 // These should be kept in sync with nsGlobalWindowOuter::FocusOuter.
2308 auto [canFocus, isActive] = CanFocusCheck(aCallerType);
2310 if (!(canFocus || isActive)) {
2311 return;
2314 // Permission check passed
2316 if (mEmbedderElement) {
2317 // Make the activeElement in this process update synchronously.
2318 nsContentUtils::RequestFrameFocus(*mEmbedderElement, true, aCallerType);
2320 uint64_t actionId = nsFocusManager::GenerateFocusActionId();
2321 if (ContentChild* cc = ContentChild::GetSingleton()) {
2322 cc->SendWindowFocus(this, aCallerType, actionId);
2323 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2324 Unused << cp->SendWindowFocus(this, aCallerType, actionId);
2328 bool BrowsingContext::CanBlurCheck(CallerType aCallerType) {
2329 // If dom.disable_window_flip == true, then content should not be allowed
2330 // to do blur (this would allow popunders, bug 369306)
2331 return aCallerType == CallerType::System ||
2332 !Preferences::GetBool("dom.disable_window_flip", true);
2335 void BrowsingContext::Blur(CallerType aCallerType, ErrorResult& aError) {
2336 if (!CanBlurCheck(aCallerType)) {
2337 return;
2340 if (ContentChild* cc = ContentChild::GetSingleton()) {
2341 cc->SendWindowBlur(this, aCallerType);
2342 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2343 Unused << cp->SendWindowBlur(this, aCallerType);
2347 Nullable<WindowProxyHolder> BrowsingContext::GetWindow() {
2348 if (XRE_IsParentProcess() && !IsInProcess()) {
2349 return nullptr;
2351 return WindowProxyHolder(this);
2354 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
2355 if (mIsDiscarded) {
2356 return nullptr;
2359 // We never return null or throw an error, but the implementation in
2360 // nsGlobalWindow does and we need to use the same signature.
2361 return WindowProxyHolder(Top());
2364 void BrowsingContext::GetOpener(JSContext* aCx,
2365 JS::MutableHandle<JS::Value> aOpener,
2366 ErrorResult& aError) const {
2367 RefPtr<BrowsingContext> opener = GetOpener();
2368 if (!opener) {
2369 aOpener.setNull();
2370 return;
2373 if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
2374 aError.NoteJSContextException(aCx);
2378 // We never throw an error, but the implementation in nsGlobalWindow does and
2379 // we need to use the same signature.
2380 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
2381 if (mIsDiscarded) {
2382 return nullptr;
2385 if (GetParent()) {
2386 return WindowProxyHolder(GetParent());
2388 return WindowProxyHolder(this);
2391 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2392 JS::Handle<JS::Value> aMessage,
2393 const nsAString& aTargetOrigin,
2394 const Sequence<JSObject*>& aTransfer,
2395 nsIPrincipal& aSubjectPrincipal,
2396 ErrorResult& aError) {
2397 if (mIsDiscarded) {
2398 return;
2401 RefPtr<BrowsingContext> sourceBc;
2402 PostMessageData data;
2403 data.targetOrigin() = aTargetOrigin;
2404 data.subjectPrincipal() = &aSubjectPrincipal;
2405 RefPtr<nsGlobalWindowInner> callerInnerWindow;
2406 nsAutoCString scriptLocation;
2407 // We don't need to get the caller's agentClusterId since that is used for
2408 // checking whether it's okay to sharing memory (and it's not allowed to share
2409 // memory cross processes)
2410 if (!nsGlobalWindowOuter::GatherPostMessageData(
2411 aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
2412 getter_AddRefs(data.targetOriginURI()),
2413 getter_AddRefs(data.callerPrincipal()),
2414 getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()),
2415 /* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) {
2416 return;
2418 if (sourceBc && sourceBc->IsDiscarded()) {
2419 return;
2421 data.source() = sourceBc;
2422 data.isFromPrivateWindow() =
2423 callerInnerWindow &&
2424 nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
2425 data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0;
2426 data.scriptLocation() = scriptLocation;
2427 JS::Rooted<JS::Value> transferArray(aCx);
2428 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
2429 &transferArray);
2430 if (NS_WARN_IF(aError.Failed())) {
2431 return;
2434 JS::CloneDataPolicy clonePolicy;
2435 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
2436 clonePolicy.allowSharedMemoryObjects();
2439 // We will see if the message is required to be in the same process or it can
2440 // be in the different process after Write().
2441 ipc::StructuredCloneData message = ipc::StructuredCloneData(
2442 StructuredCloneHolder::StructuredCloneScope::UnknownDestination,
2443 StructuredCloneHolder::TransferringSupported);
2444 message.Write(aCx, aMessage, transferArray, clonePolicy, aError);
2445 if (NS_WARN_IF(aError.Failed())) {
2446 return;
2449 ClonedOrErrorMessageData messageData;
2450 if (ContentChild* cc = ContentChild::GetSingleton()) {
2451 // The clone scope gets set when we write the message data based on the
2452 // requirements of that data that we're writing.
2453 // If the message data contains a shared memory object, then CloneScope
2454 // would return SameProcess. Otherwise, it returns DifferentProcess.
2455 if (message.CloneScope() ==
2456 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2457 ClonedMessageData clonedMessageData;
2458 if (!message.BuildClonedMessageData(clonedMessageData)) {
2459 aError.Throw(NS_ERROR_FAILURE);
2460 return;
2463 messageData = std::move(clonedMessageData);
2464 } else {
2465 MOZ_ASSERT(message.CloneScope() ==
2466 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2468 messageData = ErrorMessageData();
2470 nsContentUtils::ReportToConsole(
2471 nsIScriptError::warningFlag, "DOM Window"_ns,
2472 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2473 nsContentUtils::eDOM_PROPERTIES,
2474 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2477 cc->SendWindowPostMessage(this, messageData, data);
2478 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2479 if (message.CloneScope() ==
2480 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2481 ClonedMessageData clonedMessageData;
2482 if (!message.BuildClonedMessageData(clonedMessageData)) {
2483 aError.Throw(NS_ERROR_FAILURE);
2484 return;
2487 messageData = std::move(clonedMessageData);
2488 } else {
2489 MOZ_ASSERT(message.CloneScope() ==
2490 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2492 messageData = ErrorMessageData();
2494 nsContentUtils::ReportToConsole(
2495 nsIScriptError::warningFlag, "DOM Window"_ns,
2496 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2497 nsContentUtils::eDOM_PROPERTIES,
2498 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2501 Unused << cp->SendWindowPostMessage(this, messageData, data);
2505 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2506 JS::Handle<JS::Value> aMessage,
2507 const WindowPostMessageOptions& aOptions,
2508 nsIPrincipal& aSubjectPrincipal,
2509 ErrorResult& aError) {
2510 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
2511 aSubjectPrincipal, aError);
2514 void BrowsingContext::SendCommitTransaction(ContentParent* aParent,
2515 const BaseTransaction& aTxn,
2516 uint64_t aEpoch) {
2517 Unused << aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2520 void BrowsingContext::SendCommitTransaction(ContentChild* aChild,
2521 const BaseTransaction& aTxn,
2522 uint64_t aEpoch) {
2523 aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2526 BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
2527 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
2528 MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content);
2530 IPCInitializer init;
2531 init.mId = Id();
2532 init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
2533 init.mWindowless = mWindowless;
2534 init.mUseRemoteTabs = mUseRemoteTabs;
2535 init.mUseRemoteSubframes = mUseRemoteSubframes;
2536 init.mCreatedDynamically = mCreatedDynamically;
2537 init.mChildOffset = mChildOffset;
2538 init.mOriginAttributes = mOriginAttributes;
2539 if (mChildSessionHistory && mozilla::SessionHistoryInParent()) {
2540 init.mSessionHistoryIndex = mChildSessionHistory->Index();
2541 init.mSessionHistoryCount = mChildSessionHistory->Count();
2543 init.mRequestContextId = mRequestContextId;
2544 init.mFields = mFields.RawValues();
2545 return init;
2548 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
2549 RefPtr<WindowContext> parent;
2550 if (mParentId != 0) {
2551 parent = WindowContext::GetById(mParentId);
2552 MOZ_RELEASE_ASSERT(parent);
2554 return parent.forget();
2557 already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
2558 RefPtr<BrowsingContext> opener;
2559 if (GetOpenerId() != 0) {
2560 opener = BrowsingContext::Get(GetOpenerId());
2561 MOZ_RELEASE_ASSERT(opener);
2563 return opener.forget();
2566 void BrowsingContext::StartDelayedAutoplayMediaComponents() {
2567 if (!mDocShell) {
2568 return;
2570 AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
2571 XRE_IsParentProcess() ? "Parent" : "Child", Id());
2572 mDocShell->StartDelayedAutoplayMediaComponents();
2575 nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
2576 MOZ_ASSERT(IsTop(),
2577 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2578 "browsing context");
2580 Transaction txn;
2581 txn.SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2582 txn.SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2583 return txn.Commit(this);
2586 template <typename Callback>
2587 void BrowsingContext::WalkPresContexts(Callback&& aCallback) {
2588 PreOrderWalk([&](BrowsingContext* aContext) {
2589 if (nsIDocShell* shell = aContext->GetDocShell()) {
2590 if (RefPtr pc = shell->GetPresContext()) {
2591 aCallback(pc.get());
2597 void BrowsingContext::PresContextAffectingFieldChanged() {
2598 WalkPresContexts([&](nsPresContext* aPc) {
2599 aPc->RecomputeBrowsingContextDependentData();
2603 void BrowsingContext::DidSet(FieldIndex<IDX_SessionStoreEpoch>,
2604 uint32_t aOldValue) {
2605 if (!mCurrentWindowContext) {
2606 return;
2608 SessionStoreChild* sessionStoreChild =
2609 SessionStoreChild::From(mCurrentWindowContext->GetWindowGlobalChild());
2610 if (!sessionStoreChild) {
2611 return;
2614 sessionStoreChild->SetEpoch(GetSessionStoreEpoch());
2617 void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
2618 MOZ_ASSERT(IsTop(),
2619 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2620 "browsing context");
2623 void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
2624 MOZ_ASSERT(IsTop(),
2625 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2626 "browsing context");
2629 void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
2630 ExplicitActiveStatus aOldValue) {
2631 const bool isActive = IsActive();
2632 const bool wasActive = [&] {
2633 if (aOldValue != ExplicitActiveStatus::None) {
2634 return aOldValue == ExplicitActiveStatus::Active;
2636 return GetParent() && GetParent()->IsActive();
2637 }();
2639 if (isActive == wasActive) {
2640 return;
2643 if (IsTop()) {
2644 Group()->UpdateToplevelsSuspendedIfNeeded();
2646 if (XRE_IsParentProcess()) {
2647 auto* bc = Canonical();
2648 if (BrowserParent* bp = bc->GetBrowserParent()) {
2649 bp->RecomputeProcessPriority();
2650 #if defined(XP_WIN) && defined(ACCESSIBILITY)
2651 if (a11y::Compatibility::IsDolphin()) {
2652 // update active accessible documents on windows
2653 if (a11y::DocAccessibleParent* tabDoc =
2654 bp->GetTopLevelDocAccessible()) {
2655 HWND window = tabDoc->GetEmulatedWindowHandle();
2656 MOZ_ASSERT(window);
2657 if (window) {
2658 if (isActive) {
2659 a11y::nsWinUtils::ShowNativeWindow(window);
2660 } else {
2661 a11y::nsWinUtils::HideNativeWindow(window);
2666 #endif
2671 PreOrderWalk([&](BrowsingContext* aContext) {
2672 if (nsCOMPtr<nsIDocShell> ds = aContext->GetDocShell()) {
2673 nsDocShell::Cast(ds)->ActivenessMaybeChanged();
2678 void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
2679 MOZ_ASSERT(IsTop(),
2680 "Should only set InRDMPane in the top-level browsing context");
2681 if (GetInRDMPane() == aOldValue) {
2682 return;
2684 PresContextAffectingFieldChanged();
2687 bool BrowsingContext::CanSet(FieldIndex<IDX_PageAwakeRequestCount>,
2688 uint32_t aNewValue, ContentParent* aSource) {
2689 return IsTop() && XRE_IsParentProcess() && !aSource;
2692 void BrowsingContext::DidSet(FieldIndex<IDX_PageAwakeRequestCount>,
2693 uint32_t aOldValue) {
2694 if (!IsTop() || aOldValue == GetPageAwakeRequestCount()) {
2695 return;
2697 Group()->UpdateToplevelsSuspendedIfNeeded();
2700 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
2701 ContentParent* aSource) -> CanSetResult {
2702 if (mozilla::SessionHistoryInParent()) {
2703 return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow
2704 : CanSetResult::Deny;
2707 // Without Session History in Parent, session restore code still needs to set
2708 // this from content processes.
2709 return LegacyRevertIfNotOwningOrParentProcess(aSource);
2712 void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
2713 RecomputeCanExecuteScripts();
2716 void BrowsingContext::RecomputeCanExecuteScripts() {
2717 const bool old = mCanExecuteScripts;
2718 if (!AllowJavascript()) {
2719 // Scripting has been explicitly disabled on our BrowsingContext.
2720 mCanExecuteScripts = false;
2721 } else if (GetParentWindowContext()) {
2722 // Otherwise, inherit parent.
2723 mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts();
2724 } else {
2725 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2726 // script. Allow.
2727 mCanExecuteScripts = true;
2730 if (old != mCanExecuteScripts) {
2731 for (WindowContext* wc : GetWindowContexts()) {
2732 wc->RecomputeCanExecuteScripts();
2737 bool BrowsingContext::InactiveForSuspend() const {
2738 if (!StaticPrefs::dom_suspend_inactive_enabled()) {
2739 return false;
2741 // We should suspend a page only when it's inactive and doesn't have any awake
2742 // request that is used to prevent page from being suspended because web page
2743 // might still need to run their script. Eg. waiting for media keys to resume
2744 // media, playing web audio, waiting in a video call conference room.
2745 return !IsActive() && GetPageAwakeRequestCount() == 0;
2748 bool BrowsingContext::CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2749 dom::TouchEventsOverride, ContentParent* aSource) {
2750 return XRE_IsParentProcess() && !aSource;
2753 void BrowsingContext::DidSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2754 dom::TouchEventsOverride&& aOldValue) {
2755 if (GetTouchEventsOverrideInternal() == aOldValue) {
2756 return;
2758 WalkPresContexts([&](nsPresContext* aPc) {
2759 aPc->MediaFeatureValuesChanged(
2760 {MediaFeatureChangeReason::SystemMetricsChange},
2761 // We're already iterating through sub documents, so we don't need to
2762 // propagate the change again.
2763 MediaFeatureChangePropagation::JustThisDocument);
2767 void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderColorSchemes>,
2768 EmbedderColorSchemes&& aOldValue) {
2769 if (GetEmbedderColorSchemes() == aOldValue) {
2770 return;
2772 PresContextAffectingFieldChanged();
2775 void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
2776 dom::PrefersColorSchemeOverride aOldValue) {
2777 MOZ_ASSERT(IsTop());
2778 if (PrefersColorSchemeOverride() == aOldValue) {
2779 return;
2781 PresContextAffectingFieldChanged();
2784 void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
2785 nsString&& aOldValue) {
2786 MOZ_ASSERT(IsTop());
2787 if (GetMediumOverride() == aOldValue) {
2788 return;
2790 PresContextAffectingFieldChanged();
2793 void BrowsingContext::DidSet(FieldIndex<IDX_DisplayMode>,
2794 enum DisplayMode aOldValue) {
2795 MOZ_ASSERT(IsTop());
2797 if (GetDisplayMode() == aOldValue) {
2798 return;
2801 WalkPresContexts([&](nsPresContext* aPc) {
2802 aPc->MediaFeatureValuesChanged(
2803 {MediaFeatureChangeReason::DisplayModeChange},
2804 // We're already iterating through sub documents, so we don't need
2805 // to propagate the change again.
2807 // Images and other resources don't change their display-mode
2808 // evaluation, display-mode is a property of the browsing context.
2809 MediaFeatureChangePropagation::JustThisDocument);
2813 void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) {
2814 MOZ_ASSERT(IsTop(), "Set muted flag on non top-level context!");
2815 USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64,
2816 GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child",
2817 Id());
2818 PreOrderWalk([&](BrowsingContext* aContext) {
2819 nsPIDOMWindowOuter* win = aContext->GetDOMWindow();
2820 if (win) {
2821 win->RefreshMediaElementsVolume();
2826 bool BrowsingContext::CanSet(FieldIndex<IDX_IsAppTab>, const bool& aValue,
2827 ContentParent* aSource) {
2828 return XRE_IsParentProcess() && !aSource && IsTop();
2831 bool BrowsingContext::CanSet(FieldIndex<IDX_HasSiblings>, const bool& aValue,
2832 ContentParent* aSource) {
2833 return XRE_IsParentProcess() && !aSource && IsTop();
2836 bool BrowsingContext::CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2837 const bool& aValue, ContentParent* aSource) {
2838 return IsTop();
2841 void BrowsingContext::DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2842 bool aOldValue) {
2843 MOZ_ASSERT(IsTop(), "Set attribute on non top-level context!");
2844 if (aOldValue == GetShouldDelayMediaFromStart()) {
2845 return;
2847 if (!GetShouldDelayMediaFromStart()) {
2848 PreOrderWalk([&](BrowsingContext* aContext) {
2849 if (nsPIDOMWindowOuter* win = aContext->GetDOMWindow()) {
2850 win->ActivateMediaComponents();
2856 bool BrowsingContext::CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue,
2857 ContentParent* aSource) {
2858 return XRE_IsParentProcess() && !aSource && IsTop();
2861 void BrowsingContext::DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue) {
2862 MOZ_ASSERT(IsTop());
2863 if (GetOverrideDPPX() == aOldValue) {
2864 return;
2866 PresContextAffectingFieldChanged();
2869 void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent,
2870 ErrorResult& aRv) {
2871 Top()->SetUserAgentOverride(aUserAgent, aRv);
2874 nsresult BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) {
2875 return Top()->SetUserAgentOverride(aUserAgent);
2878 void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
2879 MOZ_ASSERT(IsTop());
2881 PreOrderWalk([&](BrowsingContext* aContext) {
2882 nsIDocShell* shell = aContext->GetDocShell();
2883 if (shell) {
2884 shell->ClearCachedUserAgent();
2889 bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool,
2890 ContentParent* aSource) {
2891 return IsTop() && !aSource && mozilla::BFCacheInParent();
2894 void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
2895 MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent());
2896 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2898 const bool isInBFCache = GetIsInBFCache();
2899 if (!isInBFCache) {
2900 UpdateCurrentTopByBrowserId(this);
2901 PreOrderWalk([&](BrowsingContext* aContext) {
2902 aContext->mIsInBFCache = false;
2903 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2904 if (shell) {
2905 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true);
2910 if (isInBFCache && XRE_IsContentProcess() && mDocShell) {
2911 nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide();
2914 if (isInBFCache) {
2915 PreOrderWalk([&](BrowsingContext* aContext) {
2916 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2917 if (shell) {
2918 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false);
2921 } else {
2922 PostOrderWalk([&](BrowsingContext* aContext) {
2923 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2924 if (shell) {
2925 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true);
2930 if (isInBFCache) {
2931 PreOrderWalk([&](BrowsingContext* aContext) {
2932 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2933 if (shell) {
2934 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false);
2935 if (nsPresContext* pc = shell->GetPresContext()) {
2936 pc->EventStateManager()->ResetHoverState();
2939 aContext->mIsInBFCache = true;
2940 Document* doc = aContext->GetDocument();
2941 if (doc) {
2942 // Notifying needs to happen after mIsInBFCache is set to true.
2943 doc->NotifyActivityChanged();
2947 if (XRE_IsParentProcess()) {
2948 if (mCurrentWindowContext &&
2949 mCurrentWindowContext->Canonical()->Fullscreen()) {
2950 mCurrentWindowContext->Canonical()->ExitTopChromeDocumentFullscreen();
2956 void BrowsingContext::DidSet(FieldIndex<IDX_SyntheticDocumentContainer>) {
2957 if (WindowContext* parentWindowContext = GetParentWindowContext()) {
2958 parentWindowContext->UpdateChildSynthetic(this,
2959 GetSyntheticDocumentContainer());
2963 void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
2964 ErrorResult& aRv) {
2965 Top()->SetPlatformOverride(aPlatform, aRv);
2968 void BrowsingContext::DidSet(FieldIndex<IDX_PlatformOverride>) {
2969 MOZ_ASSERT(IsTop());
2971 PreOrderWalk([&](BrowsingContext* aContext) {
2972 nsIDocShell* shell = aContext->GetDocShell();
2973 if (shell) {
2974 shell->ClearCachedPlatform();
2979 auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
2980 ContentParent* aSource) -> CanSetResult {
2981 if (aSource) {
2982 MOZ_ASSERT(XRE_IsParentProcess());
2984 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
2985 return CanSetResult::Revert;
2987 } else if (!IsInProcess() && !XRE_IsParentProcess()) {
2988 // Don't allow this to be set from content processes that
2989 // don't own the BrowsingContext.
2990 return CanSetResult::Deny;
2993 return CanSetResult::Allow;
2996 bool BrowsingContext::CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
2997 const bool& aValue, ContentParent* aSource) {
2998 // Should only be set in the parent process.
2999 return XRE_IsParentProcess() && !aSource && IsTop();
3002 void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
3003 bool aOldValue) {
3004 bool isActivateEvent = GetIsActiveBrowserWindowInternal();
3005 // The browser window containing this context has changed
3006 // activation state so update window inactive document states
3007 // for all in-process documents.
3008 PreOrderWalk([isActivateEvent](BrowsingContext* aContext) {
3009 if (RefPtr<Document> doc = aContext->GetExtantDocument()) {
3010 doc->UpdateDocumentStates(DocumentState::WINDOW_INACTIVE, true);
3012 RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
3013 if (win) {
3014 RefPtr<MediaDevices> devices;
3015 if (isActivateEvent && (devices = win->GetExtantMediaDevices())) {
3016 devices->BrowserWindowBecameActive();
3019 if (XRE_IsContentProcess() &&
3020 (!aContext->GetParent() || !aContext->GetParent()->IsInProcess())) {
3021 // Send the inner window an activate/deactivate event if
3022 // the context is the top of a sub-tree of in-process
3023 // contexts.
3024 nsContentUtils::DispatchEventOnlyToChrome(
3025 doc, nsGlobalWindowInner::Cast(win),
3026 isActivateEvent ? u"activate"_ns : u"deactivate"_ns,
3027 CanBubble::eYes, Cancelable::eYes, nullptr);
3034 bool BrowsingContext::CanSet(FieldIndex<IDX_OpenerPolicy>,
3035 nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
3036 ContentParent* aSource) {
3037 // A potentially cross-origin isolated BC can't change opener policy, nor can
3038 // a BC become potentially cross-origin isolated. An unchanged policy is
3039 // always OK.
3040 return GetOpenerPolicy() == aPolicy ||
3041 (GetOpenerPolicy() !=
3042 nsILoadInfo::
3043 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
3044 aPolicy !=
3045 nsILoadInfo::
3046 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP);
3049 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
3050 const bool& aAllowContentRetargeting,
3051 ContentParent* aSource) -> CanSetResult {
3052 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3055 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
3056 const bool& aAllowContentRetargetingOnChildren,
3057 ContentParent* aSource) -> CanSetResult {
3058 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3061 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowPlugins>,
3062 const bool& aAllowPlugins, ContentParent* aSource)
3063 -> CanSetResult {
3064 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3067 bool BrowsingContext::CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>,
3068 const bool& aAllowed, ContentParent* aSource) {
3069 return CheckOnlyEmbedderCanSet(aSource);
3072 bool BrowsingContext::CanSet(FieldIndex<IDX_UseErrorPages>,
3073 const bool& aUseErrorPages,
3074 ContentParent* aSource) {
3075 return CheckOnlyEmbedderCanSet(aSource);
3078 TouchEventsOverride BrowsingContext::TouchEventsOverride() const {
3079 for (const auto* bc = this; bc; bc = bc->GetParent()) {
3080 auto tev = bc->GetTouchEventsOverrideInternal();
3081 if (tev != dom::TouchEventsOverride::None) {
3082 return tev;
3085 return dom::TouchEventsOverride::None;
3088 bool BrowsingContext::TargetTopLevelLinkClicksToBlank() const {
3089 return Top()->GetTargetTopLevelLinkClicksToBlankInternal();
3092 // We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
3093 // BC field. And we map it to the top level BrowsingContext.
3094 bool BrowsingContext::WatchedByDevTools() {
3095 return Top()->GetWatchedByDevToolsInternal();
3098 // Enforce that the watchedByDevTools BC field can only be set on the top level
3099 // Browsing Context.
3100 bool BrowsingContext::CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>,
3101 const bool& aWatchedByDevTools,
3102 ContentParent* aSource) {
3103 return IsTop();
3105 void BrowsingContext::SetWatchedByDevTools(bool aWatchedByDevTools,
3106 ErrorResult& aRv) {
3107 if (!IsTop()) {
3108 aRv.ThrowInvalidModificationError(
3109 "watchedByDevTools can only be set on top BrowsingContext");
3110 return;
3112 SetWatchedByDevToolsInternal(aWatchedByDevTools, aRv);
3115 auto BrowsingContext::CanSet(FieldIndex<IDX_DefaultLoadFlags>,
3116 const uint32_t& aDefaultLoadFlags,
3117 ContentParent* aSource) -> CanSetResult {
3118 // Bug 1623565 - Are these flags only used by the debugger, which makes it
3119 // possible that this field can only be settable by the parent process?
3120 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3123 void BrowsingContext::DidSet(FieldIndex<IDX_DefaultLoadFlags>) {
3124 auto loadFlags = GetDefaultLoadFlags();
3125 if (GetDocShell()) {
3126 nsDocShell::Cast(GetDocShell())->SetLoadGroupDefaultLoadFlags(loadFlags);
3129 if (XRE_IsParentProcess()) {
3130 PreOrderWalk([&](BrowsingContext* aContext) {
3131 if (aContext != this) {
3132 // Setting load flags on a discarded context has no effect.
3133 Unused << aContext->SetDefaultLoadFlags(loadFlags);
3139 bool BrowsingContext::CanSet(FieldIndex<IDX_UseGlobalHistory>,
3140 const bool& aUseGlobalHistory,
3141 ContentParent* aSource) {
3142 // Should only be set in the parent process.
3143 // return XRE_IsParentProcess() && !aSource;
3144 return true;
3147 auto BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>,
3148 const nsString& aUserAgent, ContentParent* aSource)
3149 -> CanSetResult {
3150 if (!IsTop()) {
3151 return CanSetResult::Deny;
3154 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3157 auto BrowsingContext::CanSet(FieldIndex<IDX_PlatformOverride>,
3158 const nsString& aPlatform, ContentParent* aSource)
3159 -> CanSetResult {
3160 if (!IsTop()) {
3161 return CanSetResult::Deny;
3164 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3167 bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
3168 if (XRE_IsParentProcess()) {
3169 uint64_t childId = aSource ? aSource->ChildID() : 0;
3170 return Canonical()->IsEmbeddedInProcess(childId);
3172 return mEmbeddedByThisProcess;
3175 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
3176 const uint64_t& aValue, ContentParent* aSource) {
3177 // If we have a parent window, our embedder inner window ID must match it.
3178 if (mParentWindow) {
3179 return mParentWindow->Id() == aValue;
3182 // For toplevel BrowsingContext instances, this value may only be set by the
3183 // parent process, or initialized to `0`.
3184 return CheckOnlyEmbedderCanSet(aSource);
3187 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
3188 const Maybe<nsString>&, ContentParent* aSource) {
3189 return CheckOnlyEmbedderCanSet(aSource);
3192 auto BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
3193 const uint64_t& aValue, ContentParent* aSource)
3194 -> CanSetResult {
3195 // Generally allow clearing this. We may want to be more precise about this
3196 // check in the future.
3197 if (aValue == 0) {
3198 return CanSetResult::Allow;
3201 // We must have access to the specified context.
3202 RefPtr<WindowContext> window = WindowContext::GetById(aValue);
3203 if (!window || window->GetBrowsingContext() != this) {
3204 return CanSetResult::Deny;
3207 if (aSource) {
3208 // If the sending process is no longer the current owner, revert
3209 MOZ_ASSERT(XRE_IsParentProcess());
3210 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
3211 return CanSetResult::Revert;
3213 } else if (XRE_IsContentProcess() && !IsOwnedByProcess()) {
3214 return CanSetResult::Deny;
3217 return CanSetResult::Allow;
3220 bool BrowsingContext::CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>,
3221 const uint64_t& aValue, ContentParent* aSource) {
3222 return XRE_IsParentProcess() && !aSource;
3225 void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
3226 RefPtr<WindowContext> prevWindowContext = mCurrentWindowContext.forget();
3227 mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
3228 MOZ_ASSERT(
3229 !mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext),
3230 "WindowContext not registered?");
3232 // Clear our cached `children` value, to ensure that JS sees the up-to-date
3233 // value.
3234 BrowsingContext_Binding::ClearCachedChildrenValue(this);
3236 if (XRE_IsParentProcess()) {
3237 if (prevWindowContext != mCurrentWindowContext) {
3238 if (prevWindowContext) {
3239 prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false);
3241 if (mCurrentWindowContext) {
3242 // We set a timer when we set the current inner window. This
3243 // will then flush the session storage to session store to
3244 // make sure that we don't miss to store session storage to
3245 // session store that is a result of navigation. This is due
3246 // to Bug 1700623. We wish to fix this in Bug 1711886, where
3247 // making sure to store everything would make this timer
3248 // unnecessary.
3249 Canonical()->MaybeScheduleSessionStoreUpdate();
3250 mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true);
3253 BrowserParent::UpdateFocusFromBrowsingContext();
3257 bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
3258 ContentParent* aSource) {
3259 // Ensure that we only mark a browsing context as popup spam once and never
3260 // unmark it.
3261 return aValue && !GetIsPopupSpam();
3264 void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) {
3265 if (GetIsPopupSpam()) {
3266 PopupBlocker::RegisterOpenPopupSpam();
3270 bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>,
3271 const nsString& aMessageManagerGroup,
3272 ContentParent* aSource) {
3273 // Should only be set in the parent process on toplevel.
3274 return XRE_IsParentProcess() && !aSource && IsTopContent();
3277 bool BrowsingContext::CanSet(
3278 FieldIndex<IDX_OrientationLock>,
3279 const mozilla::hal::ScreenOrientation& aOrientationLock,
3280 ContentParent* aSource) {
3281 return IsTop();
3284 bool BrowsingContext::IsLoading() {
3285 if (GetLoading()) {
3286 return true;
3289 // If we're in the same process as the page, we're possibly just
3290 // updating the flag.
3291 nsIDocShell* shell = GetDocShell();
3292 if (shell) {
3293 Document* doc = shell->GetDocument();
3294 return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
3297 return false;
3300 void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) {
3301 if (mFields.Get<IDX_Loading>()) {
3302 return;
3305 while (!mDeprioritizedLoadRunner.isEmpty()) {
3306 nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
3307 NS_DispatchToCurrentThread(runner.forget());
3310 if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
3311 Top() == this) {
3312 Group()->FlushPostMessageEvents();
3316 // Inform the Document for this context of the (potential) change in
3317 // loading state
3318 void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) {
3319 nsPIDOMWindowOuter* outer = GetDOMWindow();
3320 if (!outer) {
3321 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3322 ("DidSetAncestorLoading BC: %p -- No outer window", (void*)this));
3323 return;
3325 Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc();
3326 if (document) {
3327 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3328 ("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)",
3329 (void*)this, GetAncestorLoading(), document->GetReadyStateEnum(),
3330 document->GetReadyStateEnum()));
3331 document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(),
3332 document->GetReadyStateEnum());
3336 void BrowsingContext::DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>) {
3337 MOZ_ASSERT(IsTop(),
3338 "Should only set AuthorStyleDisabledDefault in the top "
3339 "browsing context");
3341 // We don't need to handle changes to this field, since PageStyleChild.sys.mjs
3342 // will respond to the PageStyle:Disable message in all content processes.
3344 // But we store the state here on the top BrowsingContext so that the
3345 // docshell has somewhere to look for the current author style disabling
3346 // state when new iframes are inserted.
3349 void BrowsingContext::DidSet(FieldIndex<IDX_TextZoom>, float aOldValue) {
3350 if (GetTextZoom() == aOldValue) {
3351 return;
3354 if (IsInProcess()) {
3355 if (nsIDocShell* shell = GetDocShell()) {
3356 if (nsPresContext* pc = shell->GetPresContext()) {
3357 pc->RecomputeBrowsingContextDependentData();
3361 for (BrowsingContext* child : Children()) {
3362 // Setting text zoom on a discarded context has no effect.
3363 Unused << child->SetTextZoom(GetTextZoom());
3367 if (IsTop() && XRE_IsParentProcess()) {
3368 if (Element* element = GetEmbedderElement()) {
3369 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"TextZoomChange"_ns,
3370 CanBubble::eYes,
3371 ChromeOnlyDispatch::eYes);
3376 // TODO(emilio): It'd be potentially nicer and cheaper to allow to set this only
3377 // on the Top() browsing context, but there are a lot of tests that rely on
3378 // zooming a subframe so...
3379 void BrowsingContext::DidSet(FieldIndex<IDX_FullZoom>, float aOldValue) {
3380 if (GetFullZoom() == aOldValue) {
3381 return;
3384 if (IsInProcess()) {
3385 if (nsIDocShell* shell = GetDocShell()) {
3386 if (nsPresContext* pc = shell->GetPresContext()) {
3387 pc->RecomputeBrowsingContextDependentData();
3391 for (BrowsingContext* child : Children()) {
3392 // Setting full zoom on a discarded context has no effect.
3393 Unused << child->SetFullZoom(GetFullZoom());
3397 if (IsTop() && XRE_IsParentProcess()) {
3398 if (Element* element = GetEmbedderElement()) {
3399 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"FullZoomChange"_ns,
3400 CanBubble::eYes,
3401 ChromeOnlyDispatch::eYes);
3406 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
3407 MOZ_ASSERT(IsLoading());
3408 MOZ_ASSERT(Top() == this);
3410 RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
3411 mDeprioritizedLoadRunner.insertBack(runner);
3412 NS_DispatchToCurrentThreadQueue(
3413 runner.forget(), StaticPrefs::page_load_deprioritization_period(),
3414 EventQueuePriority::Idle);
3417 bool BrowsingContext::IsDynamic() const {
3418 const BrowsingContext* current = this;
3419 do {
3420 if (current->CreatedDynamically()) {
3421 return true;
3423 } while ((current = current->GetParent()));
3425 return false;
3428 bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const {
3429 for (const BrowsingContext* current = this; current && current->GetParent();
3430 current = current->GetParent()) {
3431 if (current->CreatedDynamically()) {
3432 return false;
3434 aPath.AppendElement(current->ChildOffset());
3436 return true;
3439 void BrowsingContext::GetHistoryID(JSContext* aCx,
3440 JS::MutableHandle<JS::Value> aVal,
3441 ErrorResult& aError) {
3442 if (!xpc::ID2JSValue(aCx, GetHistoryID(), aVal)) {
3443 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
3447 void BrowsingContext::InitSessionHistory() {
3448 MOZ_ASSERT(!IsDiscarded());
3449 MOZ_ASSERT(IsTop());
3450 MOZ_ASSERT(EverAttached());
3452 if (!GetHasSessionHistory()) {
3453 MOZ_ALWAYS_SUCCEEDS(SetHasSessionHistory(true));
3457 ChildSHistory* BrowsingContext::GetChildSessionHistory() {
3458 if (!mozilla::SessionHistoryInParent()) {
3459 // For now we're checking that the session history object for the child
3460 // process is available before returning the ChildSHistory object, because
3461 // it is the actual implementation that ChildSHistory forwards to. This can
3462 // be removed once session history is stored exclusively in the parent
3463 // process.
3464 return mChildSessionHistory && mChildSessionHistory->IsInProcess()
3465 ? mChildSessionHistory.get()
3466 : nullptr;
3469 return mChildSessionHistory;
3472 void BrowsingContext::CreateChildSHistory() {
3473 MOZ_ASSERT(IsTop());
3474 MOZ_ASSERT(GetHasSessionHistory());
3475 MOZ_ASSERT(!mChildSessionHistory);
3477 // Because session history is global in a browsing context tree, every process
3478 // that has access to a browsing context tree needs access to its session
3479 // history. That is why we create the ChildSHistory object in every process
3480 // where we have access to this browsing context (which is the top one).
3481 mChildSessionHistory = new ChildSHistory(this);
3483 // If the top browsing context (this one) is loaded in this process then we
3484 // also create the session history implementation for the child process.
3485 // This can be removed once session history is stored exclusively in the
3486 // parent process.
3487 mChildSessionHistory->SetIsInProcess(IsInProcess());
3490 void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
3491 bool aOldValue) {
3492 MOZ_ASSERT(GetHasSessionHistory() || !aOldValue,
3493 "We don't support turning off session history.");
3495 if (GetHasSessionHistory() && !aOldValue) {
3496 CreateChildSHistory();
3500 bool BrowsingContext::CanSet(
3501 FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
3502 const bool& aTargetTopLevelLinkClicksToBlankInternal,
3503 ContentParent* aSource) {
3504 return XRE_IsParentProcess() && !aSource && IsTop();
3507 bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
3508 ContentParent* aSource) {
3509 // We should only be able to set this for toplevel contexts which don't have
3510 // an ID yet.
3511 return GetBrowserId() == 0 && IsTop() && Children().IsEmpty();
3514 bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
3515 bool aNewValue, ContentParent* aSource) {
3516 // Can only be cleared from `true` to `false`, and should only ever be set on
3517 // the toplevel BrowsingContext.
3518 return IsTop() && GetPendingInitialization() && !aNewValue;
3521 bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
3522 ContentParent* aSource) {
3523 return IsTop();
3526 bool BrowsingContext::CanSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
3527 const bool& aIsUnderHiddenEmbedderElement,
3528 ContentParent* aSource) {
3529 return true;
3532 bool BrowsingContext::CanSet(FieldIndex<IDX_ForceOffline>, bool aNewValue,
3533 ContentParent* aSource) {
3534 return XRE_IsParentProcess() && !aSource;
3537 void BrowsingContext::DidSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
3538 bool aOldValue) {
3539 nsIDocShell* shell = GetDocShell();
3540 if (!shell) {
3541 return;
3543 const bool newValue = IsUnderHiddenEmbedderElement();
3544 if (NS_WARN_IF(aOldValue == newValue)) {
3545 return;
3548 if (auto* bc = BrowserChild::GetFrom(shell)) {
3549 bc->UpdateVisibility();
3552 if (PresShell* presShell = shell->GetPresShell()) {
3553 presShell->SetIsUnderHiddenEmbedderElement(newValue);
3556 // Propagate to children.
3557 for (BrowsingContext* child : Children()) {
3558 Element* embedderElement = child->GetEmbedderElement();
3559 if (!embedderElement) {
3560 // TODO: We shouldn't need to null check here since `child` and the
3561 // element returned by `child->GetEmbedderElement()` are in our
3562 // process (the actual browsing context represented by `child` may not
3563 // be, but that doesn't matter). However, there are currently a very
3564 // small number of crashes due to `embedderElement` being null, somehow
3565 // - see bug 1551241. For now we wallpaper the crash.
3566 continue;
3569 bool embedderFrameIsHidden = true;
3570 if (auto* embedderFrame = embedderElement->GetPrimaryFrame()) {
3571 embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
3574 bool hidden = IsUnderHiddenEmbedderElement() || embedderFrameIsHidden;
3575 if (child->IsUnderHiddenEmbedderElement() != hidden) {
3576 Unused << child->SetIsUnderHiddenEmbedderElement(hidden);
3581 bool BrowsingContext::IsPopupAllowed() {
3582 for (auto* context = GetCurrentWindowContext(); context;
3583 context = context->GetParentWindowContext()) {
3584 if (context->CanShowPopup()) {
3585 return true;
3589 return false;
3592 /* static */
3593 bool BrowsingContext::ShouldAddEntryForRefresh(
3594 nsIURI* aPreviousURI, const SessionHistoryInfo& aInfo) {
3595 return ShouldAddEntryForRefresh(aPreviousURI, aInfo.GetURI(),
3596 aInfo.HasPostData());
3599 /* static */
3600 bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aPreviousURI,
3601 nsIURI* aNewURI,
3602 bool aHasPostData) {
3603 if (aHasPostData) {
3604 return true;
3607 bool equalsURI = false;
3608 if (aPreviousURI) {
3609 aPreviousURI->Equals(aNewURI, &equalsURI);
3611 return !equalsURI;
3614 void BrowsingContext::SessionHistoryCommit(
3615 const LoadingSessionHistoryInfo& aInfo, uint32_t aLoadType,
3616 nsIURI* aPreviousURI, SessionHistoryInfo* aPreviousActiveEntry,
3617 bool aPersist, bool aCloneEntryChildren, bool aChannelExpired,
3618 uint32_t aCacheKey) {
3619 nsID changeID = {};
3620 if (XRE_IsContentProcess()) {
3621 RefPtr<ChildSHistory> rootSH = Top()->GetChildSessionHistory();
3622 if (rootSH) {
3623 if (!aInfo.mLoadIsFromSessionHistory) {
3624 // We try to mimic as closely as possible what will happen in
3625 // CanonicalBrowsingContext::SessionHistoryCommit. We'll be
3626 // incrementing the session history length if we're not replacing,
3627 // this is a top-level load or it's not the initial load in an iframe,
3628 // ShouldUpdateSessionHistory(loadType) returns true and it's not a
3629 // refresh for which ShouldAddEntryForRefresh returns false.
3630 // It is possible that this leads to wrong length temporarily, but
3631 // so would not having the check for replace.
3632 // Note that nsSHistory::AddEntry does a replace load if the current
3633 // entry is not marked as a persisted entry. The child process does
3634 // not have access to the current entry, so we use the previous active
3635 // entry as the best approximation. When that's not the current entry
3636 // then the length might be wrong briefly, until the parent process
3637 // commits the actual length.
3638 if (!LOAD_TYPE_HAS_FLAGS(
3639 aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) &&
3640 (IsTop()
3641 ? (!aPreviousActiveEntry || aPreviousActiveEntry->GetPersist())
3642 : !!aPreviousActiveEntry) &&
3643 ShouldUpdateSessionHistory(aLoadType) &&
3644 (!LOAD_TYPE_HAS_FLAGS(aLoadType,
3645 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) ||
3646 ShouldAddEntryForRefresh(aPreviousURI, aInfo.mInfo))) {
3647 changeID = rootSH->AddPendingHistoryChange();
3649 } else {
3650 // History load doesn't change the length, only index.
3651 changeID = rootSH->AddPendingHistoryChange(aInfo.mOffset, 0);
3654 ContentChild* cc = ContentChild::GetSingleton();
3655 mozilla::Unused << cc->SendHistoryCommit(
3656 this, aInfo.mLoadId, changeID, aLoadType, aPersist, aCloneEntryChildren,
3657 aChannelExpired, aCacheKey);
3658 } else {
3659 Canonical()->SessionHistoryCommit(aInfo.mLoadId, changeID, aLoadType,
3660 aPersist, aCloneEntryChildren,
3661 aChannelExpired, aCacheKey);
3665 void BrowsingContext::SetActiveSessionHistoryEntry(
3666 const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
3667 uint32_t aLoadType, uint32_t aUpdatedCacheKey, bool aUpdateLength) {
3668 if (XRE_IsContentProcess()) {
3669 // XXX Why we update cache key only in content process case?
3670 if (aUpdatedCacheKey != 0) {
3671 aInfo->SetCacheKey(aUpdatedCacheKey);
3674 nsID changeID = {};
3675 if (aUpdateLength) {
3676 RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
3677 if (shistory) {
3678 changeID = shistory->AddPendingHistoryChange();
3681 ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry(
3682 this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey,
3683 changeID);
3684 } else {
3685 Canonical()->SetActiveSessionHistoryEntry(
3686 aPreviousScrollPos, aInfo, aLoadType, aUpdatedCacheKey, nsID());
3690 void BrowsingContext::ReplaceActiveSessionHistoryEntry(
3691 SessionHistoryInfo* aInfo) {
3692 if (XRE_IsContentProcess()) {
3693 ContentChild::GetSingleton()->SendReplaceActiveSessionHistoryEntry(this,
3694 *aInfo);
3695 } else {
3696 Canonical()->ReplaceActiveSessionHistoryEntry(aInfo);
3700 void BrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
3701 if (XRE_IsContentProcess()) {
3702 ContentChild::GetSingleton()
3703 ->SendRemoveDynEntriesFromActiveSessionHistoryEntry(this);
3704 } else {
3705 Canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
3709 void BrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
3710 if (XRE_IsContentProcess()) {
3711 ContentChild::GetSingleton()->SendRemoveFromSessionHistory(this, aChangeID);
3712 } else {
3713 Canonical()->RemoveFromSessionHistory(aChangeID);
3717 void BrowsingContext::HistoryGo(
3718 int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
3719 bool aUserActivation, std::function<void(Maybe<int32_t>&&)>&& aResolver) {
3720 if (XRE_IsContentProcess()) {
3721 ContentChild::GetSingleton()->SendHistoryGo(
3722 this, aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3723 std::move(aResolver),
3724 [](mozilla::ipc::
3725 ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
3726 } else {
3727 RefPtr<CanonicalBrowsingContext> self = Canonical();
3728 aResolver(self->HistoryGo(
3729 aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3730 Canonical()->GetContentParent()
3731 ? Some(Canonical()->GetContentParent()->ChildID())
3732 : Nothing()));
3736 void BrowsingContext::SetChildSHistory(ChildSHistory* aChildSHistory) {
3737 mChildSessionHistory = aChildSHistory;
3738 mChildSessionHistory->SetBrowsingContext(this);
3739 mFields.SetWithoutSyncing<IDX_HasSessionHistory>(true);
3742 bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) {
3743 // We don't update session history on reload unless we're loading
3744 // an iframe in shift-reload case.
3745 return nsDocShell::ShouldUpdateGlobalHistory(aLoadType) &&
3746 (!(aLoadType & nsIDocShell::LOAD_CMD_RELOAD) ||
3747 (IsForceReloadType(aLoadType) && IsSubframe()));
3750 nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
3751 // We only rate limit non system callers
3752 if (aCallerType == CallerType::System) {
3753 return NS_OK;
3756 // Fetch rate limiting preferences
3757 uint32_t limitCount =
3758 StaticPrefs::dom_navigation_locationChangeRateLimit_count();
3759 uint32_t timeSpanSeconds =
3760 StaticPrefs::dom_navigation_locationChangeRateLimit_timespan();
3762 // Disable throttling if either of the preferences is set to 0.
3763 if (limitCount == 0 || timeSpanSeconds == 0) {
3764 return NS_OK;
3767 TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds);
3769 if (mLocationChangeRateLimitSpanStart.IsNull() ||
3770 ((TimeStamp::Now() - mLocationChangeRateLimitSpanStart) > throttleSpan)) {
3771 // Initial call or timespan exceeded, reset counter and timespan.
3772 mLocationChangeRateLimitSpanStart = TimeStamp::Now();
3773 mLocationChangeRateLimitCount = 1;
3774 return NS_OK;
3777 if (mLocationChangeRateLimitCount >= limitCount) {
3778 // Rate limit reached
3780 Document* doc = GetDocument();
3781 if (doc) {
3782 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc,
3783 nsContentUtils::eDOM_PROPERTIES,
3784 "LocChangeFloodingPrevented");
3787 return NS_ERROR_DOM_SECURITY_ERR;
3790 mLocationChangeRateLimitCount++;
3791 return NS_OK;
3794 void BrowsingContext::ResetLocationChangeRateLimit() {
3795 // Resetting the timestamp object will cause the check function to
3796 // init again and reset the rate limit.
3797 mLocationChangeRateLimitSpanStart = TimeStamp();
3800 void BrowsingContext::LocationCreated(dom::Location* aLocation) {
3801 MOZ_ASSERT(!aLocation->isInList());
3802 mLocations.insertBack(aLocation);
3805 void BrowsingContext::ClearCachedValuesOfLocations() {
3806 for (dom::Location* loc = mLocations.getFirst(); loc; loc = loc->getNext()) {
3807 loc->ClearCachedValues();
3811 } // namespace dom
3813 namespace ipc {
3815 void IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Write(
3816 IPC::MessageWriter* aWriter, IProtocol* aActor,
3817 const dom::MaybeDiscarded<dom::BrowsingContext>& aParam) {
3818 MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
3819 aParam.GetMaybeDiscarded()->EverAttached());
3820 uint64_t id = aParam.ContextId();
3821 WriteIPDLParam(aWriter, aActor, id);
3824 bool IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Read(
3825 IPC::MessageReader* aReader, IProtocol* aActor,
3826 dom::MaybeDiscarded<dom::BrowsingContext>* aResult) {
3827 uint64_t id = 0;
3828 if (!ReadIPDLParam(aReader, aActor, &id)) {
3829 return false;
3832 if (id == 0) {
3833 *aResult = nullptr;
3834 } else if (RefPtr<dom::BrowsingContext> bc = dom::BrowsingContext::Get(id)) {
3835 *aResult = std::move(bc);
3836 } else {
3837 aResult->SetDiscarded(id);
3839 return true;
3842 void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
3843 IPC::MessageWriter* aWriter, IProtocol* aActor,
3844 const dom::BrowsingContext::IPCInitializer& aInit) {
3845 // Write actor ID parameters.
3846 WriteIPDLParam(aWriter, aActor, aInit.mId);
3847 WriteIPDLParam(aWriter, aActor, aInit.mParentId);
3848 WriteIPDLParam(aWriter, aActor, aInit.mWindowless);
3849 WriteIPDLParam(aWriter, aActor, aInit.mUseRemoteTabs);
3850 WriteIPDLParam(aWriter, aActor, aInit.mUseRemoteSubframes);
3851 WriteIPDLParam(aWriter, aActor, aInit.mCreatedDynamically);
3852 WriteIPDLParam(aWriter, aActor, aInit.mChildOffset);
3853 WriteIPDLParam(aWriter, aActor, aInit.mOriginAttributes);
3854 WriteIPDLParam(aWriter, aActor, aInit.mRequestContextId);
3855 WriteIPDLParam(aWriter, aActor, aInit.mSessionHistoryIndex);
3856 WriteIPDLParam(aWriter, aActor, aInit.mSessionHistoryCount);
3857 WriteIPDLParam(aWriter, aActor, aInit.mFields);
3860 bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
3861 IPC::MessageReader* aReader, IProtocol* aActor,
3862 dom::BrowsingContext::IPCInitializer* aInit) {
3863 // Read actor ID parameters.
3864 if (!ReadIPDLParam(aReader, aActor, &aInit->mId) ||
3865 !ReadIPDLParam(aReader, aActor, &aInit->mParentId) ||
3866 !ReadIPDLParam(aReader, aActor, &aInit->mWindowless) ||
3867 !ReadIPDLParam(aReader, aActor, &aInit->mUseRemoteTabs) ||
3868 !ReadIPDLParam(aReader, aActor, &aInit->mUseRemoteSubframes) ||
3869 !ReadIPDLParam(aReader, aActor, &aInit->mCreatedDynamically) ||
3870 !ReadIPDLParam(aReader, aActor, &aInit->mChildOffset) ||
3871 !ReadIPDLParam(aReader, aActor, &aInit->mOriginAttributes) ||
3872 !ReadIPDLParam(aReader, aActor, &aInit->mRequestContextId) ||
3873 !ReadIPDLParam(aReader, aActor, &aInit->mSessionHistoryIndex) ||
3874 !ReadIPDLParam(aReader, aActor, &aInit->mSessionHistoryCount) ||
3875 !ReadIPDLParam(aReader, aActor, &aInit->mFields)) {
3876 return false;
3878 return true;
3881 template struct IPDLParamTraits<dom::BrowsingContext::BaseTransaction>;
3883 } // namespace ipc
3884 } // namespace mozilla