no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / docshell / base / BrowsingContext.cpp
blob141036a86c2a5b60fad60b3c81689388fd5583c9
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 || aType == Type::Content) {
426 // Non-root browsing-contexts inherit their status from its parent.
427 // Top-content either gets managed by the top chrome, or gets manually
428 // managed by the front-end (see ManuallyManagesActiveness). In any case
429 // we want to start off as inactive.
430 return ExplicitActiveStatus::None;
432 // Chrome starts as active.
433 return ExplicitActiveStatus::Active;
434 }();
436 fields.Get<IDX_FullZoom>() = parentBC ? parentBC->FullZoom() : 1.0f;
437 fields.Get<IDX_TextZoom>() = parentBC ? parentBC->TextZoom() : 1.0f;
439 bool allowContentRetargeting =
440 inherit ? inherit->GetAllowContentRetargetingOnChildren() : true;
441 fields.Get<IDX_AllowContentRetargeting>() = allowContentRetargeting;
442 fields.Get<IDX_AllowContentRetargetingOnChildren>() = allowContentRetargeting;
444 // Assume top allows fullscreen for its children unless otherwise stated.
445 // Subframes start with it false unless otherwise noted in SetEmbedderElement.
446 fields.Get<IDX_FullscreenAllowedByOwner>() = !aParent;
448 fields.Get<IDX_DefaultLoadFlags>() =
449 inherit ? inherit->GetDefaultLoadFlags() : nsIRequest::LOAD_NORMAL;
451 fields.Get<IDX_OrientationLock>() = mozilla::hal::ScreenOrientation::None;
453 fields.Get<IDX_UseGlobalHistory>() =
454 inherit ? inherit->GetUseGlobalHistory() : false;
456 fields.Get<IDX_UseErrorPages>() = true;
458 fields.Get<IDX_TouchEventsOverrideInternal>() = TouchEventsOverride::None;
460 fields.Get<IDX_AllowJavascript>() =
461 inherit ? inherit->GetAllowJavascript() : true;
463 fields.Get<IDX_IsPopupRequested>() = aIsPopupRequested;
465 if (!parentBC) {
466 fields.Get<IDX_ShouldDelayMediaFromStart>() =
467 StaticPrefs::media_block_autoplay_until_in_foreground();
470 RefPtr<BrowsingContext> context;
471 if (XRE_IsParentProcess()) {
472 context = new CanonicalBrowsingContext(parentWC, group, id,
473 /* aOwnerProcessId */ 0,
474 /* aEmbedderProcessId */ 0, aType,
475 std::move(fields));
476 } else {
477 context =
478 new BrowsingContext(parentWC, group, id, aType, std::move(fields));
481 context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent;
482 context->mCreatedDynamically = aCreatedDynamically;
483 if (inherit) {
484 context->mPrivateBrowsingId = inherit->mPrivateBrowsingId;
485 context->mUseRemoteTabs = inherit->mUseRemoteTabs;
486 context->mUseRemoteSubframes = inherit->mUseRemoteSubframes;
487 context->mOriginAttributes = inherit->mOriginAttributes;
490 nsCOMPtr<nsIRequestContextService> rcsvc =
491 net::RequestContextService::GetOrCreate();
492 if (rcsvc) {
493 nsCOMPtr<nsIRequestContext> requestContext;
494 nsresult rv = rcsvc->NewRequestContext(getter_AddRefs(requestContext));
495 if (NS_SUCCEEDED(rv) && requestContext) {
496 context->mRequestContextId = requestContext->GetID();
500 return context.forget();
503 already_AddRefed<BrowsingContext> BrowsingContext::CreateIndependent(
504 Type aType) {
505 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
506 "BCs created in the content process must be related to "
507 "some BrowserChild");
508 RefPtr<BrowsingContext> bc(
509 CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, false));
510 bc->mWindowless = bc->IsContent();
511 bc->mEmbeddedByThisProcess = true;
512 bc->EnsureAttached();
513 return bc.forget();
516 void BrowsingContext::EnsureAttached() {
517 if (!mEverAttached) {
518 Register(this);
520 // Attach the browsing context to the tree.
521 Attach(/* aFromIPC */ false, /* aOriginProcess */ nullptr);
525 /* static */
526 mozilla::ipc::IPCResult BrowsingContext::CreateFromIPC(
527 BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
528 ContentParent* aOriginProcess) {
529 MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
530 MOZ_DIAGNOSTIC_ASSERT(aGroup);
532 uint64_t originId = 0;
533 if (aOriginProcess) {
534 originId = aOriginProcess->ChildID();
535 aGroup->EnsureHostProcess(aOriginProcess);
538 MOZ_LOG(GetLog(), LogLevel::Debug,
539 ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
540 aInit.mId, originId));
542 RefPtr<WindowContext> parent = aInit.GetParent();
544 RefPtr<BrowsingContext> context;
545 if (XRE_IsParentProcess()) {
546 // If the new BrowsingContext has a parent, it is a sub-frame embedded in
547 // whatever process sent the message. If it doesn't, and is not windowless,
548 // it is a new window or tab, and will be embedded in the parent process.
549 uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0;
550 context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
551 embedderProcessId, Type::Content,
552 std::move(aInit.mFields));
553 } else {
554 context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
555 std::move(aInit.mFields));
558 context->mWindowless = aInit.mWindowless;
559 context->mCreatedDynamically = aInit.mCreatedDynamically;
560 context->mChildOffset = aInit.mChildOffset;
561 if (context->GetHasSessionHistory()) {
562 context->CreateChildSHistory();
563 if (mozilla::SessionHistoryInParent()) {
564 context->GetChildSessionHistory()->SetIndexAndLength(
565 aInit.mSessionHistoryIndex, aInit.mSessionHistoryCount, nsID());
569 // NOTE: Call through the `Set` methods for these values to ensure that any
570 // relevant process-local state is also updated.
571 context->SetOriginAttributes(aInit.mOriginAttributes);
572 context->SetRemoteTabs(aInit.mUseRemoteTabs);
573 context->SetRemoteSubframes(aInit.mUseRemoteSubframes);
574 context->mRequestContextId = aInit.mRequestContextId;
575 // NOTE: Private browsing ID is set by `SetOriginAttributes`.
577 Register(context);
579 return context->Attach(/* aFromIPC */ true, aOriginProcess);
582 BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
583 BrowsingContextGroup* aGroup,
584 uint64_t aBrowsingContextId, Type aType,
585 FieldValues&& aInit)
586 : mFields(std::move(aInit)),
587 mType(aType),
588 mBrowsingContextId(aBrowsingContextId),
589 mGroup(aGroup),
590 mParentWindow(aParentWindow),
591 mPrivateBrowsingId(0),
592 mEverAttached(false),
593 mIsInProcess(false),
594 mIsDiscarded(false),
595 mWindowless(false),
596 mDanglingRemoteOuterProxies(false),
597 mEmbeddedByThisProcess(false),
598 mUseRemoteTabs(false),
599 mUseRemoteSubframes(false),
600 mCreatedDynamically(false),
601 mIsInBFCache(false),
602 mCanExecuteScripts(true),
603 mChildOffset(0) {
604 MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup);
605 MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
606 MOZ_RELEASE_ASSERT(mGroup);
609 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
610 // XXX(nika): We should communicate that we are now an active BrowsingContext
611 // process to the parent & do other validation here.
612 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
613 MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
614 mDocShell = aDocShell;
615 mDanglingRemoteOuterProxies = !mIsInProcess;
616 mIsInProcess = true;
617 if (mChildSessionHistory) {
618 mChildSessionHistory->SetIsInProcess(true);
621 RecomputeCanExecuteScripts();
622 ClearCachedValuesOfLocations();
625 // This class implements a callback that will return the remote window proxy for
626 // mBrowsingContext in that compartment, if it has one. It also removes the
627 // proxy from the map, because the object will be transplanted into another kind
628 // of object.
629 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
630 : public js::CompartmentTransplantCallback {
631 public:
632 explicit CompartmentRemoteProxyTransplantCallback(
633 BrowsingContext* aBrowsingContext)
634 : mBrowsingContext(aBrowsingContext) {}
636 virtual JSObject* getObjectToTransplant(
637 JS::Compartment* compartment) override {
638 auto* priv = xpc::CompartmentPrivate::Get(compartment);
639 if (!priv) {
640 return nullptr;
643 auto& map = priv->GetRemoteProxyMap();
644 auto result = map.lookup(mBrowsingContext);
645 if (!result) {
646 return nullptr;
648 JSObject* resultObject = result->value();
649 map.remove(result);
651 return resultObject;
654 private:
655 BrowsingContext* mBrowsingContext;
658 void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies(
659 JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) {
660 if (!mDanglingRemoteOuterProxies) {
661 return;
663 mDanglingRemoteOuterProxies = false;
665 CompartmentRemoteProxyTransplantCallback cb(this);
666 js::RemapRemoteWindowProxies(aCx, &cb, aOuter);
669 bool BrowsingContext::IsActive() const {
670 const BrowsingContext* current = this;
671 do {
672 auto explicit_ = current->GetExplicitActive();
673 if (explicit_ != ExplicitActiveStatus::None) {
674 return explicit_ == ExplicitActiveStatus::Active;
676 if (mParentWindow && !mParentWindow->IsCurrent()) {
677 return false;
679 } while ((current = current->GetParent()));
681 return false;
684 bool BrowsingContext::GetIsActiveBrowserWindow() {
685 if (!XRE_IsParentProcess()) {
686 return Top()->GetIsActiveBrowserWindowInternal();
689 // chrome:// urls loaded in the parent won't receive
690 // their own activation so we defer to the top chrome
691 // Browsing Context when in the parent process.
692 return Canonical()
693 ->TopCrossChromeBoundary()
694 ->GetIsActiveBrowserWindowInternal();
697 void BrowsingContext::SetIsActiveBrowserWindow(bool aActive) {
698 Unused << SetIsActiveBrowserWindowInternal(aActive);
701 bool BrowsingContext::FullscreenAllowed() const {
702 for (auto* current = this; current; current = current->GetParent()) {
703 if (!current->GetFullscreenAllowedByOwner()) {
704 return false;
707 return true;
710 static bool OwnerAllowsFullscreen(const Element& aEmbedder) {
711 if (aEmbedder.IsXULElement()) {
712 return !aEmbedder.HasAttr(nsGkAtoms::disablefullscreen);
714 if (aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) {
715 // This is controlled by feature policy.
716 return true;
718 if (const auto* embed = HTMLEmbedElement::FromNode(aEmbedder)) {
719 return embed->AllowFullscreen();
721 return false;
724 void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
725 mEmbeddedByThisProcess = true;
727 // Update embedder-element-specific fields in a shared transaction.
728 // Don't do this when clearing our embedder, as we're being destroyed either
729 // way.
730 if (aEmbedder) {
731 Transaction txn;
732 txn.SetEmbedderElementType(Some(aEmbedder->LocalName()));
733 txn.SetEmbeddedInContentDocument(
734 aEmbedder->OwnerDoc()->IsContentDocument());
735 if (nsCOMPtr<nsPIDOMWindowInner> inner =
736 do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
737 txn.SetEmbedderInnerWindowId(inner->WindowID());
739 txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder));
740 if (XRE_IsParentProcess() && aEmbedder->IsXULElement() && IsTopContent()) {
741 nsAutoString messageManagerGroup;
742 aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
743 txn.SetMessageManagerGroup(messageManagerGroup);
744 txn.SetUseGlobalHistory(
745 !aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory));
746 if (!aEmbedder->HasAttr(nsGkAtoms::manualactiveness)) {
747 // We're active iff the parent cross-chrome-boundary is active. Note we
748 // can't just use this->Canonical()->GetParentCrossChromeBoundary here,
749 // since mEmbedderElement is still null at this point.
750 RefPtr bc = aEmbedder->OwnerDoc()->GetBrowsingContext();
751 const bool isActive = bc && bc->IsActive();
752 txn.SetExplicitActive(isActive ? ExplicitActiveStatus::Active
753 : ExplicitActiveStatus::Inactive);
754 if (auto* bp = Canonical()->GetBrowserParent()) {
755 bp->SetRenderLayers(isActive);
760 MOZ_ALWAYS_SUCCEEDS(txn.Commit(this));
763 if (XRE_IsParentProcess() && IsTopContent()) {
764 Canonical()->MaybeSetPermanentKey(aEmbedder);
767 mEmbedderElement = aEmbedder;
769 if (mEmbedderElement) {
770 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
771 obs->NotifyWhenScriptSafe(ToSupports(this),
772 "browsing-context-did-set-embedder", nullptr);
775 if (IsEmbedderTypeObjectOrEmbed()) {
776 Unused << SetSyntheticDocumentContainer(true);
781 bool BrowsingContext::IsEmbedderTypeObjectOrEmbed() {
782 if (const Maybe<nsString>& type = GetEmbedderElementType()) {
783 return nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type);
785 return false;
788 void BrowsingContext::Embed() {
789 if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
790 frame->BindToBrowsingContext(this);
794 mozilla::ipc::IPCResult BrowsingContext::Attach(bool aFromIPC,
795 ContentParent* aOriginProcess) {
796 MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
797 MOZ_DIAGNOSTIC_ASSERT_IF(aFromIPC, aOriginProcess || XRE_IsContentProcess());
798 mEverAttached = true;
800 if (MOZ_LOG_TEST(GetLog(), LogLevel::Debug)) {
801 nsAutoCString suffix;
802 mOriginAttributes.CreateSuffix(suffix);
803 MOZ_LOG(GetLog(), LogLevel::Debug,
804 ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64
805 " (private=%d, remote=%d, fission=%d, oa=%s)",
806 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
807 GetParent() ? GetParent()->Id() : 0, (int)mPrivateBrowsingId,
808 (int)mUseRemoteTabs, (int)mUseRemoteSubframes, suffix.get()));
811 MOZ_DIAGNOSTIC_ASSERT(mGroup);
812 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
814 if (mGroup->IsPotentiallyCrossOriginIsolated() !=
815 (Top()->GetOpenerPolicy() ==
816 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP)) {
817 MOZ_DIAGNOSTIC_ASSERT(aFromIPC);
818 if (aFromIPC) {
819 auto* actor = aOriginProcess
820 ? static_cast<mozilla::ipc::IProtocol*>(aOriginProcess)
821 : static_cast<mozilla::ipc::IProtocol*>(
822 ContentChild::GetSingleton());
823 return IPC_FAIL(
824 actor,
825 "Invalid CrossOriginIsolated state in BrowsingContext::Attach call");
826 } else {
827 MOZ_CRASH(
828 "Invalid CrossOriginIsolated state in BrowsingContext::Attach call");
832 AssertCoherentLoadContext();
834 // Add ourselves either to our parent or BrowsingContextGroup's child list.
835 // Important: We shouldn't return IPC_FAIL after this point, since the
836 // BrowsingContext will have already been added to the tree.
837 if (mParentWindow) {
838 if (!aFromIPC) {
839 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow->IsDiscarded(),
840 "local attach in discarded window");
841 MOZ_DIAGNOSTIC_ASSERT(!GetParent()->IsDiscarded(),
842 "local attach call in discarded bc");
843 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild(),
844 "local attach call with oop parent window");
845 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild()->CanSend(),
846 "local attach call with dead parent window");
848 mChildOffset =
849 mCreatedDynamically ? -1 : mParentWindow->Children().Length();
850 mParentWindow->AppendChildBrowsingContext(this);
851 RecomputeCanExecuteScripts();
852 } else {
853 mGroup->Toplevels().AppendElement(this);
856 if (GetIsPopupSpam()) {
857 PopupBlocker::RegisterOpenPopupSpam();
860 if (IsTop() && GetHasSessionHistory() && !mChildSessionHistory) {
861 CreateChildSHistory();
864 // Why the context is being attached. This will always be "attach" in the
865 // content process, but may be "replace" if it's known the context being
866 // replaced in the parent process.
867 const char16_t* why = u"attach";
869 if (XRE_IsContentProcess() && !aFromIPC) {
870 // Send attach to our parent if we need to.
871 ContentChild::GetSingleton()->SendCreateBrowsingContext(
872 mGroup->Id(), GetIPCInitializer());
873 } else if (XRE_IsParentProcess()) {
874 // If this window was created as a subframe by a content process, it must be
875 // being hosted within the same BrowserParent as its mParentWindow.
876 // Toplevel BrowsingContexts created by content have their BrowserParent
877 // configured during `RecvConstructPopupBrowser`.
878 if (mParentWindow && aOriginProcess) {
879 MOZ_DIAGNOSTIC_ASSERT(
880 mParentWindow->Canonical()->GetContentParent() == aOriginProcess,
881 "Creator process isn't the same as our embedder?");
882 Canonical()->SetCurrentBrowserParent(
883 mParentWindow->Canonical()->GetBrowserParent());
886 mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) {
887 MOZ_DIAGNOSTIC_ASSERT(IsContent(),
888 "chrome BCG cannot be synced to content process");
889 if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID())) {
890 Unused << aParent->SendCreateBrowsingContext(mGroup->Id(),
891 GetIPCInitializer());
895 if (IsTop() && IsContent() && Canonical()->GetWebProgress()) {
896 why = u"replace";
899 // We want to create a BrowsingContextWebProgress for all content
900 // BrowsingContexts.
901 if (IsContent() && !Canonical()->mWebProgress) {
902 Canonical()->mWebProgress = new BrowsingContextWebProgress(Canonical());
906 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
907 obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached",
908 why);
911 if (XRE_IsParentProcess()) {
912 Canonical()->CanonicalAttach();
914 return IPC_OK();
917 void BrowsingContext::Detach(bool aFromIPC) {
918 MOZ_LOG(GetLog(), LogLevel::Debug,
919 ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
920 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
921 GetParent() ? GetParent()->Id() : 0));
923 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
924 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
926 if (XRE_IsParentProcess()) {
927 Canonical()->AddPendingDiscard();
929 auto callListeners =
930 MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] {
931 for (const auto& listener : listeners) {
932 listener(id);
934 if (XRE_IsParentProcess()) {
935 Canonical()->RemovePendingDiscard();
939 nsCOMPtr<nsIRequestContextService> rcsvc =
940 net::RequestContextService::GetOrCreate();
941 if (rcsvc) {
942 rcsvc->RemoveRequestContext(GetRequestContextId());
945 // This will only ever be null if the cycle-collector has unlinked us. Don't
946 // try to detach ourselves in that case.
947 if (NS_WARN_IF(!mGroup)) {
948 MOZ_ASSERT_UNREACHABLE();
949 return;
952 if (mParentWindow) {
953 mParentWindow->RemoveChildBrowsingContext(this);
954 } else {
955 mGroup->Toplevels().RemoveElement(this);
958 if (XRE_IsParentProcess()) {
959 RefPtr<CanonicalBrowsingContext> self{Canonical()};
960 Group()->EachParent([&](ContentParent* aParent) {
961 // Only the embedder process is allowed to initiate a BrowsingContext
962 // detach, so if we've gotten here, the host process already knows we've
963 // been detached, and there's no need to tell it again.
965 // If the owner process is not the same as the embedder process, its
966 // BrowsingContext will be detached when its nsWebBrowser instance is
967 // destroyed.
968 bool doDiscard = !Canonical()->IsEmbeddedInProcess(aParent->ChildID()) &&
969 !Canonical()->IsOwnedByProcess(aParent->ChildID());
971 // Hold a strong reference to ourself, and keep our BrowsingContextGroup
972 // alive, until the responses comes back to ensure we don't die while
973 // messages relating to this context are in-flight.
975 // When the callback is called, the keepalive on our group will be
976 // destroyed, and the reference to the BrowsingContext will be dropped,
977 // which may cause it to be fully destroyed.
978 mGroup->AddKeepAlive();
979 self->AddPendingDiscard();
980 auto callback = [self](auto) {
981 self->mGroup->RemoveKeepAlive();
982 self->RemovePendingDiscard();
985 aParent->SendDiscardBrowsingContext(this, doDiscard, callback, callback);
987 } else {
988 // Hold a strong reference to ourself until the responses come back to
989 // ensure the BrowsingContext isn't cleaned up before the parent process
990 // acknowledges the discard request.
991 auto callback = [self = RefPtr{this}](auto) {};
992 ContentChild::GetSingleton()->SendDiscardBrowsingContext(
993 this, !aFromIPC, callback, callback);
996 mGroup->Unregister(this);
997 UnregisterBrowserId(this);
998 mIsDiscarded = true;
1000 if (XRE_IsParentProcess()) {
1001 nsFocusManager* fm = nsFocusManager::GetFocusManager();
1002 if (fm) {
1003 fm->BrowsingContextDetached(this);
1007 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1008 // Why the context is being discarded. This will always be "discard" in the
1009 // content process, but may be "replace" if it's known the context being
1010 // replaced in the parent process.
1011 const char16_t* why = u"discard";
1012 if (XRE_IsParentProcess() && IsTop() && !Canonical()->GetWebProgress()) {
1013 why = u"replace";
1015 obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", why);
1018 // NOTE: Doesn't use SetClosed, as it will be set in all processes
1019 // automatically by calls to Detach()
1020 mFields.SetWithoutSyncing<IDX_Closed>(true);
1022 if (GetIsPopupSpam()) {
1023 PopupBlocker::UnregisterOpenPopupSpam();
1024 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1025 // automatically.
1026 mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1029 AssertOriginAttributesMatchPrivateBrowsing();
1031 if (XRE_IsParentProcess()) {
1032 Canonical()->CanonicalDiscard();
1036 void BrowsingContext::AddDiscardListener(
1037 std::function<void(uint64_t)>&& aListener) {
1038 if (mIsDiscarded) {
1039 aListener(Id());
1040 return;
1042 mDiscardListeners.AppendElement(std::move(aListener));
1045 void BrowsingContext::PrepareForProcessChange() {
1046 MOZ_LOG(GetLog(), LogLevel::Debug,
1047 ("%s: Preparing 0x%08" PRIx64 " for a process change",
1048 XRE_IsParentProcess() ? "Parent" : "Child", Id()));
1050 MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
1051 MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
1053 mIsInProcess = false;
1054 mUserGestureStart = TimeStamp();
1056 ClearCachedValuesOfLocations();
1058 // NOTE: For now, clear our nsDocShell reference, as we're primarily in a
1059 // different process now. This may need to change in the future with
1060 // Cross-Process BFCache.
1061 mDocShell = nullptr;
1062 if (mChildSessionHistory) {
1063 // This can be removed once session history is stored exclusively in the
1064 // parent process.
1065 mChildSessionHistory->SetIsInProcess(false);
1068 if (!mWindowProxy) {
1069 return;
1072 // We have to go through mWindowProxy rather than calling GetDOMWindow() on
1073 // mDocShell because the mDocshell reference gets cleared immediately after
1074 // the window is closed.
1075 nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy);
1076 MOZ_ASSERT(!mWindowProxy);
1079 bool BrowsingContext::IsTargetable() const {
1080 return !GetClosed() && AncestorsAreCurrent();
1083 void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
1084 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->Group() == Group());
1085 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->mType == mType);
1087 MOZ_ALWAYS_SUCCEEDS(SetOpenerId(aOpener ? aOpener->Id() : 0));
1089 if (IsChrome() && IsTop() && aOpener) {
1090 // Inherit color scheme overrides from parent window. This is to inherit the
1091 // color scheme of dark themed PBM windows in dialogs opened by such
1092 // windows.
1093 auto openerOverride = aOpener->Top()->PrefersColorSchemeOverride();
1094 if (openerOverride != PrefersColorSchemeOverride()) {
1095 MOZ_ALWAYS_SUCCEEDS(SetPrefersColorSchemeOverride(openerOverride));
1100 bool BrowsingContext::HasOpener() const {
1101 return sBrowsingContexts->Contains(GetOpenerId());
1104 bool BrowsingContext::AncestorsAreCurrent() const {
1105 const BrowsingContext* bc = this;
1106 while (true) {
1107 if (bc->IsDiscarded()) {
1108 return false;
1111 if (WindowContext* wc = bc->GetParentWindowContext()) {
1112 if (!wc->IsCurrent() || wc->IsDiscarded()) {
1113 return false;
1116 bc = wc->GetBrowsingContext();
1117 } else {
1118 return true;
1123 bool BrowsingContext::IsInBFCache() const {
1124 if (mozilla::SessionHistoryInParent()) {
1125 return mIsInBFCache;
1127 return mParentWindow &&
1128 mParentWindow->TopWindowContext()->GetWindowStateSaved();
1131 Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
1132 if (WindowContext* current = mCurrentWindowContext) {
1133 return current->Children();
1135 return Span<RefPtr<BrowsingContext>>();
1138 void BrowsingContext::GetChildren(
1139 nsTArray<RefPtr<BrowsingContext>>& aChildren) {
1140 aChildren.AppendElements(Children());
1143 Span<RefPtr<BrowsingContext>> BrowsingContext::NonSyntheticChildren() const {
1144 if (WindowContext* current = mCurrentWindowContext) {
1145 return current->NonSyntheticChildren();
1147 return Span<RefPtr<BrowsingContext>>();
1150 void BrowsingContext::GetWindowContexts(
1151 nsTArray<RefPtr<WindowContext>>& aWindows) {
1152 aWindows.AppendElements(mWindowContexts);
1155 void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
1156 MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
1157 "WindowContext already registered!");
1158 MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
1160 mWindowContexts.AppendElement(aWindow);
1162 // If the newly registered WindowContext is for our current inner window ID,
1163 // re-run the `DidSet` handler to re-establish the relationship.
1164 if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
1165 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1166 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow);
1170 void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
1171 MOZ_ASSERT(mWindowContexts.Contains(aWindow),
1172 "WindowContext not registered!");
1173 mWindowContexts.RemoveElement(aWindow);
1175 // If our currently active window was unregistered, clear our reference to it.
1176 if (aWindow == mCurrentWindowContext) {
1177 // Re-read our `CurrentInnerWindowId` value and use it to set
1178 // `mCurrentWindowContext`. As `aWindow` is now unregistered and discarded,
1179 // we won't find it, and the value will be cleared back to `nullptr`.
1180 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1181 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == nullptr);
1185 void BrowsingContext::PreOrderWalkVoid(
1186 const std::function<void(BrowsingContext*)>& aCallback) {
1187 aCallback(this);
1189 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1190 children.AppendElements(Children());
1192 for (auto& child : children) {
1193 child->PreOrderWalkVoid(aCallback);
1197 BrowsingContext::WalkFlag BrowsingContext::PreOrderWalkFlag(
1198 const std::function<WalkFlag(BrowsingContext*)>& aCallback) {
1199 switch (aCallback(this)) {
1200 case WalkFlag::Skip:
1201 return WalkFlag::Next;
1202 case WalkFlag::Stop:
1203 return WalkFlag::Stop;
1204 case WalkFlag::Next:
1205 default:
1206 break;
1209 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1210 children.AppendElements(Children());
1212 for (auto& child : children) {
1213 switch (child->PreOrderWalkFlag(aCallback)) {
1214 case WalkFlag::Stop:
1215 return WalkFlag::Stop;
1216 default:
1217 break;
1221 return WalkFlag::Next;
1224 void BrowsingContext::PostOrderWalk(
1225 const std::function<void(BrowsingContext*)>& aCallback) {
1226 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1227 children.AppendElements(Children());
1229 for (auto& child : children) {
1230 child->PostOrderWalk(aCallback);
1233 aCallback(this);
1236 void BrowsingContext::GetAllBrowsingContextsInSubtree(
1237 nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) {
1238 PreOrderWalk([&](BrowsingContext* aContext) {
1239 aBrowsingContexts.AppendElement(aContext);
1243 BrowsingContext* BrowsingContext::FindChildWithName(
1244 const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
1245 if (aName.IsEmpty()) {
1246 // You can't find a browsing context with the empty name.
1247 return nullptr;
1250 for (BrowsingContext* child : NonSyntheticChildren()) {
1251 if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) &&
1252 child->IsTargetable()) {
1253 return child;
1257 return nullptr;
1260 BrowsingContext* BrowsingContext::FindWithSpecialName(
1261 const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
1262 // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
1263 // browsing context pointed to by a special name is active. Should
1264 // it be? See Bug 1527913.
1265 if (aName.LowerCaseEqualsLiteral("_self")) {
1266 return this;
1269 if (aName.LowerCaseEqualsLiteral("_parent")) {
1270 if (BrowsingContext* parent = GetParent()) {
1271 return aRequestingWindow.CanNavigate(parent) ? parent : nullptr;
1273 return this;
1276 if (aName.LowerCaseEqualsLiteral("_top")) {
1277 BrowsingContext* top = Top();
1279 return aRequestingWindow.CanNavigate(top) ? top : nullptr;
1282 return nullptr;
1285 BrowsingContext* BrowsingContext::FindWithNameInSubtree(
1286 const nsAString& aName, WindowGlobalChild* aRequestingWindow) {
1287 MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
1289 if (NameEquals(aName) &&
1290 (!aRequestingWindow || aRequestingWindow->CanNavigate(this)) &&
1291 IsTargetable()) {
1292 return this;
1295 for (BrowsingContext* child : NonSyntheticChildren()) {
1296 if (BrowsingContext* found =
1297 child->FindWithNameInSubtree(aName, aRequestingWindow)) {
1298 return found;
1302 return nullptr;
1305 bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
1306 // If no target then not sandboxed.
1307 if (!aTarget) {
1308 return false;
1311 // We cannot be sandboxed from ourselves.
1312 if (aTarget == this) {
1313 return false;
1316 // Default the sandbox flags to our flags, so that if we can't retrieve the
1317 // active document, we will still enforce our own.
1318 uint32_t sandboxFlags = GetSandboxFlags();
1319 if (mDocShell) {
1320 if (RefPtr<Document> doc = mDocShell->GetExtantDocument()) {
1321 sandboxFlags = doc->GetSandboxFlags();
1325 // If no flags, we are not sandboxed at all.
1326 if (!sandboxFlags) {
1327 return false;
1330 // If aTarget has an ancestor, it is not top level.
1331 if (RefPtr<BrowsingContext> ancestorOfTarget = aTarget->GetParent()) {
1332 do {
1333 // We are not sandboxed if we are an ancestor of target.
1334 if (ancestorOfTarget == this) {
1335 return false;
1337 ancestorOfTarget = ancestorOfTarget->GetParent();
1338 } while (ancestorOfTarget);
1340 // Otherwise, we are sandboxed from aTarget.
1341 return true;
1344 // aTarget is top level, are we the "one permitted sandboxed
1345 // navigator", i.e. did we open aTarget?
1346 if (aTarget->GetOnePermittedSandboxedNavigatorId() == Id()) {
1347 return false;
1350 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
1351 // from our top.
1352 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) && aTarget == Top()) {
1353 return false;
1356 // If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not
1357 // sandboxed from our top if we have user interaction. We assume there is a
1358 // valid transient user gesture interaction if this check happens in the
1359 // target process given that we have checked in the triggering process
1360 // already.
1361 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) &&
1362 mCurrentWindowContext &&
1363 (!mCurrentWindowContext->IsInProcess() ||
1364 mCurrentWindowContext->HasValidTransientUserGestureActivation()) &&
1365 aTarget == Top()) {
1366 return false;
1369 // Otherwise, we are sandboxed from aTarget.
1370 return true;
1373 RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
1374 RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager;
1375 if (!manager) {
1376 manager = new SessionStorageManager(this);
1378 return manager;
1381 bool BrowsingContext::CrossOriginIsolated() {
1382 MOZ_ASSERT(NS_IsMainThread());
1384 return StaticPrefs::
1385 dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup() &&
1386 Top()->GetOpenerPolicy() ==
1387 nsILoadInfo::
1388 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
1389 XRE_IsContentProcess() &&
1390 StringBeginsWith(ContentChild::GetSingleton()->GetRemoteType(),
1391 WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
1394 void BrowsingContext::SetTriggeringAndInheritPrincipals(
1395 nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
1396 uint64_t aLoadIdentifier) {
1397 mTriggeringPrincipal = Some(
1398 PrincipalWithLoadIdentifierTuple(aTriggeringPrincipal, aLoadIdentifier));
1399 if (aPrincipalToInherit) {
1400 mPrincipalToInherit = Some(
1401 PrincipalWithLoadIdentifierTuple(aPrincipalToInherit, aLoadIdentifier));
1405 std::tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>>
1406 BrowsingContext::GetTriggeringAndInheritPrincipalsForCurrentLoad() {
1407 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
1408 GetSavedPrincipal(mTriggeringPrincipal);
1409 nsCOMPtr<nsIPrincipal> principalToInherit =
1410 GetSavedPrincipal(mPrincipalToInherit);
1411 return std::make_tuple(triggeringPrincipal, principalToInherit);
1414 nsIPrincipal* BrowsingContext::GetSavedPrincipal(
1415 Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple) {
1416 if (aPrincipalTuple) {
1417 nsCOMPtr<nsIPrincipal> principal;
1418 uint64_t loadIdentifier;
1419 std::tie(principal, loadIdentifier) = *aPrincipalTuple;
1420 // We want to return a principal only if the load identifier for it
1421 // matches the current one for this BC.
1422 if (auto current = GetCurrentLoadIdentifier();
1423 current && *current == loadIdentifier) {
1424 return principal;
1427 return nullptr;
1430 BrowsingContext::~BrowsingContext() {
1431 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow ||
1432 !mParentWindow->mChildren.Contains(this));
1433 MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
1435 mDeprioritizedLoadRunner.clear();
1437 if (sBrowsingContexts) {
1438 sBrowsingContexts->Remove(Id());
1440 UnregisterBrowserId(this);
1442 ClearCachedValuesOfLocations();
1443 mLocations.clear();
1446 /* static */
1447 void BrowsingContext::DiscardFromContentParent(ContentParent* aCP) {
1448 MOZ_ASSERT(XRE_IsParentProcess());
1450 if (sBrowsingContexts) {
1451 AutoTArray<RefPtr<BrowsingContext>, 8> toDiscard;
1452 for (const auto& data : sBrowsingContexts->Values()) {
1453 auto* bc = data->Canonical();
1454 if (!bc->IsDiscarded() && bc->IsEmbeddedInProcess(aCP->ChildID())) {
1455 toDiscard.AppendElement(bc);
1459 for (BrowsingContext* bc : toDiscard) {
1460 bc->Detach(/* aFromIPC */ true);
1465 nsISupports* BrowsingContext::GetParentObject() const {
1466 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1469 JSObject* BrowsingContext::WrapObject(JSContext* aCx,
1470 JS::Handle<JSObject*> aGivenProto) {
1471 return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
1474 bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
1475 JSStructuredCloneWriter* aWriter,
1476 StructuredCloneHolder* aHolder) {
1477 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
1478 return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
1479 JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
1482 /* static */
1483 JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
1484 JSStructuredCloneReader* aReader,
1485 StructuredCloneHolder* aHolder) {
1486 uint32_t idLow = 0;
1487 uint32_t idHigh = 0;
1488 if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
1489 return nullptr;
1491 uint64_t id = uint64_t(idHigh) << 32 | idLow;
1493 // Note: Do this check after reading our ID data. Returning null will abort
1494 // the decode operation anyway, but we should at least be as safe as possible.
1495 if (NS_WARN_IF(!NS_IsMainThread())) {
1496 MOZ_DIAGNOSTIC_ASSERT(false,
1497 "We shouldn't be trying to decode a BrowsingContext "
1498 "on a background thread.");
1499 return nullptr;
1502 JS::Rooted<JS::Value> val(aCx, JS::NullValue());
1503 // We'll get rooting hazard errors from the RefPtr destructor if it isn't
1504 // destroyed before we try to return a raw JSObject*, so create it in its own
1505 // scope.
1506 if (RefPtr<BrowsingContext> context = Get(id)) {
1507 if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
1508 return nullptr;
1511 return val.toObjectOrNull();
1514 bool BrowsingContext::CanSetOriginAttributes() {
1515 // A discarded BrowsingContext has already been destroyed, and cannot modify
1516 // its OriginAttributes.
1517 if (NS_WARN_IF(IsDiscarded())) {
1518 return false;
1521 // Before attaching is the safest time to set OriginAttributes, and the only
1522 // allowed time for content BrowsingContexts.
1523 if (!EverAttached()) {
1524 return true;
1527 // Attached content BrowsingContexts may have been synced to other processes.
1528 if (NS_WARN_IF(IsContent())) {
1529 MOZ_CRASH();
1530 return false;
1532 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
1534 // Cannot set OriginAttributes after we've created our child BrowsingContext.
1535 if (NS_WARN_IF(!Children().IsEmpty())) {
1536 return false;
1539 // Only allow setting OriginAttributes if we have no associated document, or
1540 // the document is still `about:blank`.
1541 // TODO: Bug 1273058 - should have no document when setting origin attributes.
1542 if (WindowGlobalParent* window = Canonical()->GetCurrentWindowGlobal()) {
1543 if (nsIURI* uri = window->GetDocumentURI()) {
1544 MOZ_ASSERT(NS_IsAboutBlank(uri));
1545 return NS_IsAboutBlank(uri);
1548 return true;
1551 Nullable<WindowProxyHolder> BrowsingContext::GetAssociatedWindow() {
1552 // nsILoadContext usually only returns same-process windows,
1553 // so we intentionally return nullptr if this BC is out of
1554 // process.
1555 if (IsInProcess()) {
1556 return WindowProxyHolder(this);
1558 return nullptr;
1561 Nullable<WindowProxyHolder> BrowsingContext::GetTopWindow() {
1562 return Top()->GetAssociatedWindow();
1565 Element* BrowsingContext::GetTopFrameElement() {
1566 return Top()->GetEmbedderElement();
1569 void BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing,
1570 ErrorResult& aError) {
1571 nsresult rv = SetUsePrivateBrowsing(aUsePrivateBrowsing);
1572 if (NS_FAILED(rv)) {
1573 aError.Throw(rv);
1577 void BrowsingContext::SetUseTrackingProtectionWebIDL(
1578 bool aUseTrackingProtection, ErrorResult& aRv) {
1579 SetForceEnableTrackingProtection(aUseTrackingProtection, aRv);
1582 void BrowsingContext::GetOriginAttributes(JSContext* aCx,
1583 JS::MutableHandle<JS::Value> aVal,
1584 ErrorResult& aError) {
1585 AssertOriginAttributesMatchPrivateBrowsing();
1587 if (!ToJSValue(aCx, mOriginAttributes, aVal)) {
1588 aError.NoteJSContextException(aCx);
1592 NS_IMETHODIMP BrowsingContext::GetAssociatedWindow(
1593 mozIDOMWindowProxy** aAssociatedWindow) {
1594 nsCOMPtr<mozIDOMWindowProxy> win = GetDOMWindow();
1595 win.forget(aAssociatedWindow);
1596 return NS_OK;
1599 NS_IMETHODIMP BrowsingContext::GetTopWindow(mozIDOMWindowProxy** aTopWindow) {
1600 return Top()->GetAssociatedWindow(aTopWindow);
1603 NS_IMETHODIMP BrowsingContext::GetTopFrameElement(Element** aTopFrameElement) {
1604 RefPtr<Element> topFrameElement = GetTopFrameElement();
1605 topFrameElement.forget(aTopFrameElement);
1606 return NS_OK;
1609 NS_IMETHODIMP BrowsingContext::GetIsContent(bool* aIsContent) {
1610 *aIsContent = IsContent();
1611 return NS_OK;
1614 NS_IMETHODIMP BrowsingContext::GetUsePrivateBrowsing(
1615 bool* aUsePrivateBrowsing) {
1616 *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
1617 return NS_OK;
1620 NS_IMETHODIMP BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1621 if (!CanSetOriginAttributes()) {
1622 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1623 if (changed) {
1624 NS_WARNING("SetUsePrivateBrowsing when !CanSetOriginAttributes()");
1626 return changed ? NS_ERROR_FAILURE : NS_OK;
1629 return SetPrivateBrowsing(aUsePrivateBrowsing);
1632 NS_IMETHODIMP BrowsingContext::SetPrivateBrowsing(bool aPrivateBrowsing) {
1633 if (!CanSetOriginAttributes()) {
1634 NS_WARNING("Attempt to set PrivateBrowsing when !CanSetOriginAttributes");
1635 return NS_ERROR_FAILURE;
1638 bool changed = aPrivateBrowsing != (mPrivateBrowsingId > 0);
1639 if (changed) {
1640 mPrivateBrowsingId = aPrivateBrowsing ? 1 : 0;
1641 if (IsContent()) {
1642 mOriginAttributes.SyncAttributesWithPrivateBrowsing(aPrivateBrowsing);
1645 if (XRE_IsParentProcess()) {
1646 Canonical()->AdjustPrivateBrowsingCount(aPrivateBrowsing);
1649 AssertOriginAttributesMatchPrivateBrowsing();
1651 if (changed && mDocShell) {
1652 nsDocShell::Cast(mDocShell)->NotifyPrivateBrowsingChanged();
1654 return NS_OK;
1657 NS_IMETHODIMP BrowsingContext::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1658 *aUseRemoteTabs = mUseRemoteTabs;
1659 return NS_OK;
1662 NS_IMETHODIMP BrowsingContext::SetRemoteTabs(bool aUseRemoteTabs) {
1663 if (!CanSetOriginAttributes()) {
1664 NS_WARNING("Attempt to set RemoteTabs when !CanSetOriginAttributes");
1665 return NS_ERROR_FAILURE;
1668 static bool annotated = false;
1669 if (aUseRemoteTabs && !annotated) {
1670 annotated = true;
1671 CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::DOMIPCEnabled,
1672 true);
1675 // Don't allow non-remote tabs with remote subframes.
1676 if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
1677 return NS_ERROR_UNEXPECTED;
1680 mUseRemoteTabs = aUseRemoteTabs;
1681 return NS_OK;
1684 NS_IMETHODIMP BrowsingContext::GetUseRemoteSubframes(
1685 bool* aUseRemoteSubframes) {
1686 *aUseRemoteSubframes = mUseRemoteSubframes;
1687 return NS_OK;
1690 NS_IMETHODIMP BrowsingContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
1691 if (!CanSetOriginAttributes()) {
1692 NS_WARNING("Attempt to set RemoteSubframes when !CanSetOriginAttributes");
1693 return NS_ERROR_FAILURE;
1696 static bool annotated = false;
1697 if (aUseRemoteSubframes && !annotated) {
1698 annotated = true;
1699 CrashReporter::AnnotateCrashReport(
1700 CrashReporter::Annotation::DOMFissionEnabled, true);
1703 // Don't allow non-remote tabs with remote subframes.
1704 if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
1705 return NS_ERROR_UNEXPECTED;
1708 mUseRemoteSubframes = aUseRemoteSubframes;
1709 return NS_OK;
1712 NS_IMETHODIMP BrowsingContext::GetUseTrackingProtection(
1713 bool* aUseTrackingProtection) {
1714 *aUseTrackingProtection = false;
1716 if (GetForceEnableTrackingProtection() ||
1717 StaticPrefs::privacy_trackingprotection_enabled() ||
1718 (UsePrivateBrowsing() &&
1719 StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
1720 *aUseTrackingProtection = true;
1721 return NS_OK;
1724 if (GetParent()) {
1725 return GetParent()->GetUseTrackingProtection(aUseTrackingProtection);
1728 return NS_OK;
1731 NS_IMETHODIMP BrowsingContext::SetUseTrackingProtection(
1732 bool aUseTrackingProtection) {
1733 return SetForceEnableTrackingProtection(aUseTrackingProtection);
1736 NS_IMETHODIMP BrowsingContext::GetScriptableOriginAttributes(
1737 JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
1738 AssertOriginAttributesMatchPrivateBrowsing();
1740 bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
1741 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1742 return NS_OK;
1745 NS_IMETHODIMP_(void)
1746 BrowsingContext::GetOriginAttributes(OriginAttributes& aAttrs) {
1747 aAttrs = mOriginAttributes;
1748 AssertOriginAttributesMatchPrivateBrowsing();
1751 nsresult BrowsingContext::SetOriginAttributes(const OriginAttributes& aAttrs) {
1752 if (!CanSetOriginAttributes()) {
1753 NS_WARNING("Attempt to set OriginAttributes when !CanSetOriginAttributes");
1754 return NS_ERROR_FAILURE;
1757 AssertOriginAttributesMatchPrivateBrowsing();
1758 mOriginAttributes = aAttrs;
1760 bool isPrivate = mOriginAttributes.mPrivateBrowsingId !=
1761 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1762 // Chrome Browsing Context can not contain OriginAttributes.mPrivateBrowsingId
1763 if (IsChrome() && isPrivate) {
1764 mOriginAttributes.mPrivateBrowsingId =
1765 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1767 SetPrivateBrowsing(isPrivate);
1768 AssertOriginAttributesMatchPrivateBrowsing();
1770 return NS_OK;
1773 void BrowsingContext::AssertCoherentLoadContext() {
1774 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1775 // LoadContext should generally match our opener or parent.
1776 if (IsContent()) {
1777 if (RefPtr<BrowsingContext> opener = GetOpener()) {
1778 MOZ_DIAGNOSTIC_ASSERT(opener->mType == mType);
1779 MOZ_DIAGNOSTIC_ASSERT(opener->mGroup == mGroup);
1780 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteTabs == mUseRemoteTabs);
1781 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteSubframes == mUseRemoteSubframes);
1782 MOZ_DIAGNOSTIC_ASSERT(opener->mPrivateBrowsingId == mPrivateBrowsingId);
1783 MOZ_DIAGNOSTIC_ASSERT(
1784 opener->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1787 if (RefPtr<BrowsingContext> parent = GetParent()) {
1788 MOZ_DIAGNOSTIC_ASSERT(parent->mType == mType);
1789 MOZ_DIAGNOSTIC_ASSERT(parent->mGroup == mGroup);
1790 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteTabs == mUseRemoteTabs);
1791 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteSubframes == mUseRemoteSubframes);
1792 MOZ_DIAGNOSTIC_ASSERT(parent->mPrivateBrowsingId == mPrivateBrowsingId);
1793 MOZ_DIAGNOSTIC_ASSERT(
1794 parent->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1797 // UseRemoteSubframes and UseRemoteTabs must match.
1798 MOZ_DIAGNOSTIC_ASSERT(
1799 !mUseRemoteSubframes || mUseRemoteTabs,
1800 "Cannot set useRemoteSubframes without also setting useRemoteTabs");
1802 // Double-check OriginAttributes/Private Browsing
1803 AssertOriginAttributesMatchPrivateBrowsing();
1804 #endif
1807 void BrowsingContext::AssertOriginAttributesMatchPrivateBrowsing() {
1808 // Chrome browsing contexts must not have a private browsing OriginAttribute
1809 // Content browsing contexts must maintain the equality:
1810 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
1811 if (IsChrome()) {
1812 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
1813 } else {
1814 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
1815 mPrivateBrowsingId);
1819 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext)
1820 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1821 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
1822 NS_INTERFACE_MAP_ENTRY(nsISupports)
1823 NS_INTERFACE_MAP_END
1825 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
1827 NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext)
1828 NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext)
1830 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext)
1831 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1832 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1834 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
1835 if (sBrowsingContexts) {
1836 sBrowsingContexts->Remove(tmp->Id());
1838 UnregisterBrowserId(tmp);
1840 if (tmp->GetIsPopupSpam()) {
1841 PopupBlocker::UnregisterOpenPopupSpam();
1842 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1843 // automatically.
1844 tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1847 NS_IMPL_CYCLE_COLLECTION_UNLINK(
1848 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1849 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1850 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1851 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1853 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
1854 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
1855 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1856 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1857 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1859 static bool IsCertainlyAliveForCC(BrowsingContext* aContext) {
1860 return aContext->HasKnownLiveWrapper() ||
1861 (AppShutdown::GetCurrentShutdownPhase() ==
1862 ShutdownPhase::NotInShutdown &&
1863 aContext->EverAttached() && !aContext->IsDiscarded());
1866 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(BrowsingContext)
1867 if (IsCertainlyAliveForCC(tmp)) {
1868 if (tmp->PreservingWrapper()) {
1869 tmp->MarkWrapperLive();
1871 return true;
1873 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1875 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(BrowsingContext)
1876 return IsCertainlyAliveForCC(tmp) && tmp->HasNothingToTrace(tmp);
1877 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1879 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
1880 return IsCertainlyAliveForCC(tmp);
1881 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1883 class RemoteLocationProxy
1884 : public RemoteObjectProxy<BrowsingContext::LocationProxy,
1885 Location_Binding::sCrossOriginProperties> {
1886 public:
1887 typedef RemoteObjectProxy Base;
1889 constexpr RemoteLocationProxy()
1890 : RemoteObjectProxy(prototypes::id::Location) {}
1892 void NoteChildren(JSObject* aProxy,
1893 nsCycleCollectionTraversalCallback& aCb) const override {
1894 auto location =
1895 static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
1896 CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
1897 "JS::GetPrivate(obj)->GetBrowsingContext()");
1901 static const RemoteLocationProxy sSingleton;
1903 // Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
1904 // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
1905 // malloc.
1906 template <>
1907 const JSClass RemoteLocationProxy::Base::sClass =
1908 PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
1910 void BrowsingContext::Location(JSContext* aCx,
1911 JS::MutableHandle<JSObject*> aLocation,
1912 ErrorResult& aError) {
1913 aError.MightThrowJSException();
1914 sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
1915 aLocation);
1916 if (!aLocation) {
1917 aError.StealExceptionFromJSContext(aCx);
1921 bool BrowsingContext::RemoveRootFromBFCacheSync() {
1922 if (WindowContext* wc = GetParentWindowContext()) {
1923 if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) {
1924 return doc->RemoveFromBFCacheSync();
1927 return false;
1930 nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) {
1931 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1932 if (sourceBC.IsNull()) {
1933 return NS_OK;
1936 // We might be called after the source BC has been discarded, but before we've
1937 // destroyed our in-process instance of the BrowsingContext object in some
1938 // situations (e.g. after creating a new pop-up with window.open while the
1939 // window is being closed). In these situations we want to still perform the
1940 // sandboxing check against our in-process copy. If we've forgotten about the
1941 // context already, assume it is sanboxed. (bug 1643450)
1942 BrowsingContext* bc = sourceBC.GetMaybeDiscarded();
1943 if (!bc || bc->IsSandboxedFrom(this)) {
1944 return NS_ERROR_DOM_SECURITY_ERR;
1946 return NS_OK;
1949 nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
1950 bool aSetNavigating) {
1951 // Per spec, most load attempts are silently ignored when a BrowsingContext is
1952 // null (which in our code corresponds to discarded), so we simply fail
1953 // silently in those cases. Regardless, we cannot trigger loads in/from
1954 // discarded BrowsingContexts via IPC, so we need to abort in any case.
1955 if (IsDiscarded()) {
1956 return NS_OK;
1959 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
1960 "Targeting occurs in InternalLoad");
1961 aLoadState->AssertProcessCouldTriggerLoadIfSystem();
1963 if (mDocShell) {
1964 nsCOMPtr<nsIDocShell> docShell = mDocShell;
1965 return docShell->LoadURI(aLoadState, aSetNavigating);
1968 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
1969 // document-specific sandbox flags are only available in the process
1970 // triggering the load, and we don't want the target process to have to trust
1971 // the triggering process to do the appropriate checks for the
1972 // BrowsingContext's sandbox flags.
1973 MOZ_TRY(CheckSandboxFlags(aLoadState));
1974 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
1975 aLoadState->PrincipalToInherit(),
1976 aLoadState->GetLoadIdentifier());
1978 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1980 if (net::SchemeIsJavascript(aLoadState->URI())) {
1981 if (!XRE_IsParentProcess()) {
1982 // Web content should only be able to load javascript: URIs into documents
1983 // whose principals the caller principal subsumes, which by definition
1984 // excludes any document in a cross-process BrowsingContext.
1985 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
1987 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
1988 "Should never see a cross-process javascript: load "
1989 "triggered from content");
1992 MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
1993 if (sourceBC && sourceBC->IsInProcess()) {
1994 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
1995 if (WindowGlobalChild* wgc =
1996 win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
1997 if (!wgc->CanNavigate(this)) {
1998 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2000 wgc->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating);
2002 } else if (XRE_IsParentProcess()) {
2003 if (Canonical()->LoadInParent(aLoadState, aSetNavigating)) {
2004 return NS_OK;
2007 if (ContentParent* cp = Canonical()->GetContentParent()) {
2008 // Attempt to initiate this load immediately in the parent, if it succeeds
2009 // it'll return a unique identifier so that we can find it later.
2010 uint64_t loadIdentifier = 0;
2011 if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) {
2012 MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome());
2013 loadIdentifier = GetCurrentLoadIdentifier().value();
2014 aLoadState->SetChannelInitialized(true);
2017 cp->TransmitBlobDataIfBlobURL(aLoadState->URI());
2019 // Setup a confirmation callback once the content process receives this
2020 // load. Normally we'd expect a PDocumentChannel actor to have been
2021 // created to claim the load identifier by that time. If not, then it
2022 // won't be coming, so make sure we clean up and deregister.
2023 cp->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating)
2024 ->Then(GetMainThreadSerialEventTarget(), __func__,
2025 [loadIdentifier](
2026 const PContentParent::LoadURIPromise::ResolveOrRejectValue&
2027 aValue) {
2028 if (loadIdentifier) {
2029 net::DocumentLoadListener::CleanupParentLoadAttempt(
2030 loadIdentifier);
2034 } else {
2035 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2036 if (!sourceBC) {
2037 return NS_ERROR_UNEXPECTED;
2039 // If we're in a content process and the source BC is no longer in-process,
2040 // just fail silently.
2042 return NS_OK;
2045 nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
2046 if (IsDiscarded()) {
2047 return NS_OK;
2049 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
2050 aLoadState->PrincipalToInherit(),
2051 aLoadState->GetLoadIdentifier());
2053 MOZ_DIAGNOSTIC_ASSERT(aLoadState->Target().IsEmpty(),
2054 "should already have retargeted");
2055 MOZ_DIAGNOSTIC_ASSERT(!aLoadState->TargetBrowsingContext().IsNull(),
2056 "should have target bc set");
2057 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext() == this,
2058 "must be targeting this BrowsingContext");
2059 aLoadState->AssertProcessCouldTriggerLoadIfSystem();
2061 if (mDocShell) {
2062 RefPtr<nsDocShell> docShell = nsDocShell::Cast(mDocShell);
2063 return docShell->InternalLoad(aLoadState);
2066 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
2067 // document-specific sandbox flags are only available in the process
2068 // triggering the load, and we don't want the target process to have to trust
2069 // the triggering process to do the appropriate checks for the
2070 // BrowsingContext's sandbox flags.
2071 MOZ_TRY(CheckSandboxFlags(aLoadState));
2073 const auto& sourceBC = aLoadState->SourceBrowsingContext();
2075 if (net::SchemeIsJavascript(aLoadState->URI())) {
2076 if (!XRE_IsParentProcess()) {
2077 // Web content should only be able to load javascript: URIs into documents
2078 // whose principals the caller principal subsumes, which by definition
2079 // excludes any document in a cross-process BrowsingContext.
2080 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
2082 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
2083 "Should never see a cross-process javascript: load "
2084 "triggered from content");
2087 if (XRE_IsParentProcess()) {
2088 ContentParent* cp = Canonical()->GetContentParent();
2089 if (!cp || !cp->CanSend()) {
2090 return NS_ERROR_FAILURE;
2093 MOZ_ALWAYS_SUCCEEDS(
2094 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2095 Unused << cp->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
2096 } else {
2097 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2098 MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
2100 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
2101 WindowGlobalChild* wgc =
2102 win->GetCurrentInnerWindow()->GetWindowGlobalChild();
2103 if (!wgc || !wgc->CanSend()) {
2104 return NS_ERROR_FAILURE;
2106 if (!wgc->CanNavigate(this)) {
2107 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2110 MOZ_ALWAYS_SUCCEEDS(
2111 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2112 wgc->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
2115 return NS_OK;
2118 void BrowsingContext::DisplayLoadError(const nsAString& aURI) {
2119 MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError"));
2120 MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
2121 MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess());
2123 if (mDocShell) {
2124 bool didDisplayLoadError = false;
2125 nsCOMPtr<nsIDocShell> docShell = mDocShell;
2126 docShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr,
2127 PromiseFlatString(aURI).get(), nullptr,
2128 &didDisplayLoadError);
2129 } else {
2130 if (ContentParent* cp = Canonical()->GetContentParent()) {
2131 Unused << cp->SendDisplayLoadError(this, PromiseFlatString(aURI));
2136 WindowProxyHolder BrowsingContext::Window() {
2137 return WindowProxyHolder(Self());
2140 WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) {
2141 return Window();
2144 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
2145 if (mIsDiscarded) {
2146 return;
2149 if (IsSubframe()) {
2150 // .close() on frames is a no-op.
2151 return;
2154 if (GetDOMWindow()) {
2155 nsGlobalWindowOuter::Cast(GetDOMWindow())
2156 ->CloseOuter(aCallerType == CallerType::System);
2157 return;
2160 // This is a bit of a hack for webcompat. Content needs to see an updated
2161 // |window.closed| value as early as possible, so we set this before we
2162 // actually send the DOMWindowClose event, which happens in the process where
2163 // the document for this browsing context is loaded.
2164 MOZ_ALWAYS_SUCCEEDS(SetClosed(true));
2166 if (ContentChild* cc = ContentChild::GetSingleton()) {
2167 cc->SendWindowClose(this, aCallerType == CallerType::System);
2168 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2169 Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
2173 template <typename FuncT>
2174 inline bool ApplyToDocumentsForPopup(Document* doc, FuncT func) {
2175 // HACK: Some pages using bogus library + UA sniffing call window.open()
2176 // from a blank iframe, only on Firefox, see bug 1685056.
2178 // This is a hack-around to preserve behavior in that particular and
2179 // specific case, by consuming activation on the parent document, so we
2180 // don't care about the InProcessParent bits not being fission-safe or what
2181 // not.
2182 if (func(doc)) {
2183 return true;
2185 if (!doc->IsInitialDocument()) {
2186 return false;
2188 Document* parentDoc = doc->GetInProcessParentDocument();
2189 if (!parentDoc || !parentDoc->NodePrincipal()->Equals(doc->NodePrincipal())) {
2190 return false;
2192 return func(parentDoc);
2195 PopupBlocker::PopupControlState BrowsingContext::RevisePopupAbuseLevel(
2196 PopupBlocker::PopupControlState aControl) {
2197 if (!IsContent()) {
2198 return PopupBlocker::openAllowed;
2201 RefPtr<Document> doc = GetExtantDocument();
2202 PopupBlocker::PopupControlState abuse = aControl;
2203 switch (abuse) {
2204 case PopupBlocker::openControlled:
2205 case PopupBlocker::openBlocked:
2206 case PopupBlocker::openOverridden:
2207 if (IsPopupAllowed()) {
2208 abuse = PopupBlocker::PopupControlState(abuse - 1);
2210 break;
2211 case PopupBlocker::openAbused:
2212 if (IsPopupAllowed() ||
2213 (doc && doc->HasValidTransientUserGestureActivation())) {
2214 // Skip PopupBlocker::openBlocked
2215 abuse = PopupBlocker::openControlled;
2217 break;
2218 case PopupBlocker::openAllowed:
2219 break;
2220 default:
2221 NS_WARNING("Strange PopupControlState!");
2224 // limit the number of simultaneously open popups
2225 if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked ||
2226 abuse == PopupBlocker::openControlled) {
2227 int32_t popupMax = StaticPrefs::dom_popup_maximum();
2228 if (popupMax >= 0 &&
2229 PopupBlocker::GetOpenPopupSpamCount() >= (uint32_t)popupMax) {
2230 abuse = PopupBlocker::openOverridden;
2234 // If we're currently in-process, attempt to consume transient user gesture
2235 // activations.
2236 if (doc) {
2237 auto ConsumeTransientUserActivationForMultiplePopupBlocking =
2238 [&]() -> bool {
2239 return ApplyToDocumentsForPopup(doc, [](Document* doc) {
2240 return doc->ConsumeTransientUserGestureActivation();
2244 // If this popup is allowed, let's block any other for this event, forcing
2245 // PopupBlocker::openBlocked state.
2246 if ((abuse == PopupBlocker::openAllowed ||
2247 abuse == PopupBlocker::openControlled) &&
2248 StaticPrefs::dom_block_multiple_popups() && !IsPopupAllowed() &&
2249 !ConsumeTransientUserActivationForMultiplePopupBlocking()) {
2250 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
2251 doc, nsContentUtils::eDOM_PROPERTIES,
2252 "MultiplePopupsBlockedNoUserActivation");
2253 abuse = PopupBlocker::openBlocked;
2257 return abuse;
2260 void BrowsingContext::GetUserActivationModifiersForPopup(
2261 UserActivation::Modifiers* aModifiers) {
2262 RefPtr<Document> doc = GetExtantDocument();
2263 if (doc) {
2264 // Unlike RevisePopupAbuseLevel, modifiers can always be used regardless
2265 // of PopupControlState.
2266 (void)ApplyToDocumentsForPopup(doc, [&](Document* doc) {
2267 return doc->GetTransientUserGestureActivationModifiers(aModifiers);
2272 void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
2273 Unused << SetHistoryEntryCount(GetHistoryEntryCount() + 1);
2276 std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) {
2277 nsFocusManager* fm = nsFocusManager::GetFocusManager();
2278 if (!fm) {
2279 return {false, false};
2282 nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
2283 BrowsingContext* callerBC = caller ? caller->GetBrowsingContext() : nullptr;
2284 RefPtr<BrowsingContext> openerBC = GetOpener();
2285 MOZ_DIAGNOSTIC_ASSERT(!openerBC || openerBC->Group() == Group());
2287 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
2288 // window which opened us to raise us at times when popups are allowed
2289 // (bugs 355482 and 369306).
2290 bool canFocus = aCallerType == CallerType::System ||
2291 !Preferences::GetBool("dom.disable_window_flip", true);
2292 if (!canFocus && openerBC == callerBC) {
2293 canFocus =
2294 (callerBC ? callerBC : this)
2295 ->RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
2296 PopupBlocker::openBlocked;
2299 bool isActive = false;
2300 if (XRE_IsParentProcess()) {
2301 CanonicalBrowsingContext* chromeTop = Canonical()->TopCrossChromeBoundary();
2302 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = fm->GetActiveWindow();
2303 isActive = activeWindow == chromeTop->GetDOMWindow();
2304 } else {
2305 isActive = fm->GetActiveBrowsingContext() == Top();
2308 return {canFocus, isActive};
2311 void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
2312 // These checks need to happen before the RequestFrameFocus call, which
2313 // is why they are done in an untrusted process. If we wanted to enforce
2314 // these in the parent, we'd need to do the checks there _also_.
2315 // These should be kept in sync with nsGlobalWindowOuter::FocusOuter.
2317 auto [canFocus, isActive] = CanFocusCheck(aCallerType);
2319 if (!(canFocus || isActive)) {
2320 return;
2323 // Permission check passed
2325 if (mEmbedderElement) {
2326 // Make the activeElement in this process update synchronously.
2327 nsContentUtils::RequestFrameFocus(*mEmbedderElement, true, aCallerType);
2329 uint64_t actionId = nsFocusManager::GenerateFocusActionId();
2330 if (ContentChild* cc = ContentChild::GetSingleton()) {
2331 cc->SendWindowFocus(this, aCallerType, actionId);
2332 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2333 Unused << cp->SendWindowFocus(this, aCallerType, actionId);
2337 bool BrowsingContext::CanBlurCheck(CallerType aCallerType) {
2338 // If dom.disable_window_flip == true, then content should not be allowed
2339 // to do blur (this would allow popunders, bug 369306)
2340 return aCallerType == CallerType::System ||
2341 !Preferences::GetBool("dom.disable_window_flip", true);
2344 void BrowsingContext::Blur(CallerType aCallerType, ErrorResult& aError) {
2345 if (!CanBlurCheck(aCallerType)) {
2346 return;
2349 if (ContentChild* cc = ContentChild::GetSingleton()) {
2350 cc->SendWindowBlur(this, aCallerType);
2351 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2352 Unused << cp->SendWindowBlur(this, aCallerType);
2356 Nullable<WindowProxyHolder> BrowsingContext::GetWindow() {
2357 if (XRE_IsParentProcess() && !IsInProcess()) {
2358 return nullptr;
2360 return WindowProxyHolder(this);
2363 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
2364 if (mIsDiscarded) {
2365 return nullptr;
2368 // We never return null or throw an error, but the implementation in
2369 // nsGlobalWindow does and we need to use the same signature.
2370 return WindowProxyHolder(Top());
2373 void BrowsingContext::GetOpener(JSContext* aCx,
2374 JS::MutableHandle<JS::Value> aOpener,
2375 ErrorResult& aError) const {
2376 RefPtr<BrowsingContext> opener = GetOpener();
2377 if (!opener) {
2378 aOpener.setNull();
2379 return;
2382 if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
2383 aError.NoteJSContextException(aCx);
2387 // We never throw an error, but the implementation in nsGlobalWindow does and
2388 // we need to use the same signature.
2389 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
2390 if (mIsDiscarded) {
2391 return nullptr;
2394 if (GetParent()) {
2395 return WindowProxyHolder(GetParent());
2397 return WindowProxyHolder(this);
2400 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2401 JS::Handle<JS::Value> aMessage,
2402 const nsAString& aTargetOrigin,
2403 const Sequence<JSObject*>& aTransfer,
2404 nsIPrincipal& aSubjectPrincipal,
2405 ErrorResult& aError) {
2406 if (mIsDiscarded) {
2407 return;
2410 RefPtr<BrowsingContext> sourceBc;
2411 PostMessageData data;
2412 data.targetOrigin() = aTargetOrigin;
2413 data.subjectPrincipal() = &aSubjectPrincipal;
2414 RefPtr<nsGlobalWindowInner> callerInnerWindow;
2415 nsAutoCString scriptLocation;
2416 // We don't need to get the caller's agentClusterId since that is used for
2417 // checking whether it's okay to sharing memory (and it's not allowed to share
2418 // memory cross processes)
2419 if (!nsGlobalWindowOuter::GatherPostMessageData(
2420 aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
2421 getter_AddRefs(data.targetOriginURI()),
2422 getter_AddRefs(data.callerPrincipal()),
2423 getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()),
2424 /* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) {
2425 return;
2427 if (sourceBc && sourceBc->IsDiscarded()) {
2428 return;
2430 data.source() = sourceBc;
2431 data.isFromPrivateWindow() =
2432 callerInnerWindow &&
2433 nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
2434 data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0;
2435 data.scriptLocation() = scriptLocation;
2436 JS::Rooted<JS::Value> transferArray(aCx);
2437 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
2438 &transferArray);
2439 if (NS_WARN_IF(aError.Failed())) {
2440 return;
2443 JS::CloneDataPolicy clonePolicy;
2444 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
2445 clonePolicy.allowSharedMemoryObjects();
2448 // We will see if the message is required to be in the same process or it can
2449 // be in the different process after Write().
2450 ipc::StructuredCloneData message = ipc::StructuredCloneData(
2451 StructuredCloneHolder::StructuredCloneScope::UnknownDestination,
2452 StructuredCloneHolder::TransferringSupported);
2453 message.Write(aCx, aMessage, transferArray, clonePolicy, aError);
2454 if (NS_WARN_IF(aError.Failed())) {
2455 return;
2458 ClonedOrErrorMessageData messageData;
2459 if (ContentChild* cc = ContentChild::GetSingleton()) {
2460 // The clone scope gets set when we write the message data based on the
2461 // requirements of that data that we're writing.
2462 // If the message data contains a shared memory object, then CloneScope
2463 // would return SameProcess. Otherwise, it returns DifferentProcess.
2464 if (message.CloneScope() ==
2465 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2466 ClonedMessageData clonedMessageData;
2467 if (!message.BuildClonedMessageData(clonedMessageData)) {
2468 aError.Throw(NS_ERROR_FAILURE);
2469 return;
2472 messageData = std::move(clonedMessageData);
2473 } else {
2474 MOZ_ASSERT(message.CloneScope() ==
2475 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2477 messageData = ErrorMessageData();
2479 nsContentUtils::ReportToConsole(
2480 nsIScriptError::warningFlag, "DOM Window"_ns,
2481 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2482 nsContentUtils::eDOM_PROPERTIES,
2483 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2486 cc->SendWindowPostMessage(this, messageData, data);
2487 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2488 if (message.CloneScope() ==
2489 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2490 ClonedMessageData clonedMessageData;
2491 if (!message.BuildClonedMessageData(clonedMessageData)) {
2492 aError.Throw(NS_ERROR_FAILURE);
2493 return;
2496 messageData = std::move(clonedMessageData);
2497 } else {
2498 MOZ_ASSERT(message.CloneScope() ==
2499 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2501 messageData = ErrorMessageData();
2503 nsContentUtils::ReportToConsole(
2504 nsIScriptError::warningFlag, "DOM Window"_ns,
2505 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2506 nsContentUtils::eDOM_PROPERTIES,
2507 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2510 Unused << cp->SendWindowPostMessage(this, messageData, data);
2514 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2515 JS::Handle<JS::Value> aMessage,
2516 const WindowPostMessageOptions& aOptions,
2517 nsIPrincipal& aSubjectPrincipal,
2518 ErrorResult& aError) {
2519 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
2520 aSubjectPrincipal, aError);
2523 void BrowsingContext::SendCommitTransaction(ContentParent* aParent,
2524 const BaseTransaction& aTxn,
2525 uint64_t aEpoch) {
2526 Unused << aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2529 void BrowsingContext::SendCommitTransaction(ContentChild* aChild,
2530 const BaseTransaction& aTxn,
2531 uint64_t aEpoch) {
2532 aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2535 BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
2536 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
2537 MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content);
2539 IPCInitializer init;
2540 init.mId = Id();
2541 init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
2542 init.mWindowless = mWindowless;
2543 init.mUseRemoteTabs = mUseRemoteTabs;
2544 init.mUseRemoteSubframes = mUseRemoteSubframes;
2545 init.mCreatedDynamically = mCreatedDynamically;
2546 init.mChildOffset = mChildOffset;
2547 init.mOriginAttributes = mOriginAttributes;
2548 if (mChildSessionHistory && mozilla::SessionHistoryInParent()) {
2549 init.mSessionHistoryIndex = mChildSessionHistory->Index();
2550 init.mSessionHistoryCount = mChildSessionHistory->Count();
2552 init.mRequestContextId = mRequestContextId;
2553 init.mFields = mFields.RawValues();
2554 return init;
2557 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
2558 RefPtr<WindowContext> parent;
2559 if (mParentId != 0) {
2560 parent = WindowContext::GetById(mParentId);
2561 MOZ_RELEASE_ASSERT(parent);
2563 return parent.forget();
2566 already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
2567 RefPtr<BrowsingContext> opener;
2568 if (GetOpenerId() != 0) {
2569 opener = BrowsingContext::Get(GetOpenerId());
2570 MOZ_RELEASE_ASSERT(opener);
2572 return opener.forget();
2575 void BrowsingContext::StartDelayedAutoplayMediaComponents() {
2576 if (!mDocShell) {
2577 return;
2579 AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
2580 XRE_IsParentProcess() ? "Parent" : "Child", Id());
2581 mDocShell->StartDelayedAutoplayMediaComponents();
2584 nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
2585 MOZ_ASSERT(IsTop(),
2586 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2587 "browsing context");
2589 Transaction txn;
2590 txn.SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2591 txn.SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2592 return txn.Commit(this);
2595 template <typename Callback>
2596 void BrowsingContext::WalkPresContexts(Callback&& aCallback) {
2597 PreOrderWalk([&](BrowsingContext* aContext) {
2598 if (nsIDocShell* shell = aContext->GetDocShell()) {
2599 if (RefPtr pc = shell->GetPresContext()) {
2600 aCallback(pc.get());
2606 void BrowsingContext::PresContextAffectingFieldChanged() {
2607 WalkPresContexts([&](nsPresContext* aPc) {
2608 aPc->RecomputeBrowsingContextDependentData();
2612 void BrowsingContext::DidSet(FieldIndex<IDX_SessionStoreEpoch>,
2613 uint32_t aOldValue) {
2614 if (!mCurrentWindowContext) {
2615 return;
2617 SessionStoreChild* sessionStoreChild =
2618 SessionStoreChild::From(mCurrentWindowContext->GetWindowGlobalChild());
2619 if (!sessionStoreChild) {
2620 return;
2623 sessionStoreChild->SetEpoch(GetSessionStoreEpoch());
2626 void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
2627 MOZ_ASSERT(IsTop(),
2628 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2629 "browsing context");
2632 void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
2633 MOZ_ASSERT(IsTop(),
2634 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2635 "browsing context");
2638 bool BrowsingContext::CanSet(FieldIndex<IDX_ExplicitActive>,
2639 const ExplicitActiveStatus&,
2640 ContentParent* aSource) {
2641 return XRE_IsParentProcess() && IsTop() && !aSource;
2644 void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
2645 ExplicitActiveStatus aOldValue) {
2646 MOZ_ASSERT(IsTop());
2648 const bool isActive = IsActive();
2649 const bool wasActive = [&] {
2650 if (aOldValue != ExplicitActiveStatus::None) {
2651 return aOldValue == ExplicitActiveStatus::Active;
2653 return GetParent() && GetParent()->IsActive();
2654 }();
2656 if (isActive == wasActive) {
2657 return;
2660 Group()->UpdateToplevelsSuspendedIfNeeded();
2661 if (XRE_IsParentProcess()) {
2662 if (BrowserParent* bp = Canonical()->GetBrowserParent()) {
2663 bp->RecomputeProcessPriority();
2664 #if defined(XP_WIN) && defined(ACCESSIBILITY)
2665 if (a11y::Compatibility::IsDolphin()) {
2666 // update active accessible documents on windows
2667 if (a11y::DocAccessibleParent* tabDoc =
2668 bp->GetTopLevelDocAccessible()) {
2669 HWND window = tabDoc->GetEmulatedWindowHandle();
2670 MOZ_ASSERT(window);
2671 if (window) {
2672 if (isActive) {
2673 a11y::nsWinUtils::ShowNativeWindow(window);
2674 } else {
2675 a11y::nsWinUtils::HideNativeWindow(window);
2680 #endif
2683 // NOTE(emilio): Ideally we'd want to reuse the ExplicitActiveStatus::None
2684 // set-up, but that's non-trivial to do because in content processes we
2685 // can't access the top-cross-chrome-boundary bc.
2686 auto manageTopDescendant = [&](auto* aChild) {
2687 if (!aChild->ManuallyManagesActiveness()) {
2688 aChild->SetIsActiveInternal(isActive, IgnoreErrors());
2689 if (BrowserParent* bp = aChild->GetBrowserParent()) {
2690 bp->SetRenderLayers(isActive);
2693 return CallState::Continue;
2695 Canonical()->CallOnAllTopDescendants(manageTopDescendant,
2696 /* aIncludeNestedBrowsers = */ false);
2699 PreOrderWalk([&](BrowsingContext* aContext) {
2700 if (nsCOMPtr<nsIDocShell> ds = aContext->GetDocShell()) {
2701 nsDocShell::Cast(ds)->ActivenessMaybeChanged();
2706 void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
2707 MOZ_ASSERT(IsTop(),
2708 "Should only set InRDMPane in the top-level browsing context");
2709 if (GetInRDMPane() == aOldValue) {
2710 return;
2712 PresContextAffectingFieldChanged();
2715 bool BrowsingContext::CanSet(FieldIndex<IDX_PageAwakeRequestCount>,
2716 uint32_t aNewValue, ContentParent* aSource) {
2717 return IsTop() && XRE_IsParentProcess() && !aSource;
2720 void BrowsingContext::DidSet(FieldIndex<IDX_PageAwakeRequestCount>,
2721 uint32_t aOldValue) {
2722 if (!IsTop() || aOldValue == GetPageAwakeRequestCount()) {
2723 return;
2725 Group()->UpdateToplevelsSuspendedIfNeeded();
2728 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
2729 ContentParent* aSource) -> CanSetResult {
2730 if (mozilla::SessionHistoryInParent()) {
2731 return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow
2732 : CanSetResult::Deny;
2735 // Without Session History in Parent, session restore code still needs to set
2736 // this from content processes.
2737 return LegacyRevertIfNotOwningOrParentProcess(aSource);
2740 void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
2741 RecomputeCanExecuteScripts();
2744 void BrowsingContext::RecomputeCanExecuteScripts() {
2745 const bool old = mCanExecuteScripts;
2746 if (!AllowJavascript()) {
2747 // Scripting has been explicitly disabled on our BrowsingContext.
2748 mCanExecuteScripts = false;
2749 } else if (GetParentWindowContext()) {
2750 // Otherwise, inherit parent.
2751 mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts();
2752 } else {
2753 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2754 // script. Allow.
2755 mCanExecuteScripts = true;
2758 if (old != mCanExecuteScripts) {
2759 for (WindowContext* wc : GetWindowContexts()) {
2760 wc->RecomputeCanExecuteScripts();
2765 bool BrowsingContext::InactiveForSuspend() const {
2766 if (!StaticPrefs::dom_suspend_inactive_enabled()) {
2767 return false;
2769 // We should suspend a page only when it's inactive and doesn't have any awake
2770 // request that is used to prevent page from being suspended because web page
2771 // might still need to run their script. Eg. waiting for media keys to resume
2772 // media, playing web audio, waiting in a video call conference room.
2773 return !IsActive() && GetPageAwakeRequestCount() == 0;
2776 bool BrowsingContext::CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2777 dom::TouchEventsOverride, ContentParent* aSource) {
2778 return XRE_IsParentProcess() && !aSource;
2781 void BrowsingContext::DidSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2782 dom::TouchEventsOverride&& aOldValue) {
2783 if (GetTouchEventsOverrideInternal() == aOldValue) {
2784 return;
2786 WalkPresContexts([&](nsPresContext* aPc) {
2787 aPc->MediaFeatureValuesChanged(
2788 {MediaFeatureChangeReason::SystemMetricsChange},
2789 // We're already iterating through sub documents, so we don't need to
2790 // propagate the change again.
2791 MediaFeatureChangePropagation::JustThisDocument);
2795 void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderColorSchemes>,
2796 EmbedderColorSchemes&& aOldValue) {
2797 if (GetEmbedderColorSchemes() == aOldValue) {
2798 return;
2800 PresContextAffectingFieldChanged();
2803 void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
2804 dom::PrefersColorSchemeOverride aOldValue) {
2805 MOZ_ASSERT(IsTop());
2806 if (PrefersColorSchemeOverride() == aOldValue) {
2807 return;
2809 PresContextAffectingFieldChanged();
2812 void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
2813 nsString&& aOldValue) {
2814 MOZ_ASSERT(IsTop());
2815 if (GetMediumOverride() == aOldValue) {
2816 return;
2818 PresContextAffectingFieldChanged();
2821 void BrowsingContext::DidSet(FieldIndex<IDX_DisplayMode>,
2822 enum DisplayMode aOldValue) {
2823 MOZ_ASSERT(IsTop());
2825 if (GetDisplayMode() == aOldValue) {
2826 return;
2829 WalkPresContexts([&](nsPresContext* aPc) {
2830 aPc->MediaFeatureValuesChanged(
2831 {MediaFeatureChangeReason::DisplayModeChange},
2832 // We're already iterating through sub documents, so we don't need
2833 // to propagate the change again.
2835 // Images and other resources don't change their display-mode
2836 // evaluation, display-mode is a property of the browsing context.
2837 MediaFeatureChangePropagation::JustThisDocument);
2841 void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) {
2842 MOZ_ASSERT(IsTop(), "Set muted flag on non top-level context!");
2843 USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64,
2844 GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child",
2845 Id());
2846 PreOrderWalk([&](BrowsingContext* aContext) {
2847 nsPIDOMWindowOuter* win = aContext->GetDOMWindow();
2848 if (win) {
2849 win->RefreshMediaElementsVolume();
2854 bool BrowsingContext::CanSet(FieldIndex<IDX_IsAppTab>, const bool& aValue,
2855 ContentParent* aSource) {
2856 return XRE_IsParentProcess() && !aSource && IsTop();
2859 bool BrowsingContext::CanSet(FieldIndex<IDX_HasSiblings>, const bool& aValue,
2860 ContentParent* aSource) {
2861 return XRE_IsParentProcess() && !aSource && IsTop();
2864 bool BrowsingContext::CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2865 const bool& aValue, ContentParent* aSource) {
2866 return IsTop();
2869 void BrowsingContext::DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2870 bool aOldValue) {
2871 MOZ_ASSERT(IsTop(), "Set attribute on non top-level context!");
2872 if (aOldValue == GetShouldDelayMediaFromStart()) {
2873 return;
2875 if (!GetShouldDelayMediaFromStart()) {
2876 PreOrderWalk([&](BrowsingContext* aContext) {
2877 if (nsPIDOMWindowOuter* win = aContext->GetDOMWindow()) {
2878 win->ActivateMediaComponents();
2884 bool BrowsingContext::CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue,
2885 ContentParent* aSource) {
2886 return XRE_IsParentProcess() && !aSource && IsTop();
2889 void BrowsingContext::DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue) {
2890 MOZ_ASSERT(IsTop());
2891 if (GetOverrideDPPX() == aOldValue) {
2892 return;
2894 PresContextAffectingFieldChanged();
2897 void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent,
2898 ErrorResult& aRv) {
2899 Top()->SetUserAgentOverride(aUserAgent, aRv);
2902 nsresult BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) {
2903 return Top()->SetUserAgentOverride(aUserAgent);
2906 void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
2907 MOZ_ASSERT(IsTop());
2909 PreOrderWalk([&](BrowsingContext* aContext) {
2910 nsIDocShell* shell = aContext->GetDocShell();
2911 if (shell) {
2912 shell->ClearCachedUserAgent();
2917 bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool,
2918 ContentParent* aSource) {
2919 return IsTop() && !aSource && mozilla::BFCacheInParent();
2922 void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
2923 MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent());
2924 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2926 const bool isInBFCache = GetIsInBFCache();
2927 if (!isInBFCache) {
2928 UpdateCurrentTopByBrowserId(this);
2929 PreOrderWalk([&](BrowsingContext* aContext) {
2930 aContext->mIsInBFCache = false;
2931 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2932 if (shell) {
2933 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true);
2938 if (isInBFCache && XRE_IsContentProcess() && mDocShell) {
2939 nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide();
2942 if (isInBFCache) {
2943 PreOrderWalk([&](BrowsingContext* aContext) {
2944 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2945 if (shell) {
2946 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false);
2949 } else {
2950 PostOrderWalk([&](BrowsingContext* aContext) {
2951 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2952 if (shell) {
2953 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true);
2958 if (isInBFCache) {
2959 PreOrderWalk([&](BrowsingContext* aContext) {
2960 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2961 if (shell) {
2962 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false);
2963 if (nsPresContext* pc = shell->GetPresContext()) {
2964 pc->EventStateManager()->ResetHoverState();
2967 aContext->mIsInBFCache = true;
2968 Document* doc = aContext->GetDocument();
2969 if (doc) {
2970 // Notifying needs to happen after mIsInBFCache is set to true.
2971 doc->NotifyActivityChanged();
2975 if (XRE_IsParentProcess()) {
2976 if (mCurrentWindowContext &&
2977 mCurrentWindowContext->Canonical()->Fullscreen()) {
2978 mCurrentWindowContext->Canonical()->ExitTopChromeDocumentFullscreen();
2984 void BrowsingContext::DidSet(FieldIndex<IDX_SyntheticDocumentContainer>) {
2985 if (WindowContext* parentWindowContext = GetParentWindowContext()) {
2986 parentWindowContext->UpdateChildSynthetic(this,
2987 GetSyntheticDocumentContainer());
2991 void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
2992 ErrorResult& aRv) {
2993 Top()->SetPlatformOverride(aPlatform, aRv);
2996 void BrowsingContext::DidSet(FieldIndex<IDX_PlatformOverride>) {
2997 MOZ_ASSERT(IsTop());
2999 PreOrderWalk([&](BrowsingContext* aContext) {
3000 nsIDocShell* shell = aContext->GetDocShell();
3001 if (shell) {
3002 shell->ClearCachedPlatform();
3007 auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
3008 ContentParent* aSource) -> CanSetResult {
3009 if (aSource) {
3010 MOZ_ASSERT(XRE_IsParentProcess());
3012 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
3013 return CanSetResult::Revert;
3015 } else if (!IsInProcess() && !XRE_IsParentProcess()) {
3016 // Don't allow this to be set from content processes that
3017 // don't own the BrowsingContext.
3018 return CanSetResult::Deny;
3021 return CanSetResult::Allow;
3024 bool BrowsingContext::CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
3025 const bool& aValue, ContentParent* aSource) {
3026 // Should only be set in the parent process.
3027 return XRE_IsParentProcess() && !aSource && IsTop();
3030 void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
3031 bool aOldValue) {
3032 bool isActivateEvent = GetIsActiveBrowserWindowInternal();
3033 // The browser window containing this context has changed
3034 // activation state so update window inactive document states
3035 // for all in-process documents.
3036 PreOrderWalk([isActivateEvent](BrowsingContext* aContext) {
3037 if (RefPtr<Document> doc = aContext->GetExtantDocument()) {
3038 doc->UpdateDocumentStates(DocumentState::WINDOW_INACTIVE, true);
3040 RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
3041 if (win) {
3042 RefPtr<MediaDevices> devices;
3043 if (isActivateEvent && (devices = win->GetExtantMediaDevices())) {
3044 devices->BrowserWindowBecameActive();
3047 if (XRE_IsContentProcess() &&
3048 (!aContext->GetParent() || !aContext->GetParent()->IsInProcess())) {
3049 // Send the inner window an activate/deactivate event if
3050 // the context is the top of a sub-tree of in-process
3051 // contexts.
3052 nsContentUtils::DispatchEventOnlyToChrome(
3053 doc, nsGlobalWindowInner::Cast(win),
3054 isActivateEvent ? u"activate"_ns : u"deactivate"_ns,
3055 CanBubble::eYes, Cancelable::eYes, nullptr);
3062 bool BrowsingContext::CanSet(FieldIndex<IDX_OpenerPolicy>,
3063 nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
3064 ContentParent* aSource) {
3065 // A potentially cross-origin isolated BC can't change opener policy, nor can
3066 // a BC become potentially cross-origin isolated. An unchanged policy is
3067 // always OK.
3068 return GetOpenerPolicy() == aPolicy ||
3069 (GetOpenerPolicy() !=
3070 nsILoadInfo::
3071 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
3072 aPolicy !=
3073 nsILoadInfo::
3074 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP);
3077 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
3078 const bool& aAllowContentRetargeting,
3079 ContentParent* aSource) -> CanSetResult {
3080 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3083 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
3084 const bool& aAllowContentRetargetingOnChildren,
3085 ContentParent* aSource) -> CanSetResult {
3086 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3089 bool BrowsingContext::CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>,
3090 const bool& aAllowed, ContentParent* aSource) {
3091 return CheckOnlyEmbedderCanSet(aSource);
3094 bool BrowsingContext::CanSet(FieldIndex<IDX_UseErrorPages>,
3095 const bool& aUseErrorPages,
3096 ContentParent* aSource) {
3097 return CheckOnlyEmbedderCanSet(aSource);
3100 TouchEventsOverride BrowsingContext::TouchEventsOverride() const {
3101 for (const auto* bc = this; bc; bc = bc->GetParent()) {
3102 auto tev = bc->GetTouchEventsOverrideInternal();
3103 if (tev != dom::TouchEventsOverride::None) {
3104 return tev;
3107 return dom::TouchEventsOverride::None;
3110 bool BrowsingContext::TargetTopLevelLinkClicksToBlank() const {
3111 return Top()->GetTargetTopLevelLinkClicksToBlankInternal();
3114 // We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
3115 // BC field. And we map it to the top level BrowsingContext.
3116 bool BrowsingContext::WatchedByDevTools() {
3117 return Top()->GetWatchedByDevToolsInternal();
3120 // Enforce that the watchedByDevTools BC field can only be set on the top level
3121 // Browsing Context.
3122 bool BrowsingContext::CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>,
3123 const bool& aWatchedByDevTools,
3124 ContentParent* aSource) {
3125 return IsTop();
3127 void BrowsingContext::SetWatchedByDevTools(bool aWatchedByDevTools,
3128 ErrorResult& aRv) {
3129 if (!IsTop()) {
3130 aRv.ThrowInvalidModificationError(
3131 "watchedByDevTools can only be set on top BrowsingContext");
3132 return;
3134 SetWatchedByDevToolsInternal(aWatchedByDevTools, aRv);
3137 auto BrowsingContext::CanSet(FieldIndex<IDX_DefaultLoadFlags>,
3138 const uint32_t& aDefaultLoadFlags,
3139 ContentParent* aSource) -> CanSetResult {
3140 // Bug 1623565 - Are these flags only used by the debugger, which makes it
3141 // possible that this field can only be settable by the parent process?
3142 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3145 void BrowsingContext::DidSet(FieldIndex<IDX_DefaultLoadFlags>) {
3146 auto loadFlags = GetDefaultLoadFlags();
3147 if (GetDocShell()) {
3148 nsDocShell::Cast(GetDocShell())->SetLoadGroupDefaultLoadFlags(loadFlags);
3151 if (XRE_IsParentProcess()) {
3152 PreOrderWalk([&](BrowsingContext* aContext) {
3153 if (aContext != this) {
3154 // Setting load flags on a discarded context has no effect.
3155 Unused << aContext->SetDefaultLoadFlags(loadFlags);
3161 bool BrowsingContext::CanSet(FieldIndex<IDX_UseGlobalHistory>,
3162 const bool& aUseGlobalHistory,
3163 ContentParent* aSource) {
3164 // Should only be set in the parent process.
3165 // return XRE_IsParentProcess() && !aSource;
3166 return true;
3169 auto BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>,
3170 const nsString& aUserAgent, ContentParent* aSource)
3171 -> CanSetResult {
3172 if (!IsTop()) {
3173 return CanSetResult::Deny;
3176 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3179 auto BrowsingContext::CanSet(FieldIndex<IDX_PlatformOverride>,
3180 const nsString& aPlatform, ContentParent* aSource)
3181 -> CanSetResult {
3182 if (!IsTop()) {
3183 return CanSetResult::Deny;
3186 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3189 bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
3190 if (XRE_IsParentProcess()) {
3191 uint64_t childId = aSource ? aSource->ChildID() : 0;
3192 return Canonical()->IsEmbeddedInProcess(childId);
3194 return mEmbeddedByThisProcess;
3197 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
3198 const uint64_t& aValue, ContentParent* aSource) {
3199 // If we have a parent window, our embedder inner window ID must match it.
3200 if (mParentWindow) {
3201 return mParentWindow->Id() == aValue;
3204 // For toplevel BrowsingContext instances, this value may only be set by the
3205 // parent process, or initialized to `0`.
3206 return CheckOnlyEmbedderCanSet(aSource);
3209 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
3210 const Maybe<nsString>&, ContentParent* aSource) {
3211 return CheckOnlyEmbedderCanSet(aSource);
3214 auto BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
3215 const uint64_t& aValue, ContentParent* aSource)
3216 -> CanSetResult {
3217 // Generally allow clearing this. We may want to be more precise about this
3218 // check in the future.
3219 if (aValue == 0) {
3220 return CanSetResult::Allow;
3223 // We must have access to the specified context.
3224 RefPtr<WindowContext> window = WindowContext::GetById(aValue);
3225 if (!window || window->GetBrowsingContext() != this) {
3226 return CanSetResult::Deny;
3229 if (aSource) {
3230 // If the sending process is no longer the current owner, revert
3231 MOZ_ASSERT(XRE_IsParentProcess());
3232 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
3233 return CanSetResult::Revert;
3235 } else if (XRE_IsContentProcess() && !IsOwnedByProcess()) {
3236 return CanSetResult::Deny;
3239 return CanSetResult::Allow;
3242 bool BrowsingContext::CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>,
3243 const uint64_t& aValue, ContentParent* aSource) {
3244 return XRE_IsParentProcess() && !aSource;
3247 void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
3248 RefPtr<WindowContext> prevWindowContext = mCurrentWindowContext.forget();
3249 mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
3250 MOZ_ASSERT(
3251 !mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext),
3252 "WindowContext not registered?");
3254 // Clear our cached `children` value, to ensure that JS sees the up-to-date
3255 // value.
3256 BrowsingContext_Binding::ClearCachedChildrenValue(this);
3258 if (XRE_IsParentProcess()) {
3259 if (prevWindowContext != mCurrentWindowContext) {
3260 if (prevWindowContext) {
3261 prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false);
3263 if (mCurrentWindowContext) {
3264 // We set a timer when we set the current inner window. This
3265 // will then flush the session storage to session store to
3266 // make sure that we don't miss to store session storage to
3267 // session store that is a result of navigation. This is due
3268 // to Bug 1700623. We wish to fix this in Bug 1711886, where
3269 // making sure to store everything would make this timer
3270 // unnecessary.
3271 Canonical()->MaybeScheduleSessionStoreUpdate();
3272 mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true);
3275 BrowserParent::UpdateFocusFromBrowsingContext();
3279 bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
3280 ContentParent* aSource) {
3281 // Ensure that we only mark a browsing context as popup spam once and never
3282 // unmark it.
3283 return aValue && !GetIsPopupSpam();
3286 void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) {
3287 if (GetIsPopupSpam()) {
3288 PopupBlocker::RegisterOpenPopupSpam();
3292 bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>,
3293 const nsString& aMessageManagerGroup,
3294 ContentParent* aSource) {
3295 // Should only be set in the parent process on toplevel.
3296 return XRE_IsParentProcess() && !aSource && IsTopContent();
3299 bool BrowsingContext::CanSet(
3300 FieldIndex<IDX_OrientationLock>,
3301 const mozilla::hal::ScreenOrientation& aOrientationLock,
3302 ContentParent* aSource) {
3303 return IsTop();
3306 bool BrowsingContext::IsLoading() {
3307 if (GetLoading()) {
3308 return true;
3311 // If we're in the same process as the page, we're possibly just
3312 // updating the flag.
3313 nsIDocShell* shell = GetDocShell();
3314 if (shell) {
3315 Document* doc = shell->GetDocument();
3316 return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
3319 return false;
3322 void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) {
3323 if (mFields.Get<IDX_Loading>()) {
3324 return;
3327 while (!mDeprioritizedLoadRunner.isEmpty()) {
3328 nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
3329 NS_DispatchToCurrentThread(runner.forget());
3332 if (IsTop()) {
3333 Group()->FlushPostMessageEvents();
3337 // Inform the Document for this context of the (potential) change in
3338 // loading state
3339 void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) {
3340 nsPIDOMWindowOuter* outer = GetDOMWindow();
3341 if (!outer) {
3342 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3343 ("DidSetAncestorLoading BC: %p -- No outer window", (void*)this));
3344 return;
3346 Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc();
3347 if (document) {
3348 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3349 ("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)",
3350 (void*)this, GetAncestorLoading(), document->GetReadyStateEnum(),
3351 document->GetReadyStateEnum()));
3352 document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(),
3353 document->GetReadyStateEnum());
3357 void BrowsingContext::DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>) {
3358 MOZ_ASSERT(IsTop(),
3359 "Should only set AuthorStyleDisabledDefault in the top "
3360 "browsing context");
3362 // We don't need to handle changes to this field, since PageStyleChild.sys.mjs
3363 // will respond to the PageStyle:Disable message in all content processes.
3365 // But we store the state here on the top BrowsingContext so that the
3366 // docshell has somewhere to look for the current author style disabling
3367 // state when new iframes are inserted.
3370 void BrowsingContext::DidSet(FieldIndex<IDX_TextZoom>, float aOldValue) {
3371 if (GetTextZoom() == aOldValue) {
3372 return;
3375 if (IsInProcess()) {
3376 if (nsIDocShell* shell = GetDocShell()) {
3377 if (nsPresContext* pc = shell->GetPresContext()) {
3378 pc->RecomputeBrowsingContextDependentData();
3382 for (BrowsingContext* child : Children()) {
3383 // Setting text zoom on a discarded context has no effect.
3384 Unused << child->SetTextZoom(GetTextZoom());
3388 if (IsTop() && XRE_IsParentProcess()) {
3389 if (Element* element = GetEmbedderElement()) {
3390 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"TextZoomChange"_ns,
3391 CanBubble::eYes,
3392 ChromeOnlyDispatch::eYes);
3397 // TODO(emilio): It'd be potentially nicer and cheaper to allow to set this only
3398 // on the Top() browsing context, but there are a lot of tests that rely on
3399 // zooming a subframe so...
3400 void BrowsingContext::DidSet(FieldIndex<IDX_FullZoom>, float aOldValue) {
3401 if (GetFullZoom() == aOldValue) {
3402 return;
3405 if (IsInProcess()) {
3406 if (nsIDocShell* shell = GetDocShell()) {
3407 if (nsPresContext* pc = shell->GetPresContext()) {
3408 pc->RecomputeBrowsingContextDependentData();
3412 for (BrowsingContext* child : Children()) {
3413 // Setting full zoom on a discarded context has no effect.
3414 Unused << child->SetFullZoom(GetFullZoom());
3418 if (IsTop() && XRE_IsParentProcess()) {
3419 if (Element* element = GetEmbedderElement()) {
3420 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"FullZoomChange"_ns,
3421 CanBubble::eYes,
3422 ChromeOnlyDispatch::eYes);
3427 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
3428 MOZ_ASSERT(IsLoading());
3429 MOZ_ASSERT(Top() == this);
3431 RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
3432 mDeprioritizedLoadRunner.insertBack(runner);
3433 NS_DispatchToCurrentThreadQueue(
3434 runner.forget(), StaticPrefs::page_load_deprioritization_period(),
3435 EventQueuePriority::Idle);
3438 bool BrowsingContext::IsDynamic() const {
3439 const BrowsingContext* current = this;
3440 do {
3441 if (current->CreatedDynamically()) {
3442 return true;
3444 } while ((current = current->GetParent()));
3446 return false;
3449 bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const {
3450 for (const BrowsingContext* current = this; current && current->GetParent();
3451 current = current->GetParent()) {
3452 if (current->CreatedDynamically()) {
3453 return false;
3455 aPath.AppendElement(current->ChildOffset());
3457 return true;
3460 void BrowsingContext::GetHistoryID(JSContext* aCx,
3461 JS::MutableHandle<JS::Value> aVal,
3462 ErrorResult& aError) {
3463 if (!xpc::ID2JSValue(aCx, GetHistoryID(), aVal)) {
3464 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
3468 void BrowsingContext::InitSessionHistory() {
3469 MOZ_ASSERT(!IsDiscarded());
3470 MOZ_ASSERT(IsTop());
3471 MOZ_ASSERT(EverAttached());
3473 if (!GetHasSessionHistory()) {
3474 MOZ_ALWAYS_SUCCEEDS(SetHasSessionHistory(true));
3478 ChildSHistory* BrowsingContext::GetChildSessionHistory() {
3479 if (!mozilla::SessionHistoryInParent()) {
3480 // For now we're checking that the session history object for the child
3481 // process is available before returning the ChildSHistory object, because
3482 // it is the actual implementation that ChildSHistory forwards to. This can
3483 // be removed once session history is stored exclusively in the parent
3484 // process.
3485 return mChildSessionHistory && mChildSessionHistory->IsInProcess()
3486 ? mChildSessionHistory.get()
3487 : nullptr;
3490 return mChildSessionHistory;
3493 void BrowsingContext::CreateChildSHistory() {
3494 MOZ_ASSERT(IsTop());
3495 MOZ_ASSERT(GetHasSessionHistory());
3496 MOZ_ASSERT(!mChildSessionHistory);
3498 // Because session history is global in a browsing context tree, every process
3499 // that has access to a browsing context tree needs access to its session
3500 // history. That is why we create the ChildSHistory object in every process
3501 // where we have access to this browsing context (which is the top one).
3502 mChildSessionHistory = new ChildSHistory(this);
3504 // If the top browsing context (this one) is loaded in this process then we
3505 // also create the session history implementation for the child process.
3506 // This can be removed once session history is stored exclusively in the
3507 // parent process.
3508 mChildSessionHistory->SetIsInProcess(IsInProcess());
3511 void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
3512 bool aOldValue) {
3513 MOZ_ASSERT(GetHasSessionHistory() || !aOldValue,
3514 "We don't support turning off session history.");
3516 if (GetHasSessionHistory() && !aOldValue) {
3517 CreateChildSHistory();
3521 bool BrowsingContext::CanSet(
3522 FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
3523 const bool& aTargetTopLevelLinkClicksToBlankInternal,
3524 ContentParent* aSource) {
3525 return XRE_IsParentProcess() && !aSource && IsTop();
3528 bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
3529 ContentParent* aSource) {
3530 // We should only be able to set this for toplevel contexts which don't have
3531 // an ID yet.
3532 return GetBrowserId() == 0 && IsTop() && Children().IsEmpty();
3535 bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
3536 bool aNewValue, ContentParent* aSource) {
3537 // Can only be cleared from `true` to `false`, and should only ever be set on
3538 // the toplevel BrowsingContext.
3539 return IsTop() && GetPendingInitialization() && !aNewValue;
3542 bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
3543 ContentParent* aSource) {
3544 return IsTop();
3547 bool BrowsingContext::CanSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
3548 const bool& aIsUnderHiddenEmbedderElement,
3549 ContentParent* aSource) {
3550 return true;
3553 bool BrowsingContext::CanSet(FieldIndex<IDX_ForceOffline>, bool aNewValue,
3554 ContentParent* aSource) {
3555 return XRE_IsParentProcess() && !aSource;
3558 void BrowsingContext::DidSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
3559 bool aOldValue) {
3560 nsIDocShell* shell = GetDocShell();
3561 if (!shell) {
3562 return;
3564 const bool newValue = IsUnderHiddenEmbedderElement();
3565 if (NS_WARN_IF(aOldValue == newValue)) {
3566 return;
3569 if (auto* bc = BrowserChild::GetFrom(shell)) {
3570 bc->UpdateVisibility();
3573 if (PresShell* presShell = shell->GetPresShell()) {
3574 presShell->SetIsUnderHiddenEmbedderElement(newValue);
3577 // Propagate to children.
3578 for (BrowsingContext* child : Children()) {
3579 Element* embedderElement = child->GetEmbedderElement();
3580 if (!embedderElement) {
3581 // TODO: We shouldn't need to null check here since `child` and the
3582 // element returned by `child->GetEmbedderElement()` are in our
3583 // process (the actual browsing context represented by `child` may not
3584 // be, but that doesn't matter). However, there are currently a very
3585 // small number of crashes due to `embedderElement` being null, somehow
3586 // - see bug 1551241. For now we wallpaper the crash.
3587 continue;
3590 bool embedderFrameIsHidden = true;
3591 if (auto* embedderFrame = embedderElement->GetPrimaryFrame()) {
3592 embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
3595 bool hidden = IsUnderHiddenEmbedderElement() || embedderFrameIsHidden;
3596 if (child->IsUnderHiddenEmbedderElement() != hidden) {
3597 Unused << child->SetIsUnderHiddenEmbedderElement(hidden);
3602 bool BrowsingContext::IsPopupAllowed() {
3603 for (auto* context = GetCurrentWindowContext(); context;
3604 context = context->GetParentWindowContext()) {
3605 if (context->CanShowPopup()) {
3606 return true;
3610 return false;
3613 /* static */
3614 bool BrowsingContext::ShouldAddEntryForRefresh(
3615 nsIURI* aPreviousURI, const SessionHistoryInfo& aInfo) {
3616 return ShouldAddEntryForRefresh(aPreviousURI, aInfo.GetURI(),
3617 aInfo.HasPostData());
3620 /* static */
3621 bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aPreviousURI,
3622 nsIURI* aNewURI,
3623 bool aHasPostData) {
3624 if (aHasPostData) {
3625 return true;
3628 bool equalsURI = false;
3629 if (aPreviousURI) {
3630 aPreviousURI->Equals(aNewURI, &equalsURI);
3632 return !equalsURI;
3635 void BrowsingContext::SessionHistoryCommit(
3636 const LoadingSessionHistoryInfo& aInfo, uint32_t aLoadType,
3637 nsIURI* aPreviousURI, SessionHistoryInfo* aPreviousActiveEntry,
3638 bool aPersist, bool aCloneEntryChildren, bool aChannelExpired,
3639 uint32_t aCacheKey) {
3640 nsID changeID = {};
3641 if (XRE_IsContentProcess()) {
3642 RefPtr<ChildSHistory> rootSH = Top()->GetChildSessionHistory();
3643 if (rootSH) {
3644 if (!aInfo.mLoadIsFromSessionHistory) {
3645 // We try to mimic as closely as possible what will happen in
3646 // CanonicalBrowsingContext::SessionHistoryCommit. We'll be
3647 // incrementing the session history length if we're not replacing,
3648 // this is a top-level load or it's not the initial load in an iframe,
3649 // ShouldUpdateSessionHistory(loadType) returns true and it's not a
3650 // refresh for which ShouldAddEntryForRefresh returns false.
3651 // It is possible that this leads to wrong length temporarily, but
3652 // so would not having the check for replace.
3653 // Note that nsSHistory::AddEntry does a replace load if the current
3654 // entry is not marked as a persisted entry. The child process does
3655 // not have access to the current entry, so we use the previous active
3656 // entry as the best approximation. When that's not the current entry
3657 // then the length might be wrong briefly, until the parent process
3658 // commits the actual length.
3659 if (!LOAD_TYPE_HAS_FLAGS(
3660 aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) &&
3661 (IsTop()
3662 ? (!aPreviousActiveEntry || aPreviousActiveEntry->GetPersist())
3663 : !!aPreviousActiveEntry) &&
3664 ShouldUpdateSessionHistory(aLoadType) &&
3665 (!LOAD_TYPE_HAS_FLAGS(aLoadType,
3666 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) ||
3667 ShouldAddEntryForRefresh(aPreviousURI, aInfo.mInfo))) {
3668 changeID = rootSH->AddPendingHistoryChange();
3670 } else {
3671 // History load doesn't change the length, only index.
3672 changeID = rootSH->AddPendingHistoryChange(aInfo.mOffset, 0);
3675 ContentChild* cc = ContentChild::GetSingleton();
3676 mozilla::Unused << cc->SendHistoryCommit(
3677 this, aInfo.mLoadId, changeID, aLoadType, aPersist, aCloneEntryChildren,
3678 aChannelExpired, aCacheKey);
3679 } else {
3680 Canonical()->SessionHistoryCommit(aInfo.mLoadId, changeID, aLoadType,
3681 aPersist, aCloneEntryChildren,
3682 aChannelExpired, aCacheKey);
3686 void BrowsingContext::SetActiveSessionHistoryEntry(
3687 const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
3688 uint32_t aLoadType, uint32_t aUpdatedCacheKey, bool aUpdateLength) {
3689 if (XRE_IsContentProcess()) {
3690 // XXX Why we update cache key only in content process case?
3691 if (aUpdatedCacheKey != 0) {
3692 aInfo->SetCacheKey(aUpdatedCacheKey);
3695 nsID changeID = {};
3696 if (aUpdateLength) {
3697 RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
3698 if (shistory) {
3699 changeID = shistory->AddPendingHistoryChange();
3702 ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry(
3703 this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey,
3704 changeID);
3705 } else {
3706 Canonical()->SetActiveSessionHistoryEntry(
3707 aPreviousScrollPos, aInfo, aLoadType, aUpdatedCacheKey, nsID());
3711 void BrowsingContext::ReplaceActiveSessionHistoryEntry(
3712 SessionHistoryInfo* aInfo) {
3713 if (XRE_IsContentProcess()) {
3714 ContentChild::GetSingleton()->SendReplaceActiveSessionHistoryEntry(this,
3715 *aInfo);
3716 } else {
3717 Canonical()->ReplaceActiveSessionHistoryEntry(aInfo);
3721 void BrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
3722 if (XRE_IsContentProcess()) {
3723 ContentChild::GetSingleton()
3724 ->SendRemoveDynEntriesFromActiveSessionHistoryEntry(this);
3725 } else {
3726 Canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
3730 void BrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
3731 if (XRE_IsContentProcess()) {
3732 ContentChild::GetSingleton()->SendRemoveFromSessionHistory(this, aChangeID);
3733 } else {
3734 Canonical()->RemoveFromSessionHistory(aChangeID);
3738 void BrowsingContext::HistoryGo(
3739 int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
3740 bool aUserActivation, std::function<void(Maybe<int32_t>&&)>&& aResolver) {
3741 if (XRE_IsContentProcess()) {
3742 ContentChild::GetSingleton()->SendHistoryGo(
3743 this, aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3744 std::move(aResolver),
3745 [](mozilla::ipc::
3746 ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
3747 } else {
3748 RefPtr<CanonicalBrowsingContext> self = Canonical();
3749 aResolver(self->HistoryGo(
3750 aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3751 Canonical()->GetContentParent()
3752 ? Some(Canonical()->GetContentParent()->ChildID())
3753 : Nothing()));
3757 void BrowsingContext::SetChildSHistory(ChildSHistory* aChildSHistory) {
3758 mChildSessionHistory = aChildSHistory;
3759 mChildSessionHistory->SetBrowsingContext(this);
3760 mFields.SetWithoutSyncing<IDX_HasSessionHistory>(true);
3763 bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) {
3764 // We don't update session history on reload unless we're loading
3765 // an iframe in shift-reload case.
3766 return nsDocShell::ShouldUpdateGlobalHistory(aLoadType) &&
3767 (!(aLoadType & nsIDocShell::LOAD_CMD_RELOAD) ||
3768 (IsForceReloadType(aLoadType) && IsSubframe()));
3771 nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
3772 // We only rate limit non system callers
3773 if (aCallerType == CallerType::System) {
3774 return NS_OK;
3777 // Fetch rate limiting preferences
3778 uint32_t limitCount =
3779 StaticPrefs::dom_navigation_locationChangeRateLimit_count();
3780 uint32_t timeSpanSeconds =
3781 StaticPrefs::dom_navigation_locationChangeRateLimit_timespan();
3783 // Disable throttling if either of the preferences is set to 0.
3784 if (limitCount == 0 || timeSpanSeconds == 0) {
3785 return NS_OK;
3788 TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds);
3790 if (mLocationChangeRateLimitSpanStart.IsNull() ||
3791 ((TimeStamp::Now() - mLocationChangeRateLimitSpanStart) > throttleSpan)) {
3792 // Initial call or timespan exceeded, reset counter and timespan.
3793 mLocationChangeRateLimitSpanStart = TimeStamp::Now();
3794 mLocationChangeRateLimitCount = 1;
3795 return NS_OK;
3798 if (mLocationChangeRateLimitCount >= limitCount) {
3799 // Rate limit reached
3801 Document* doc = GetDocument();
3802 if (doc) {
3803 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc,
3804 nsContentUtils::eDOM_PROPERTIES,
3805 "LocChangeFloodingPrevented");
3808 return NS_ERROR_DOM_SECURITY_ERR;
3811 mLocationChangeRateLimitCount++;
3812 return NS_OK;
3815 void BrowsingContext::ResetLocationChangeRateLimit() {
3816 // Resetting the timestamp object will cause the check function to
3817 // init again and reset the rate limit.
3818 mLocationChangeRateLimitSpanStart = TimeStamp();
3821 void BrowsingContext::LocationCreated(dom::Location* aLocation) {
3822 MOZ_ASSERT(!aLocation->isInList());
3823 mLocations.insertBack(aLocation);
3826 void BrowsingContext::ClearCachedValuesOfLocations() {
3827 for (dom::Location* loc = mLocations.getFirst(); loc; loc = loc->getNext()) {
3828 loc->ClearCachedValues();
3832 } // namespace dom
3834 namespace ipc {
3836 void IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Write(
3837 IPC::MessageWriter* aWriter, IProtocol* aActor,
3838 const dom::MaybeDiscarded<dom::BrowsingContext>& aParam) {
3839 MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
3840 aParam.GetMaybeDiscarded()->EverAttached());
3841 uint64_t id = aParam.ContextId();
3842 WriteIPDLParam(aWriter, aActor, id);
3845 bool IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Read(
3846 IPC::MessageReader* aReader, IProtocol* aActor,
3847 dom::MaybeDiscarded<dom::BrowsingContext>* aResult) {
3848 uint64_t id = 0;
3849 if (!ReadIPDLParam(aReader, aActor, &id)) {
3850 return false;
3853 if (id == 0) {
3854 *aResult = nullptr;
3855 } else if (RefPtr<dom::BrowsingContext> bc = dom::BrowsingContext::Get(id)) {
3856 *aResult = std::move(bc);
3857 } else {
3858 aResult->SetDiscarded(id);
3860 return true;
3863 void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
3864 IPC::MessageWriter* aWriter, IProtocol* aActor,
3865 const dom::BrowsingContext::IPCInitializer& aInit) {
3866 // Write actor ID parameters.
3867 WriteIPDLParam(aWriter, aActor, aInit.mId);
3868 WriteIPDLParam(aWriter, aActor, aInit.mParentId);
3869 WriteIPDLParam(aWriter, aActor, aInit.mWindowless);
3870 WriteIPDLParam(aWriter, aActor, aInit.mUseRemoteTabs);
3871 WriteIPDLParam(aWriter, aActor, aInit.mUseRemoteSubframes);
3872 WriteIPDLParam(aWriter, aActor, aInit.mCreatedDynamically);
3873 WriteIPDLParam(aWriter, aActor, aInit.mChildOffset);
3874 WriteIPDLParam(aWriter, aActor, aInit.mOriginAttributes);
3875 WriteIPDLParam(aWriter, aActor, aInit.mRequestContextId);
3876 WriteIPDLParam(aWriter, aActor, aInit.mSessionHistoryIndex);
3877 WriteIPDLParam(aWriter, aActor, aInit.mSessionHistoryCount);
3878 WriteIPDLParam(aWriter, aActor, aInit.mFields);
3881 bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
3882 IPC::MessageReader* aReader, IProtocol* aActor,
3883 dom::BrowsingContext::IPCInitializer* aInit) {
3884 // Read actor ID parameters.
3885 if (!ReadIPDLParam(aReader, aActor, &aInit->mId) ||
3886 !ReadIPDLParam(aReader, aActor, &aInit->mParentId) ||
3887 !ReadIPDLParam(aReader, aActor, &aInit->mWindowless) ||
3888 !ReadIPDLParam(aReader, aActor, &aInit->mUseRemoteTabs) ||
3889 !ReadIPDLParam(aReader, aActor, &aInit->mUseRemoteSubframes) ||
3890 !ReadIPDLParam(aReader, aActor, &aInit->mCreatedDynamically) ||
3891 !ReadIPDLParam(aReader, aActor, &aInit->mChildOffset) ||
3892 !ReadIPDLParam(aReader, aActor, &aInit->mOriginAttributes) ||
3893 !ReadIPDLParam(aReader, aActor, &aInit->mRequestContextId) ||
3894 !ReadIPDLParam(aReader, aActor, &aInit->mSessionHistoryIndex) ||
3895 !ReadIPDLParam(aReader, aActor, &aInit->mSessionHistoryCount) ||
3896 !ReadIPDLParam(aReader, aActor, &aInit->mFields)) {
3897 return false;
3899 return true;
3902 template struct IPDLParamTraits<dom::BrowsingContext::BaseTransaction>;
3904 } // namespace ipc
3905 } // namespace mozilla