Bug 1885337 - Part 2: Implement to/from base64 methods. r=dminor
[gecko.git] / docshell / base / BrowsingContext.cpp
blob6e1a1b689398fa6c3c73f2f243ae02c67a4476c8
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/BindingIPCUtils.h"
24 #include "mozilla/dom/BrowserHost.h"
25 #include "mozilla/dom/BrowserChild.h"
26 #include "mozilla/dom/BrowserParent.h"
27 #include "mozilla/dom/BrowsingContextGroup.h"
28 #include "mozilla/dom/BrowsingContextBinding.h"
29 #include "mozilla/dom/ContentChild.h"
30 #include "mozilla/dom/ContentParent.h"
31 #include "mozilla/dom/Document.h"
32 #include "mozilla/dom/Element.h"
33 #include "mozilla/dom/HTMLEmbedElement.h"
34 #include "mozilla/dom/HTMLIFrameElement.h"
35 #include "mozilla/dom/Location.h"
36 #include "mozilla/dom/LocationBinding.h"
37 #include "mozilla/dom/MediaDevices.h"
38 #include "mozilla/dom/PopupBlocker.h"
39 #include "mozilla/dom/ScriptSettings.h"
40 #include "mozilla/dom/SessionStoreChild.h"
41 #include "mozilla/dom/SessionStorageManager.h"
42 #include "mozilla/dom/StructuredCloneTags.h"
43 #include "mozilla/dom/UserActivationIPCUtils.h"
44 #include "mozilla/dom/WindowBinding.h"
45 #include "mozilla/dom/WindowContext.h"
46 #include "mozilla/dom/WindowGlobalChild.h"
47 #include "mozilla/dom/WindowGlobalParent.h"
48 #include "mozilla/dom/WindowProxyHolder.h"
49 #include "mozilla/dom/SyncedContextInlines.h"
50 #include "mozilla/dom/XULFrameElement.h"
51 #include "mozilla/ipc/ProtocolUtils.h"
52 #include "mozilla/net/DocumentLoadListener.h"
53 #include "mozilla/net/RequestContextService.h"
54 #include "mozilla/Assertions.h"
55 #include "mozilla/AsyncEventDispatcher.h"
56 #include "mozilla/ClearOnShutdown.h"
57 #include "mozilla/Components.h"
58 #include "mozilla/HashTable.h"
59 #include "mozilla/Logging.h"
60 #include "mozilla/MediaFeatureChange.h"
61 #include "mozilla/ResultExtensions.h"
62 #include "mozilla/Services.h"
63 #include "mozilla/StaticPrefs_fission.h"
64 #include "mozilla/StaticPrefs_media.h"
65 #include "mozilla/StaticPrefs_page_load.h"
66 #include "mozilla/StaticPtr.h"
67 #include "mozilla/URLQueryStringStripper.h"
68 #include "mozilla/EventStateManager.h"
69 #include "nsIURIFixup.h"
70 #include "nsIXULRuntime.h"
72 #include "nsDocShell.h"
73 #include "nsDocShellLoadState.h"
74 #include "nsFocusManager.h"
75 #include "nsGlobalWindowInner.h"
76 #include "nsGlobalWindowOuter.h"
77 #include "PresShell.h"
78 #include "nsIObserverService.h"
79 #include "nsISHistory.h"
80 #include "nsContentUtils.h"
81 #include "nsQueryObject.h"
82 #include "nsSandboxFlags.h"
83 #include "nsScriptError.h"
84 #include "nsThreadUtils.h"
85 #include "xpcprivate.h"
87 #include "AutoplayPolicy.h"
88 #include "GVAutoplayRequestStatusIPC.h"
90 extern mozilla::LazyLogModule gAutoplayPermissionLog;
91 extern mozilla::LazyLogModule gTimeoutDeferralLog;
93 #define AUTOPLAY_LOG(msg, ...) \
94 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
96 namespace IPC {
97 // Allow serialization and deserialization of OrientationType over IPC
98 template <>
99 struct ParamTraits<mozilla::dom::OrientationType>
100 : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::OrientationType> {
103 template <>
104 struct ParamTraits<mozilla::dom::DisplayMode>
105 : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::DisplayMode> {};
107 template <>
108 struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride>
109 : public mozilla::dom::WebIDLEnumSerializer<
110 mozilla::dom::PrefersColorSchemeOverride> {};
112 template <>
113 struct ParamTraits<mozilla::dom::ExplicitActiveStatus>
114 : public ContiguousEnumSerializer<
115 mozilla::dom::ExplicitActiveStatus,
116 mozilla::dom::ExplicitActiveStatus::None,
117 mozilla::dom::ExplicitActiveStatus::EndGuard_> {};
119 // Allow serialization and deserialization of TouchEventsOverride over IPC
120 template <>
121 struct ParamTraits<mozilla::dom::TouchEventsOverride>
122 : public mozilla::dom::WebIDLEnumSerializer<
123 mozilla::dom::TouchEventsOverride> {};
125 template <>
126 struct ParamTraits<mozilla::dom::EmbedderColorSchemes> {
127 using paramType = mozilla::dom::EmbedderColorSchemes;
129 static void Write(MessageWriter* aWriter, const paramType& aParam) {
130 WriteParam(aWriter, aParam.mUsed);
131 WriteParam(aWriter, aParam.mPreferred);
134 static bool Read(MessageReader* aReader, paramType* aResult) {
135 return ReadParam(aReader, &aResult->mUsed) &&
136 ReadParam(aReader, &aResult->mPreferred);
140 } // namespace IPC
142 namespace mozilla {
143 namespace dom {
145 // Explicit specialization of the `Transaction` type. Required by the `extern
146 // template class` declaration in the header.
147 template class syncedcontext::Transaction<BrowsingContext>;
149 extern mozilla::LazyLogModule gUserInteractionPRLog;
151 #define USER_ACTIVATION_LOG(msg, ...) \
152 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
154 static LazyLogModule gBrowsingContextLog("BrowsingContext");
155 static LazyLogModule gBrowsingContextSyncLog("BrowsingContextSync");
157 typedef nsTHashMap<nsUint64HashKey, BrowsingContext*> BrowsingContextMap;
159 // All BrowsingContexts indexed by Id
160 static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts;
161 // Top-level Content BrowsingContexts only, indexed by BrowserId instead of Id
162 static StaticAutoPtr<BrowsingContextMap> sCurrentTopByBrowserId;
164 static bool gIPCEnabledAnnotation = false;
165 static bool gFissionEnabledAnnotation = false;
167 static void UnregisterBrowserId(BrowsingContext* aBrowsingContext) {
168 if (!aBrowsingContext->IsTopContent() || !sCurrentTopByBrowserId) {
169 return;
172 // Avoids an extra lookup
173 auto browserIdEntry =
174 sCurrentTopByBrowserId->Lookup(aBrowsingContext->BrowserId());
175 if (browserIdEntry && browserIdEntry.Data() == aBrowsingContext) {
176 browserIdEntry.Remove();
180 static void Register(BrowsingContext* aBrowsingContext) {
181 sBrowsingContexts->InsertOrUpdate(aBrowsingContext->Id(), aBrowsingContext);
182 if (aBrowsingContext->IsTopContent()) {
183 sCurrentTopByBrowserId->InsertOrUpdate(aBrowsingContext->BrowserId(),
184 aBrowsingContext);
187 aBrowsingContext->Group()->Register(aBrowsingContext);
190 // static
191 void BrowsingContext::UpdateCurrentTopByBrowserId(
192 BrowsingContext* aNewBrowsingContext) {
193 if (aNewBrowsingContext->IsTopContent()) {
194 sCurrentTopByBrowserId->InsertOrUpdate(aNewBrowsingContext->BrowserId(),
195 aNewBrowsingContext);
199 BrowsingContext* BrowsingContext::GetParent() const {
200 return mParentWindow ? mParentWindow->GetBrowsingContext() : nullptr;
203 bool BrowsingContext::IsInSubtreeOf(BrowsingContext* aContext) {
204 BrowsingContext* bc = this;
205 do {
206 if (bc == aContext) {
207 return true;
209 } while ((bc = bc->GetParent()));
210 return false;
213 BrowsingContext* BrowsingContext::Top() {
214 BrowsingContext* bc = this;
215 while (bc->mParentWindow) {
216 bc = bc->GetParent();
218 return bc;
221 const BrowsingContext* BrowsingContext::Top() const {
222 const BrowsingContext* bc = this;
223 while (bc->mParentWindow) {
224 bc = bc->GetParent();
226 return bc;
229 int32_t BrowsingContext::IndexOf(BrowsingContext* aChild) {
230 int32_t index = -1;
231 for (BrowsingContext* child : Children()) {
232 ++index;
233 if (child == aChild) {
234 break;
237 return index;
240 WindowContext* BrowsingContext::GetTopWindowContext() const {
241 if (mParentWindow) {
242 return mParentWindow->TopWindowContext();
244 return mCurrentWindowContext;
247 /* static */
248 void BrowsingContext::Init() {
249 if (!sBrowsingContexts) {
250 sBrowsingContexts = new BrowsingContextMap();
251 sCurrentTopByBrowserId = new BrowsingContextMap();
252 ClearOnShutdown(&sBrowsingContexts);
253 ClearOnShutdown(&sCurrentTopByBrowserId);
254 CrashReporter::RegisterAnnotationBool(
255 CrashReporter::Annotation::DOMIPCEnabled, &gIPCEnabledAnnotation);
256 CrashReporter::RegisterAnnotationBool(
257 CrashReporter::Annotation::DOMFissionEnabled,
258 &gFissionEnabledAnnotation);
262 /* static */
263 LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; }
265 /* static */
266 LogModule* BrowsingContext::GetSyncLog() { return gBrowsingContextSyncLog; }
268 /* static */
269 already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) {
270 return do_AddRef(sBrowsingContexts->Get(aId));
273 /* static */
274 already_AddRefed<BrowsingContext> BrowsingContext::GetCurrentTopByBrowserId(
275 uint64_t aBrowserId) {
276 return do_AddRef(sCurrentTopByBrowserId->Get(aBrowserId));
279 /* static */
280 already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow(
281 WindowProxyHolder& aProxy) {
282 return do_AddRef(aProxy.get());
285 CanonicalBrowsingContext* BrowsingContext::Canonical() {
286 return CanonicalBrowsingContext::Cast(this);
289 bool BrowsingContext::IsOwnedByProcess() const {
290 return mIsInProcess && mDocShell &&
291 !nsDocShell::Cast(mDocShell)->WillChangeProcess();
294 bool BrowsingContext::SameOriginWithTop() {
295 MOZ_ASSERT(IsInProcess());
296 // If the top BrowsingContext is not same-process to us, it is cross-origin
297 if (!Top()->IsInProcess()) {
298 return false;
301 nsIDocShell* docShell = GetDocShell();
302 if (!docShell) {
303 return false;
305 Document* doc = docShell->GetDocument();
306 if (!doc) {
307 return false;
309 nsIPrincipal* principal = doc->NodePrincipal();
311 nsIDocShell* topDocShell = Top()->GetDocShell();
312 if (!topDocShell) {
313 return false;
315 Document* topDoc = topDocShell->GetDocument();
316 if (!topDoc) {
317 return false;
319 nsIPrincipal* topPrincipal = topDoc->NodePrincipal();
321 return principal->Equals(topPrincipal);
324 /* static */
325 already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached(
326 nsGlobalWindowInner* aParent, BrowsingContext* aOpener,
327 BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType,
328 bool aIsPopupRequested, bool aCreatedDynamically) {
329 if (aParent) {
330 MOZ_DIAGNOSTIC_ASSERT(aParent->GetWindowContext());
331 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->mType == aType);
332 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->GetBrowserId() != 0);
335 MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess());
337 uint64_t id = nsContentUtils::GenerateBrowsingContextId();
339 MOZ_LOG(GetLog(), LogLevel::Debug,
340 ("Creating 0x%08" PRIx64 " in %s", id,
341 XRE_IsParentProcess() ? "Parent" : "Child"));
343 RefPtr<BrowsingContext> parentBC =
344 aParent ? aParent->GetBrowsingContext() : nullptr;
345 RefPtr<WindowContext> parentWC =
346 aParent ? aParent->GetWindowContext() : nullptr;
347 BrowsingContext* inherit = parentBC ? parentBC.get() : aOpener;
349 // Determine which BrowsingContextGroup this context should be created in.
350 RefPtr<BrowsingContextGroup> group = aSpecificGroup;
351 if (aType == Type::Chrome) {
352 MOZ_DIAGNOSTIC_ASSERT(!group);
353 group = BrowsingContextGroup::GetChromeGroup();
354 } else if (!group) {
355 group = BrowsingContextGroup::Select(parentWC, aOpener);
358 // Configure initial values for synced fields.
359 FieldValues fields;
360 fields.Get<IDX_Name>() = aName;
362 if (aOpener) {
363 MOZ_DIAGNOSTIC_ASSERT(!aParent,
364 "new BC with both initial opener and parent");
365 MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == group);
366 MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == aType);
367 fields.Get<IDX_OpenerId>() = aOpener->Id();
368 fields.Get<IDX_HadOriginalOpener>() = true;
370 if (aType == Type::Chrome && !aParent) {
371 // See SetOpener for why we do this inheritance.
372 fields.Get<IDX_PrefersColorSchemeOverride>() =
373 aOpener->Top()->GetPrefersColorSchemeOverride();
377 if (aParent) {
378 MOZ_DIAGNOSTIC_ASSERT(parentBC->Group() == group);
379 MOZ_DIAGNOSTIC_ASSERT(parentBC->mType == aType);
380 fields.Get<IDX_EmbedderInnerWindowId>() = aParent->WindowID();
381 // Non-toplevel content documents are always embededed within content.
382 fields.Get<IDX_EmbeddedInContentDocument>() =
383 parentBC->mType == Type::Content;
385 // XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug
386 // 1608448) Check if the parent was itself loading already
387 auto readystate = aParent->GetDocument()->GetReadyStateEnum();
388 fields.Get<IDX_AncestorLoading>() =
389 parentBC->GetAncestorLoading() ||
390 readystate == Document::ReadyState::READYSTATE_LOADING ||
391 readystate == Document::ReadyState::READYSTATE_INTERACTIVE;
394 fields.Get<IDX_BrowserId>() =
395 parentBC ? parentBC->GetBrowserId() : nsContentUtils::GenerateBrowserId();
397 fields.Get<IDX_OpenerPolicy>() = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
398 if (aOpener && aOpener->SameOriginWithTop()) {
399 // We inherit the opener policy if there is a creator and if the creator's
400 // origin is same origin with the creator's top-level origin.
401 // If it is cross origin we should not inherit the CrossOriginOpenerPolicy
402 fields.Get<IDX_OpenerPolicy>() = aOpener->Top()->GetOpenerPolicy();
404 // If we inherit a policy which is potentially cross-origin isolated, we
405 // must be in a potentially cross-origin isolated BCG.
406 bool isPotentiallyCrossOriginIsolated =
407 fields.Get<IDX_OpenerPolicy>() ==
408 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
409 MOZ_RELEASE_ASSERT(isPotentiallyCrossOriginIsolated ==
410 group->IsPotentiallyCrossOriginIsolated());
411 } else if (aOpener) {
412 // They are not same origin
413 auto topPolicy = aOpener->Top()->GetOpenerPolicy();
414 MOZ_RELEASE_ASSERT(topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE ||
415 topPolicy ==
416 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS);
417 } else if (!aParent && group->IsPotentiallyCrossOriginIsolated()) {
418 // If we're creating a brand-new toplevel BC in a potentially cross-origin
419 // isolated group, it should start out with a strict opener policy.
420 fields.Get<IDX_OpenerPolicy>() =
421 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
424 fields.Get<IDX_HistoryID>() = nsID::GenerateUUID();
425 fields.Get<IDX_ExplicitActive>() = [&] {
426 if (parentBC || aType == Type::Content) {
427 // Non-root browsing-contexts inherit their status from its parent.
428 // Top-content either gets managed by the top chrome, or gets manually
429 // managed by the front-end (see ManuallyManagesActiveness). In any case
430 // we want to start off as inactive.
431 return ExplicitActiveStatus::None;
433 // Chrome starts as active.
434 return ExplicitActiveStatus::Active;
435 }();
437 fields.Get<IDX_FullZoom>() = parentBC ? parentBC->FullZoom() : 1.0f;
438 fields.Get<IDX_TextZoom>() = parentBC ? parentBC->TextZoom() : 1.0f;
440 bool allowContentRetargeting =
441 inherit ? inherit->GetAllowContentRetargetingOnChildren() : true;
442 fields.Get<IDX_AllowContentRetargeting>() = allowContentRetargeting;
443 fields.Get<IDX_AllowContentRetargetingOnChildren>() = allowContentRetargeting;
445 // Assume top allows fullscreen for its children unless otherwise stated.
446 // Subframes start with it false unless otherwise noted in SetEmbedderElement.
447 fields.Get<IDX_FullscreenAllowedByOwner>() = !aParent;
449 fields.Get<IDX_DefaultLoadFlags>() =
450 inherit ? inherit->GetDefaultLoadFlags() : nsIRequest::LOAD_NORMAL;
452 fields.Get<IDX_OrientationLock>() = mozilla::hal::ScreenOrientation::None;
454 fields.Get<IDX_UseGlobalHistory>() =
455 inherit ? inherit->GetUseGlobalHistory() : false;
457 fields.Get<IDX_UseErrorPages>() = true;
459 fields.Get<IDX_TouchEventsOverrideInternal>() = TouchEventsOverride::None;
461 fields.Get<IDX_AllowJavascript>() =
462 inherit ? inherit->GetAllowJavascript() : true;
464 fields.Get<IDX_IsPopupRequested>() = aIsPopupRequested;
466 if (!parentBC) {
467 fields.Get<IDX_ShouldDelayMediaFromStart>() =
468 StaticPrefs::media_block_autoplay_until_in_foreground();
471 RefPtr<BrowsingContext> context;
472 if (XRE_IsParentProcess()) {
473 context = new CanonicalBrowsingContext(parentWC, group, id,
474 /* aOwnerProcessId */ 0,
475 /* aEmbedderProcessId */ 0, aType,
476 std::move(fields));
477 } else {
478 context =
479 new BrowsingContext(parentWC, group, id, aType, std::move(fields));
482 context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent;
483 context->mCreatedDynamically = aCreatedDynamically;
484 if (inherit) {
485 context->mPrivateBrowsingId = inherit->mPrivateBrowsingId;
486 context->mUseRemoteTabs = inherit->mUseRemoteTabs;
487 context->mUseRemoteSubframes = inherit->mUseRemoteSubframes;
488 context->mOriginAttributes = inherit->mOriginAttributes;
491 nsCOMPtr<nsIRequestContextService> rcsvc =
492 net::RequestContextService::GetOrCreate();
493 if (rcsvc) {
494 nsCOMPtr<nsIRequestContext> requestContext;
495 nsresult rv = rcsvc->NewRequestContext(getter_AddRefs(requestContext));
496 if (NS_SUCCEEDED(rv) && requestContext) {
497 context->mRequestContextId = requestContext->GetID();
501 return context.forget();
504 already_AddRefed<BrowsingContext> BrowsingContext::CreateIndependent(
505 Type aType) {
506 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
507 "BCs created in the content process must be related to "
508 "some BrowserChild");
509 RefPtr<BrowsingContext> bc(
510 CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, false));
511 bc->mWindowless = bc->IsContent();
512 bc->mEmbeddedByThisProcess = true;
513 bc->EnsureAttached();
514 return bc.forget();
517 void BrowsingContext::EnsureAttached() {
518 if (!mEverAttached) {
519 Register(this);
521 // Attach the browsing context to the tree.
522 Attach(/* aFromIPC */ false, /* aOriginProcess */ nullptr);
526 /* static */
527 mozilla::ipc::IPCResult BrowsingContext::CreateFromIPC(
528 BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup,
529 ContentParent* aOriginProcess) {
530 MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess());
531 MOZ_DIAGNOSTIC_ASSERT(aGroup);
533 uint64_t originId = 0;
534 if (aOriginProcess) {
535 originId = aOriginProcess->ChildID();
536 aGroup->EnsureHostProcess(aOriginProcess);
539 MOZ_LOG(GetLog(), LogLevel::Debug,
540 ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")",
541 aInit.mId, originId));
543 RefPtr<WindowContext> parent = aInit.GetParent();
545 RefPtr<BrowsingContext> context;
546 if (XRE_IsParentProcess()) {
547 // If the new BrowsingContext has a parent, it is a sub-frame embedded in
548 // whatever process sent the message. If it doesn't, and is not windowless,
549 // it is a new window or tab, and will be embedded in the parent process.
550 uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0;
551 context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId,
552 embedderProcessId, Type::Content,
553 std::move(aInit.mFields));
554 } else {
555 context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content,
556 std::move(aInit.mFields));
559 context->mWindowless = aInit.mWindowless;
560 context->mCreatedDynamically = aInit.mCreatedDynamically;
561 context->mChildOffset = aInit.mChildOffset;
562 if (context->GetHasSessionHistory()) {
563 context->CreateChildSHistory();
564 if (mozilla::SessionHistoryInParent()) {
565 context->GetChildSessionHistory()->SetIndexAndLength(
566 aInit.mSessionHistoryIndex, aInit.mSessionHistoryCount, nsID());
570 // NOTE: Call through the `Set` methods for these values to ensure that any
571 // relevant process-local state is also updated.
572 context->SetOriginAttributes(aInit.mOriginAttributes);
573 context->SetRemoteTabs(aInit.mUseRemoteTabs);
574 context->SetRemoteSubframes(aInit.mUseRemoteSubframes);
575 context->mRequestContextId = aInit.mRequestContextId;
576 // NOTE: Private browsing ID is set by `SetOriginAttributes`.
578 Register(context);
580 return context->Attach(/* aFromIPC */ true, aOriginProcess);
583 BrowsingContext::BrowsingContext(WindowContext* aParentWindow,
584 BrowsingContextGroup* aGroup,
585 uint64_t aBrowsingContextId, Type aType,
586 FieldValues&& aInit)
587 : mFields(std::move(aInit)),
588 mType(aType),
589 mBrowsingContextId(aBrowsingContextId),
590 mGroup(aGroup),
591 mParentWindow(aParentWindow),
592 mPrivateBrowsingId(0),
593 mEverAttached(false),
594 mIsInProcess(false),
595 mIsDiscarded(false),
596 mWindowless(false),
597 mDanglingRemoteOuterProxies(false),
598 mEmbeddedByThisProcess(false),
599 mUseRemoteTabs(false),
600 mUseRemoteSubframes(false),
601 mCreatedDynamically(false),
602 mIsInBFCache(false),
603 mCanExecuteScripts(true),
604 mChildOffset(0) {
605 MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup);
606 MOZ_RELEASE_ASSERT(mBrowsingContextId != 0);
607 MOZ_RELEASE_ASSERT(mGroup);
610 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) {
611 // XXX(nika): We should communicate that we are now an active BrowsingContext
612 // process to the parent & do other validation here.
613 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
614 MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this);
615 mDocShell = aDocShell;
616 mDanglingRemoteOuterProxies = !mIsInProcess;
617 mIsInProcess = true;
618 if (mChildSessionHistory) {
619 mChildSessionHistory->SetIsInProcess(true);
622 RecomputeCanExecuteScripts();
623 ClearCachedValuesOfLocations();
626 // This class implements a callback that will return the remote window proxy for
627 // mBrowsingContext in that compartment, if it has one. It also removes the
628 // proxy from the map, because the object will be transplanted into another kind
629 // of object.
630 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback
631 : public js::CompartmentTransplantCallback {
632 public:
633 explicit CompartmentRemoteProxyTransplantCallback(
634 BrowsingContext* aBrowsingContext)
635 : mBrowsingContext(aBrowsingContext) {}
637 virtual JSObject* getObjectToTransplant(
638 JS::Compartment* compartment) override {
639 auto* priv = xpc::CompartmentPrivate::Get(compartment);
640 if (!priv) {
641 return nullptr;
644 auto& map = priv->GetRemoteProxyMap();
645 auto result = map.lookup(mBrowsingContext);
646 if (!result) {
647 return nullptr;
649 JSObject* resultObject = result->value();
650 map.remove(result);
652 return resultObject;
655 private:
656 BrowsingContext* mBrowsingContext;
659 void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies(
660 JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) {
661 if (!mDanglingRemoteOuterProxies) {
662 return;
664 mDanglingRemoteOuterProxies = false;
666 CompartmentRemoteProxyTransplantCallback cb(this);
667 js::RemapRemoteWindowProxies(aCx, &cb, aOuter);
670 bool BrowsingContext::IsActive() const {
671 const BrowsingContext* current = this;
672 do {
673 auto explicit_ = current->GetExplicitActive();
674 if (explicit_ != ExplicitActiveStatus::None) {
675 return explicit_ == ExplicitActiveStatus::Active;
677 if (mParentWindow && !mParentWindow->IsCurrent()) {
678 return false;
680 } while ((current = current->GetParent()));
682 return false;
685 bool BrowsingContext::GetIsActiveBrowserWindow() {
686 if (!XRE_IsParentProcess()) {
687 return Top()->GetIsActiveBrowserWindowInternal();
690 // chrome:// urls loaded in the parent won't receive
691 // their own activation so we defer to the top chrome
692 // Browsing Context when in the parent process.
693 return Canonical()
694 ->TopCrossChromeBoundary()
695 ->GetIsActiveBrowserWindowInternal();
698 void BrowsingContext::SetIsActiveBrowserWindow(bool aActive) {
699 Unused << SetIsActiveBrowserWindowInternal(aActive);
702 bool BrowsingContext::FullscreenAllowed() const {
703 for (auto* current = this; current; current = current->GetParent()) {
704 if (!current->GetFullscreenAllowedByOwner()) {
705 return false;
708 return true;
711 static bool OwnerAllowsFullscreen(const Element& aEmbedder) {
712 if (aEmbedder.IsXULElement()) {
713 return !aEmbedder.HasAttr(nsGkAtoms::disablefullscreen);
715 if (aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) {
716 // This is controlled by feature policy.
717 return true;
719 if (const auto* embed = HTMLEmbedElement::FromNode(aEmbedder)) {
720 return embed->AllowFullscreen();
722 return false;
725 void BrowsingContext::SetEmbedderElement(Element* aEmbedder) {
726 mEmbeddedByThisProcess = true;
728 // Update embedder-element-specific fields in a shared transaction.
729 // Don't do this when clearing our embedder, as we're being destroyed either
730 // way.
731 if (aEmbedder) {
732 Transaction txn;
733 txn.SetEmbedderElementType(Some(aEmbedder->LocalName()));
734 txn.SetEmbeddedInContentDocument(
735 aEmbedder->OwnerDoc()->IsContentDocument());
736 if (nsCOMPtr<nsPIDOMWindowInner> inner =
737 do_QueryInterface(aEmbedder->GetOwnerGlobal())) {
738 txn.SetEmbedderInnerWindowId(inner->WindowID());
740 txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder));
741 if (XRE_IsParentProcess() && aEmbedder->IsXULElement() && IsTopContent()) {
742 nsAutoString messageManagerGroup;
743 aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup);
744 txn.SetMessageManagerGroup(messageManagerGroup);
745 txn.SetUseGlobalHistory(
746 !aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory));
747 if (!aEmbedder->HasAttr(nsGkAtoms::manualactiveness)) {
748 // We're active iff the parent cross-chrome-boundary is active. Note we
749 // can't just use this->Canonical()->GetParentCrossChromeBoundary here,
750 // since mEmbedderElement is still null at this point.
751 RefPtr bc = aEmbedder->OwnerDoc()->GetBrowsingContext();
752 const bool isActive = bc && bc->IsActive();
753 txn.SetExplicitActive(isActive ? ExplicitActiveStatus::Active
754 : ExplicitActiveStatus::Inactive);
755 if (auto* bp = Canonical()->GetBrowserParent()) {
756 bp->SetRenderLayers(isActive);
761 MOZ_ALWAYS_SUCCEEDS(txn.Commit(this));
764 if (XRE_IsParentProcess() && IsTopContent()) {
765 Canonical()->MaybeSetPermanentKey(aEmbedder);
768 mEmbedderElement = aEmbedder;
770 if (mEmbedderElement) {
771 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
772 obs->NotifyWhenScriptSafe(ToSupports(this),
773 "browsing-context-did-set-embedder", nullptr);
776 if (IsEmbedderTypeObjectOrEmbed()) {
777 Unused << SetSyntheticDocumentContainer(true);
782 bool BrowsingContext::IsEmbedderTypeObjectOrEmbed() {
783 if (const Maybe<nsString>& type = GetEmbedderElementType()) {
784 return nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type);
786 return false;
789 void BrowsingContext::Embed() {
790 if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) {
791 frame->BindToBrowsingContext(this);
795 mozilla::ipc::IPCResult BrowsingContext::Attach(bool aFromIPC,
796 ContentParent* aOriginProcess) {
797 MOZ_DIAGNOSTIC_ASSERT(!mEverAttached);
798 MOZ_DIAGNOSTIC_ASSERT_IF(aFromIPC, aOriginProcess || XRE_IsContentProcess());
799 mEverAttached = true;
801 if (MOZ_LOG_TEST(GetLog(), LogLevel::Debug)) {
802 nsAutoCString suffix;
803 mOriginAttributes.CreateSuffix(suffix);
804 MOZ_LOG(GetLog(), LogLevel::Debug,
805 ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64
806 " (private=%d, remote=%d, fission=%d, oa=%s)",
807 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
808 GetParent() ? GetParent()->Id() : 0, (int)mPrivateBrowsingId,
809 (int)mUseRemoteTabs, (int)mUseRemoteSubframes, suffix.get()));
812 MOZ_DIAGNOSTIC_ASSERT(mGroup);
813 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
815 if (mGroup->IsPotentiallyCrossOriginIsolated() !=
816 (Top()->GetOpenerPolicy() ==
817 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP)) {
818 MOZ_DIAGNOSTIC_ASSERT(aFromIPC);
819 if (aFromIPC) {
820 auto* actor = aOriginProcess
821 ? static_cast<mozilla::ipc::IProtocol*>(aOriginProcess)
822 : static_cast<mozilla::ipc::IProtocol*>(
823 ContentChild::GetSingleton());
824 return IPC_FAIL(
825 actor,
826 "Invalid CrossOriginIsolated state in BrowsingContext::Attach call");
827 } else {
828 MOZ_CRASH(
829 "Invalid CrossOriginIsolated state in BrowsingContext::Attach call");
833 AssertCoherentLoadContext();
835 // Add ourselves either to our parent or BrowsingContextGroup's child list.
836 // Important: We shouldn't return IPC_FAIL after this point, since the
837 // BrowsingContext will have already been added to the tree.
838 if (mParentWindow) {
839 if (!aFromIPC) {
840 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow->IsDiscarded(),
841 "local attach in discarded window");
842 MOZ_DIAGNOSTIC_ASSERT(!GetParent()->IsDiscarded(),
843 "local attach call in discarded bc");
844 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild(),
845 "local attach call with oop parent window");
846 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild()->CanSend(),
847 "local attach call with dead parent window");
849 mChildOffset =
850 mCreatedDynamically ? -1 : mParentWindow->Children().Length();
851 mParentWindow->AppendChildBrowsingContext(this);
852 RecomputeCanExecuteScripts();
853 } else {
854 mGroup->Toplevels().AppendElement(this);
857 if (GetIsPopupSpam()) {
858 PopupBlocker::RegisterOpenPopupSpam();
861 if (IsTop() && GetHasSessionHistory() && !mChildSessionHistory) {
862 CreateChildSHistory();
865 // Why the context is being attached. This will always be "attach" in the
866 // content process, but may be "replace" if it's known the context being
867 // replaced in the parent process.
868 const char16_t* why = u"attach";
870 if (XRE_IsContentProcess() && !aFromIPC) {
871 // Send attach to our parent if we need to.
872 ContentChild::GetSingleton()->SendCreateBrowsingContext(
873 mGroup->Id(), GetIPCInitializer());
874 } else if (XRE_IsParentProcess()) {
875 // If this window was created as a subframe by a content process, it must be
876 // being hosted within the same BrowserParent as its mParentWindow.
877 // Toplevel BrowsingContexts created by content have their BrowserParent
878 // configured during `RecvConstructPopupBrowser`.
879 if (mParentWindow && aOriginProcess) {
880 MOZ_DIAGNOSTIC_ASSERT(
881 mParentWindow->Canonical()->GetContentParent() == aOriginProcess,
882 "Creator process isn't the same as our embedder?");
883 Canonical()->SetCurrentBrowserParent(
884 mParentWindow->Canonical()->GetBrowserParent());
887 mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) {
888 MOZ_DIAGNOSTIC_ASSERT(IsContent(),
889 "chrome BCG cannot be synced to content process");
890 if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID())) {
891 Unused << aParent->SendCreateBrowsingContext(mGroup->Id(),
892 GetIPCInitializer());
896 if (IsTop() && IsContent() && Canonical()->GetWebProgress()) {
897 why = u"replace";
900 // We want to create a BrowsingContextWebProgress for all content
901 // BrowsingContexts.
902 if (IsContent() && !Canonical()->mWebProgress) {
903 Canonical()->mWebProgress = new BrowsingContextWebProgress(Canonical());
907 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
908 obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached",
909 why);
912 if (XRE_IsParentProcess()) {
913 Canonical()->CanonicalAttach();
915 return IPC_OK();
918 void BrowsingContext::Detach(bool aFromIPC) {
919 MOZ_LOG(GetLog(), LogLevel::Debug,
920 ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64,
921 XRE_IsParentProcess() ? "Parent" : "Child", Id(),
922 GetParent() ? GetParent()->Id() : 0));
924 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
925 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded);
927 if (XRE_IsParentProcess()) {
928 Canonical()->AddPendingDiscard();
930 auto callListeners =
931 MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] {
932 for (const auto& listener : listeners) {
933 listener(id);
935 if (XRE_IsParentProcess()) {
936 Canonical()->RemovePendingDiscard();
940 nsCOMPtr<nsIRequestContextService> rcsvc =
941 net::RequestContextService::GetOrCreate();
942 if (rcsvc) {
943 rcsvc->RemoveRequestContext(GetRequestContextId());
946 // This will only ever be null if the cycle-collector has unlinked us. Don't
947 // try to detach ourselves in that case.
948 if (NS_WARN_IF(!mGroup)) {
949 MOZ_ASSERT_UNREACHABLE();
950 return;
953 if (mParentWindow) {
954 mParentWindow->RemoveChildBrowsingContext(this);
955 } else {
956 mGroup->Toplevels().RemoveElement(this);
959 if (XRE_IsParentProcess()) {
960 RefPtr<CanonicalBrowsingContext> self{Canonical()};
961 Group()->EachParent([&](ContentParent* aParent) {
962 // Only the embedder process is allowed to initiate a BrowsingContext
963 // detach, so if we've gotten here, the host process already knows we've
964 // been detached, and there's no need to tell it again.
966 // If the owner process is not the same as the embedder process, its
967 // BrowsingContext will be detached when its nsWebBrowser instance is
968 // destroyed.
969 bool doDiscard = !Canonical()->IsEmbeddedInProcess(aParent->ChildID()) &&
970 !Canonical()->IsOwnedByProcess(aParent->ChildID());
972 // Hold a strong reference to ourself, and keep our BrowsingContextGroup
973 // alive, until the responses comes back to ensure we don't die while
974 // messages relating to this context are in-flight.
976 // When the callback is called, the keepalive on our group will be
977 // destroyed, and the reference to the BrowsingContext will be dropped,
978 // which may cause it to be fully destroyed.
979 mGroup->AddKeepAlive();
980 self->AddPendingDiscard();
981 auto callback = [self](auto) {
982 self->mGroup->RemoveKeepAlive();
983 self->RemovePendingDiscard();
986 aParent->SendDiscardBrowsingContext(this, doDiscard, callback, callback);
988 } else {
989 // Hold a strong reference to ourself until the responses come back to
990 // ensure the BrowsingContext isn't cleaned up before the parent process
991 // acknowledges the discard request.
992 auto callback = [self = RefPtr{this}](auto) {};
993 ContentChild::GetSingleton()->SendDiscardBrowsingContext(
994 this, !aFromIPC, callback, callback);
997 mGroup->Unregister(this);
998 UnregisterBrowserId(this);
999 mIsDiscarded = true;
1001 if (XRE_IsParentProcess()) {
1002 nsFocusManager* fm = nsFocusManager::GetFocusManager();
1003 if (fm) {
1004 fm->BrowsingContextDetached(this);
1008 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1009 // Why the context is being discarded. This will always be "discard" in the
1010 // content process, but may be "replace" if it's known the context being
1011 // replaced in the parent process.
1012 const char16_t* why = u"discard";
1013 if (XRE_IsParentProcess() && IsTop() && !Canonical()->GetWebProgress()) {
1014 why = u"replace";
1016 obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", why);
1019 // NOTE: Doesn't use SetClosed, as it will be set in all processes
1020 // automatically by calls to Detach()
1021 mFields.SetWithoutSyncing<IDX_Closed>(true);
1023 if (GetIsPopupSpam()) {
1024 PopupBlocker::UnregisterOpenPopupSpam();
1025 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1026 // automatically.
1027 mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1030 AssertOriginAttributesMatchPrivateBrowsing();
1032 if (XRE_IsParentProcess()) {
1033 Canonical()->CanonicalDiscard();
1037 void BrowsingContext::AddDiscardListener(
1038 std::function<void(uint64_t)>&& aListener) {
1039 if (mIsDiscarded) {
1040 aListener(Id());
1041 return;
1043 mDiscardListeners.AppendElement(std::move(aListener));
1046 void BrowsingContext::PrepareForProcessChange() {
1047 MOZ_LOG(GetLog(), LogLevel::Debug,
1048 ("%s: Preparing 0x%08" PRIx64 " for a process change",
1049 XRE_IsParentProcess() ? "Parent" : "Child", Id()));
1051 MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame");
1052 MOZ_ASSERT(!mIsDiscarded, "We're already closed?");
1054 mIsInProcess = false;
1055 mUserGestureStart = TimeStamp();
1057 ClearCachedValuesOfLocations();
1059 // NOTE: For now, clear our nsDocShell reference, as we're primarily in a
1060 // different process now. This may need to change in the future with
1061 // Cross-Process BFCache.
1062 mDocShell = nullptr;
1063 if (mChildSessionHistory) {
1064 // This can be removed once session history is stored exclusively in the
1065 // parent process.
1066 mChildSessionHistory->SetIsInProcess(false);
1069 if (!mWindowProxy) {
1070 return;
1073 // We have to go through mWindowProxy rather than calling GetDOMWindow() on
1074 // mDocShell because the mDocshell reference gets cleared immediately after
1075 // the window is closed.
1076 nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy);
1077 MOZ_ASSERT(!mWindowProxy);
1080 bool BrowsingContext::IsTargetable() const {
1081 return !GetClosed() && AncestorsAreCurrent();
1084 void BrowsingContext::SetOpener(BrowsingContext* aOpener) {
1085 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->Group() == Group());
1086 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->mType == mType);
1088 MOZ_ALWAYS_SUCCEEDS(SetOpenerId(aOpener ? aOpener->Id() : 0));
1090 if (IsChrome() && IsTop() && aOpener) {
1091 // Inherit color scheme overrides from parent window. This is to inherit the
1092 // color scheme of dark themed PBM windows in dialogs opened by such
1093 // windows.
1094 auto openerOverride = aOpener->Top()->PrefersColorSchemeOverride();
1095 if (openerOverride != PrefersColorSchemeOverride()) {
1096 MOZ_ALWAYS_SUCCEEDS(SetPrefersColorSchemeOverride(openerOverride));
1101 bool BrowsingContext::HasOpener() const {
1102 return sBrowsingContexts->Contains(GetOpenerId());
1105 bool BrowsingContext::AncestorsAreCurrent() const {
1106 const BrowsingContext* bc = this;
1107 while (true) {
1108 if (bc->IsDiscarded()) {
1109 return false;
1112 if (WindowContext* wc = bc->GetParentWindowContext()) {
1113 if (!wc->IsCurrent() || wc->IsDiscarded()) {
1114 return false;
1117 bc = wc->GetBrowsingContext();
1118 } else {
1119 return true;
1124 bool BrowsingContext::IsInBFCache() const {
1125 if (mozilla::SessionHistoryInParent()) {
1126 return mIsInBFCache;
1128 return mParentWindow &&
1129 mParentWindow->TopWindowContext()->GetWindowStateSaved();
1132 Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const {
1133 if (WindowContext* current = mCurrentWindowContext) {
1134 return current->Children();
1136 return Span<RefPtr<BrowsingContext>>();
1139 void BrowsingContext::GetChildren(
1140 nsTArray<RefPtr<BrowsingContext>>& aChildren) {
1141 aChildren.AppendElements(Children());
1144 Span<RefPtr<BrowsingContext>> BrowsingContext::NonSyntheticChildren() const {
1145 if (WindowContext* current = mCurrentWindowContext) {
1146 return current->NonSyntheticChildren();
1148 return Span<RefPtr<BrowsingContext>>();
1151 void BrowsingContext::GetWindowContexts(
1152 nsTArray<RefPtr<WindowContext>>& aWindows) {
1153 aWindows.AppendElements(mWindowContexts);
1156 void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) {
1157 MOZ_ASSERT(!mWindowContexts.Contains(aWindow),
1158 "WindowContext already registered!");
1159 MOZ_ASSERT(aWindow->GetBrowsingContext() == this);
1161 mWindowContexts.AppendElement(aWindow);
1163 // If the newly registered WindowContext is for our current inner window ID,
1164 // re-run the `DidSet` handler to re-establish the relationship.
1165 if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) {
1166 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1167 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow);
1171 void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) {
1172 MOZ_ASSERT(mWindowContexts.Contains(aWindow),
1173 "WindowContext not registered!");
1174 mWindowContexts.RemoveElement(aWindow);
1176 // If our currently active window was unregistered, clear our reference to it.
1177 if (aWindow == mCurrentWindowContext) {
1178 // Re-read our `CurrentInnerWindowId` value and use it to set
1179 // `mCurrentWindowContext`. As `aWindow` is now unregistered and discarded,
1180 // we won't find it, and the value will be cleared back to `nullptr`.
1181 DidSet(FieldIndex<IDX_CurrentInnerWindowId>());
1182 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == nullptr);
1186 void BrowsingContext::PreOrderWalkVoid(
1187 const std::function<void(BrowsingContext*)>& aCallback) {
1188 aCallback(this);
1190 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1191 children.AppendElements(Children());
1193 for (auto& child : children) {
1194 child->PreOrderWalkVoid(aCallback);
1198 BrowsingContext::WalkFlag BrowsingContext::PreOrderWalkFlag(
1199 const std::function<WalkFlag(BrowsingContext*)>& aCallback) {
1200 switch (aCallback(this)) {
1201 case WalkFlag::Skip:
1202 return WalkFlag::Next;
1203 case WalkFlag::Stop:
1204 return WalkFlag::Stop;
1205 case WalkFlag::Next:
1206 default:
1207 break;
1210 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1211 children.AppendElements(Children());
1213 for (auto& child : children) {
1214 switch (child->PreOrderWalkFlag(aCallback)) {
1215 case WalkFlag::Stop:
1216 return WalkFlag::Stop;
1217 default:
1218 break;
1222 return WalkFlag::Next;
1225 void BrowsingContext::PostOrderWalk(
1226 const std::function<void(BrowsingContext*)>& aCallback) {
1227 AutoTArray<RefPtr<BrowsingContext>, 8> children;
1228 children.AppendElements(Children());
1230 for (auto& child : children) {
1231 child->PostOrderWalk(aCallback);
1234 aCallback(this);
1237 void BrowsingContext::GetAllBrowsingContextsInSubtree(
1238 nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) {
1239 PreOrderWalk([&](BrowsingContext* aContext) {
1240 aBrowsingContexts.AppendElement(aContext);
1244 BrowsingContext* BrowsingContext::FindChildWithName(
1245 const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
1246 if (aName.IsEmpty()) {
1247 // You can't find a browsing context with the empty name.
1248 return nullptr;
1251 for (BrowsingContext* child : NonSyntheticChildren()) {
1252 if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) &&
1253 child->IsTargetable()) {
1254 return child;
1258 return nullptr;
1261 BrowsingContext* BrowsingContext::FindWithSpecialName(
1262 const nsAString& aName, WindowGlobalChild& aRequestingWindow) {
1263 // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the
1264 // browsing context pointed to by a special name is active. Should
1265 // it be? See Bug 1527913.
1266 if (aName.LowerCaseEqualsLiteral("_self")) {
1267 return this;
1270 if (aName.LowerCaseEqualsLiteral("_parent")) {
1271 if (BrowsingContext* parent = GetParent()) {
1272 return aRequestingWindow.CanNavigate(parent) ? parent : nullptr;
1274 return this;
1277 if (aName.LowerCaseEqualsLiteral("_top")) {
1278 BrowsingContext* top = Top();
1280 return aRequestingWindow.CanNavigate(top) ? top : nullptr;
1283 return nullptr;
1286 BrowsingContext* BrowsingContext::FindWithNameInSubtree(
1287 const nsAString& aName, WindowGlobalChild* aRequestingWindow) {
1288 MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty());
1290 if (NameEquals(aName) &&
1291 (!aRequestingWindow || aRequestingWindow->CanNavigate(this)) &&
1292 IsTargetable()) {
1293 return this;
1296 for (BrowsingContext* child : NonSyntheticChildren()) {
1297 if (BrowsingContext* found =
1298 child->FindWithNameInSubtree(aName, aRequestingWindow)) {
1299 return found;
1303 return nullptr;
1306 bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) {
1307 // If no target then not sandboxed.
1308 if (!aTarget) {
1309 return false;
1312 // We cannot be sandboxed from ourselves.
1313 if (aTarget == this) {
1314 return false;
1317 // Default the sandbox flags to our flags, so that if we can't retrieve the
1318 // active document, we will still enforce our own.
1319 uint32_t sandboxFlags = GetSandboxFlags();
1320 if (mDocShell) {
1321 if (RefPtr<Document> doc = mDocShell->GetExtantDocument()) {
1322 sandboxFlags = doc->GetSandboxFlags();
1326 // If no flags, we are not sandboxed at all.
1327 if (!sandboxFlags) {
1328 return false;
1331 // If aTarget has an ancestor, it is not top level.
1332 if (RefPtr<BrowsingContext> ancestorOfTarget = aTarget->GetParent()) {
1333 do {
1334 // We are not sandboxed if we are an ancestor of target.
1335 if (ancestorOfTarget == this) {
1336 return false;
1338 ancestorOfTarget = ancestorOfTarget->GetParent();
1339 } while (ancestorOfTarget);
1341 // Otherwise, we are sandboxed from aTarget.
1342 return true;
1345 // aTarget is top level, are we the "one permitted sandboxed
1346 // navigator", i.e. did we open aTarget?
1347 if (aTarget->GetOnePermittedSandboxedNavigatorId() == Id()) {
1348 return false;
1351 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
1352 // from our top.
1353 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) && aTarget == Top()) {
1354 return false;
1357 // If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not
1358 // sandboxed from our top if we have user interaction. We assume there is a
1359 // valid transient user gesture interaction if this check happens in the
1360 // target process given that we have checked in the triggering process
1361 // already.
1362 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) &&
1363 mCurrentWindowContext &&
1364 (!mCurrentWindowContext->IsInProcess() ||
1365 mCurrentWindowContext->HasValidTransientUserGestureActivation()) &&
1366 aTarget == Top()) {
1367 return false;
1370 // Otherwise, we are sandboxed from aTarget.
1371 return true;
1374 RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() {
1375 RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager;
1376 if (!manager) {
1377 manager = new SessionStorageManager(this);
1379 return manager;
1382 bool BrowsingContext::CrossOriginIsolated() {
1383 MOZ_ASSERT(NS_IsMainThread());
1385 return StaticPrefs::
1386 dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup() &&
1387 Top()->GetOpenerPolicy() ==
1388 nsILoadInfo::
1389 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
1390 XRE_IsContentProcess() &&
1391 StringBeginsWith(ContentChild::GetSingleton()->GetRemoteType(),
1392 WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
1395 void BrowsingContext::SetTriggeringAndInheritPrincipals(
1396 nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
1397 uint64_t aLoadIdentifier) {
1398 mTriggeringPrincipal = Some(
1399 PrincipalWithLoadIdentifierTuple(aTriggeringPrincipal, aLoadIdentifier));
1400 if (aPrincipalToInherit) {
1401 mPrincipalToInherit = Some(
1402 PrincipalWithLoadIdentifierTuple(aPrincipalToInherit, aLoadIdentifier));
1406 std::tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>>
1407 BrowsingContext::GetTriggeringAndInheritPrincipalsForCurrentLoad() {
1408 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
1409 GetSavedPrincipal(mTriggeringPrincipal);
1410 nsCOMPtr<nsIPrincipal> principalToInherit =
1411 GetSavedPrincipal(mPrincipalToInherit);
1412 return std::make_tuple(triggeringPrincipal, principalToInherit);
1415 nsIPrincipal* BrowsingContext::GetSavedPrincipal(
1416 Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple) {
1417 if (aPrincipalTuple) {
1418 nsCOMPtr<nsIPrincipal> principal;
1419 uint64_t loadIdentifier;
1420 std::tie(principal, loadIdentifier) = *aPrincipalTuple;
1421 // We want to return a principal only if the load identifier for it
1422 // matches the current one for this BC.
1423 if (auto current = GetCurrentLoadIdentifier();
1424 current && *current == loadIdentifier) {
1425 return principal;
1428 return nullptr;
1431 BrowsingContext::~BrowsingContext() {
1432 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow ||
1433 !mParentWindow->mChildren.Contains(this));
1434 MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this));
1436 mDeprioritizedLoadRunner.clear();
1438 if (sBrowsingContexts) {
1439 sBrowsingContexts->Remove(Id());
1441 UnregisterBrowserId(this);
1443 ClearCachedValuesOfLocations();
1444 mLocations.clear();
1447 /* static */
1448 void BrowsingContext::DiscardFromContentParent(ContentParent* aCP) {
1449 MOZ_ASSERT(XRE_IsParentProcess());
1451 if (sBrowsingContexts) {
1452 AutoTArray<RefPtr<BrowsingContext>, 8> toDiscard;
1453 for (const auto& data : sBrowsingContexts->Values()) {
1454 auto* bc = data->Canonical();
1455 if (!bc->IsDiscarded() && bc->IsEmbeddedInProcess(aCP->ChildID())) {
1456 toDiscard.AppendElement(bc);
1460 for (BrowsingContext* bc : toDiscard) {
1461 bc->Detach(/* aFromIPC */ true);
1466 nsISupports* BrowsingContext::GetParentObject() const {
1467 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1470 JSObject* BrowsingContext::WrapObject(JSContext* aCx,
1471 JS::Handle<JSObject*> aGivenProto) {
1472 return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto);
1475 bool BrowsingContext::WriteStructuredClone(JSContext* aCx,
1476 JSStructuredCloneWriter* aWriter,
1477 StructuredCloneHolder* aHolder) {
1478 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
1479 return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) &&
1480 JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32)));
1483 /* static */
1484 JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx,
1485 JSStructuredCloneReader* aReader,
1486 StructuredCloneHolder* aHolder) {
1487 uint32_t idLow = 0;
1488 uint32_t idHigh = 0;
1489 if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) {
1490 return nullptr;
1492 uint64_t id = uint64_t(idHigh) << 32 | idLow;
1494 // Note: Do this check after reading our ID data. Returning null will abort
1495 // the decode operation anyway, but we should at least be as safe as possible.
1496 if (NS_WARN_IF(!NS_IsMainThread())) {
1497 MOZ_DIAGNOSTIC_ASSERT(false,
1498 "We shouldn't be trying to decode a BrowsingContext "
1499 "on a background thread.");
1500 return nullptr;
1503 JS::Rooted<JS::Value> val(aCx, JS::NullValue());
1504 // We'll get rooting hazard errors from the RefPtr destructor if it isn't
1505 // destroyed before we try to return a raw JSObject*, so create it in its own
1506 // scope.
1507 if (RefPtr<BrowsingContext> context = Get(id)) {
1508 if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) {
1509 return nullptr;
1512 return val.toObjectOrNull();
1515 bool BrowsingContext::CanSetOriginAttributes() {
1516 // A discarded BrowsingContext has already been destroyed, and cannot modify
1517 // its OriginAttributes.
1518 if (NS_WARN_IF(IsDiscarded())) {
1519 return false;
1522 // Before attaching is the safest time to set OriginAttributes, and the only
1523 // allowed time for content BrowsingContexts.
1524 if (!EverAttached()) {
1525 return true;
1528 // Attached content BrowsingContexts may have been synced to other processes.
1529 if (NS_WARN_IF(IsContent())) {
1530 MOZ_CRASH();
1531 return false;
1533 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
1535 // Cannot set OriginAttributes after we've created our child BrowsingContext.
1536 if (NS_WARN_IF(!Children().IsEmpty())) {
1537 return false;
1540 // Only allow setting OriginAttributes if we have no associated document, or
1541 // the document is still `about:blank`.
1542 // TODO: Bug 1273058 - should have no document when setting origin attributes.
1543 if (WindowGlobalParent* window = Canonical()->GetCurrentWindowGlobal()) {
1544 if (nsIURI* uri = window->GetDocumentURI()) {
1545 MOZ_ASSERT(NS_IsAboutBlank(uri));
1546 return NS_IsAboutBlank(uri);
1549 return true;
1552 Nullable<WindowProxyHolder> BrowsingContext::GetAssociatedWindow() {
1553 // nsILoadContext usually only returns same-process windows,
1554 // so we intentionally return nullptr if this BC is out of
1555 // process.
1556 if (IsInProcess()) {
1557 return WindowProxyHolder(this);
1559 return nullptr;
1562 Nullable<WindowProxyHolder> BrowsingContext::GetTopWindow() {
1563 return Top()->GetAssociatedWindow();
1566 Element* BrowsingContext::GetTopFrameElement() {
1567 return Top()->GetEmbedderElement();
1570 void BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing,
1571 ErrorResult& aError) {
1572 nsresult rv = SetUsePrivateBrowsing(aUsePrivateBrowsing);
1573 if (NS_FAILED(rv)) {
1574 aError.Throw(rv);
1578 void BrowsingContext::SetUseTrackingProtectionWebIDL(
1579 bool aUseTrackingProtection, ErrorResult& aRv) {
1580 SetForceEnableTrackingProtection(aUseTrackingProtection, aRv);
1583 void BrowsingContext::GetOriginAttributes(JSContext* aCx,
1584 JS::MutableHandle<JS::Value> aVal,
1585 ErrorResult& aError) {
1586 AssertOriginAttributesMatchPrivateBrowsing();
1588 if (!ToJSValue(aCx, mOriginAttributes, aVal)) {
1589 aError.NoteJSContextException(aCx);
1593 NS_IMETHODIMP BrowsingContext::GetAssociatedWindow(
1594 mozIDOMWindowProxy** aAssociatedWindow) {
1595 nsCOMPtr<mozIDOMWindowProxy> win = GetDOMWindow();
1596 win.forget(aAssociatedWindow);
1597 return NS_OK;
1600 NS_IMETHODIMP BrowsingContext::GetTopWindow(mozIDOMWindowProxy** aTopWindow) {
1601 return Top()->GetAssociatedWindow(aTopWindow);
1604 NS_IMETHODIMP BrowsingContext::GetTopFrameElement(Element** aTopFrameElement) {
1605 RefPtr<Element> topFrameElement = GetTopFrameElement();
1606 topFrameElement.forget(aTopFrameElement);
1607 return NS_OK;
1610 NS_IMETHODIMP BrowsingContext::GetIsContent(bool* aIsContent) {
1611 *aIsContent = IsContent();
1612 return NS_OK;
1615 NS_IMETHODIMP BrowsingContext::GetUsePrivateBrowsing(
1616 bool* aUsePrivateBrowsing) {
1617 *aUsePrivateBrowsing = mPrivateBrowsingId > 0;
1618 return NS_OK;
1621 NS_IMETHODIMP BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1622 if (!CanSetOriginAttributes()) {
1623 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0);
1624 if (changed) {
1625 NS_WARNING("SetUsePrivateBrowsing when !CanSetOriginAttributes()");
1627 return changed ? NS_ERROR_FAILURE : NS_OK;
1630 return SetPrivateBrowsing(aUsePrivateBrowsing);
1633 NS_IMETHODIMP BrowsingContext::SetPrivateBrowsing(bool aPrivateBrowsing) {
1634 if (!CanSetOriginAttributes()) {
1635 NS_WARNING("Attempt to set PrivateBrowsing when !CanSetOriginAttributes");
1636 return NS_ERROR_FAILURE;
1639 bool changed = aPrivateBrowsing != (mPrivateBrowsingId > 0);
1640 if (changed) {
1641 mPrivateBrowsingId = aPrivateBrowsing ? 1 : 0;
1642 if (IsContent()) {
1643 mOriginAttributes.SyncAttributesWithPrivateBrowsing(aPrivateBrowsing);
1646 if (XRE_IsParentProcess()) {
1647 Canonical()->AdjustPrivateBrowsingCount(aPrivateBrowsing);
1650 AssertOriginAttributesMatchPrivateBrowsing();
1652 if (changed && mDocShell) {
1653 nsDocShell::Cast(mDocShell)->NotifyPrivateBrowsingChanged();
1655 return NS_OK;
1658 NS_IMETHODIMP BrowsingContext::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1659 *aUseRemoteTabs = mUseRemoteTabs;
1660 return NS_OK;
1663 NS_IMETHODIMP BrowsingContext::SetRemoteTabs(bool aUseRemoteTabs) {
1664 if (!CanSetOriginAttributes()) {
1665 NS_WARNING("Attempt to set RemoteTabs when !CanSetOriginAttributes");
1666 return NS_ERROR_FAILURE;
1669 if (aUseRemoteTabs && !gIPCEnabledAnnotation) {
1670 gIPCEnabledAnnotation = true;
1673 // Don't allow non-remote tabs with remote subframes.
1674 if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) {
1675 return NS_ERROR_UNEXPECTED;
1678 mUseRemoteTabs = aUseRemoteTabs;
1679 return NS_OK;
1682 NS_IMETHODIMP BrowsingContext::GetUseRemoteSubframes(
1683 bool* aUseRemoteSubframes) {
1684 *aUseRemoteSubframes = mUseRemoteSubframes;
1685 return NS_OK;
1688 NS_IMETHODIMP BrowsingContext::SetRemoteSubframes(bool aUseRemoteSubframes) {
1689 if (!CanSetOriginAttributes()) {
1690 NS_WARNING("Attempt to set RemoteSubframes when !CanSetOriginAttributes");
1691 return NS_ERROR_FAILURE;
1694 if (aUseRemoteSubframes && !gFissionEnabledAnnotation) {
1695 gFissionEnabledAnnotation = true;
1698 // Don't allow non-remote tabs with remote subframes.
1699 if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) {
1700 return NS_ERROR_UNEXPECTED;
1703 mUseRemoteSubframes = aUseRemoteSubframes;
1704 return NS_OK;
1707 NS_IMETHODIMP BrowsingContext::GetUseTrackingProtection(
1708 bool* aUseTrackingProtection) {
1709 *aUseTrackingProtection = false;
1711 if (GetForceEnableTrackingProtection() ||
1712 StaticPrefs::privacy_trackingprotection_enabled() ||
1713 (UsePrivateBrowsing() &&
1714 StaticPrefs::privacy_trackingprotection_pbmode_enabled())) {
1715 *aUseTrackingProtection = true;
1716 return NS_OK;
1719 if (GetParent()) {
1720 return GetParent()->GetUseTrackingProtection(aUseTrackingProtection);
1723 return NS_OK;
1726 NS_IMETHODIMP BrowsingContext::SetUseTrackingProtection(
1727 bool aUseTrackingProtection) {
1728 return SetForceEnableTrackingProtection(aUseTrackingProtection);
1731 NS_IMETHODIMP BrowsingContext::GetScriptableOriginAttributes(
1732 JSContext* aCx, JS::MutableHandle<JS::Value> aVal) {
1733 AssertOriginAttributesMatchPrivateBrowsing();
1735 bool ok = ToJSValue(aCx, mOriginAttributes, aVal);
1736 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
1737 return NS_OK;
1740 NS_IMETHODIMP_(void)
1741 BrowsingContext::GetOriginAttributes(OriginAttributes& aAttrs) {
1742 aAttrs = mOriginAttributes;
1743 AssertOriginAttributesMatchPrivateBrowsing();
1746 nsresult BrowsingContext::SetOriginAttributes(const OriginAttributes& aAttrs) {
1747 if (!CanSetOriginAttributes()) {
1748 NS_WARNING("Attempt to set OriginAttributes when !CanSetOriginAttributes");
1749 return NS_ERROR_FAILURE;
1752 AssertOriginAttributesMatchPrivateBrowsing();
1753 mOriginAttributes = aAttrs;
1755 bool isPrivate = mOriginAttributes.mPrivateBrowsingId !=
1756 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1757 // Chrome Browsing Context can not contain OriginAttributes.mPrivateBrowsingId
1758 if (IsChrome() && isPrivate) {
1759 mOriginAttributes.mPrivateBrowsingId =
1760 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID;
1762 SetPrivateBrowsing(isPrivate);
1763 AssertOriginAttributesMatchPrivateBrowsing();
1765 return NS_OK;
1768 void BrowsingContext::AssertCoherentLoadContext() {
1769 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1770 // LoadContext should generally match our opener or parent.
1771 if (IsContent()) {
1772 if (RefPtr<BrowsingContext> opener = GetOpener()) {
1773 MOZ_DIAGNOSTIC_ASSERT(opener->mType == mType);
1774 MOZ_DIAGNOSTIC_ASSERT(opener->mGroup == mGroup);
1775 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteTabs == mUseRemoteTabs);
1776 MOZ_DIAGNOSTIC_ASSERT(opener->mUseRemoteSubframes == mUseRemoteSubframes);
1777 MOZ_DIAGNOSTIC_ASSERT(opener->mPrivateBrowsingId == mPrivateBrowsingId);
1778 MOZ_DIAGNOSTIC_ASSERT(
1779 opener->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1782 if (RefPtr<BrowsingContext> parent = GetParent()) {
1783 MOZ_DIAGNOSTIC_ASSERT(parent->mType == mType);
1784 MOZ_DIAGNOSTIC_ASSERT(parent->mGroup == mGroup);
1785 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteTabs == mUseRemoteTabs);
1786 MOZ_DIAGNOSTIC_ASSERT(parent->mUseRemoteSubframes == mUseRemoteSubframes);
1787 MOZ_DIAGNOSTIC_ASSERT(parent->mPrivateBrowsingId == mPrivateBrowsingId);
1788 MOZ_DIAGNOSTIC_ASSERT(
1789 parent->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes));
1792 // UseRemoteSubframes and UseRemoteTabs must match.
1793 MOZ_DIAGNOSTIC_ASSERT(
1794 !mUseRemoteSubframes || mUseRemoteTabs,
1795 "Cannot set useRemoteSubframes without also setting useRemoteTabs");
1797 // Double-check OriginAttributes/Private Browsing
1798 AssertOriginAttributesMatchPrivateBrowsing();
1799 #endif
1802 void BrowsingContext::AssertOriginAttributesMatchPrivateBrowsing() {
1803 // Chrome browsing contexts must not have a private browsing OriginAttribute
1804 // Content browsing contexts must maintain the equality:
1805 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId
1806 if (IsChrome()) {
1807 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0);
1808 } else {
1809 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId ==
1810 mPrivateBrowsingId);
1814 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext)
1815 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1816 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
1817 NS_INTERFACE_MAP_ENTRY(nsISupports)
1818 NS_INTERFACE_MAP_END
1820 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext)
1822 NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext)
1823 NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext)
1825 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext)
1826 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
1827 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1829 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext)
1830 if (sBrowsingContexts) {
1831 sBrowsingContexts->Remove(tmp->Id());
1833 UnregisterBrowserId(tmp);
1835 if (tmp->GetIsPopupSpam()) {
1836 PopupBlocker::UnregisterOpenPopupSpam();
1837 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes
1838 // automatically.
1839 tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false);
1842 NS_IMPL_CYCLE_COLLECTION_UNLINK(
1843 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1844 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1845 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
1846 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1848 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext)
1849 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
1850 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts,
1851 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory)
1852 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1854 static bool IsCertainlyAliveForCC(BrowsingContext* aContext) {
1855 return aContext->HasKnownLiveWrapper() ||
1856 (AppShutdown::GetCurrentShutdownPhase() ==
1857 ShutdownPhase::NotInShutdown &&
1858 aContext->EverAttached() && !aContext->IsDiscarded());
1861 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(BrowsingContext)
1862 if (IsCertainlyAliveForCC(tmp)) {
1863 if (tmp->PreservingWrapper()) {
1864 tmp->MarkWrapperLive();
1866 return true;
1868 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1870 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(BrowsingContext)
1871 return IsCertainlyAliveForCC(tmp) && tmp->HasNothingToTrace(tmp);
1872 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1874 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext)
1875 return IsCertainlyAliveForCC(tmp);
1876 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1878 class RemoteLocationProxy
1879 : public RemoteObjectProxy<BrowsingContext::LocationProxy,
1880 Location_Binding::sCrossOriginProperties> {
1881 public:
1882 typedef RemoteObjectProxy Base;
1884 constexpr RemoteLocationProxy()
1885 : RemoteObjectProxy(prototypes::id::Location) {}
1887 void NoteChildren(JSObject* aProxy,
1888 nsCycleCollectionTraversalCallback& aCb) const override {
1889 auto location =
1890 static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy));
1891 CycleCollectionNoteChild(aCb, location->GetBrowsingContext(),
1892 "JS::GetPrivate(obj)->GetBrowsingContext()");
1896 static const RemoteLocationProxy sSingleton;
1898 // Give RemoteLocationProxy 2 reserved slots, like the other wrappers,
1899 // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring
1900 // malloc.
1901 template <>
1902 const JSClass RemoteLocationProxy::Base::sClass =
1903 PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2));
1905 void BrowsingContext::Location(JSContext* aCx,
1906 JS::MutableHandle<JSObject*> aLocation,
1907 ErrorResult& aError) {
1908 aError.MightThrowJSException();
1909 sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr,
1910 aLocation);
1911 if (!aLocation) {
1912 aError.StealExceptionFromJSContext(aCx);
1916 bool BrowsingContext::RemoveRootFromBFCacheSync() {
1917 if (WindowContext* wc = GetParentWindowContext()) {
1918 if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) {
1919 return doc->RemoveFromBFCacheSync();
1922 return false;
1925 nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) {
1926 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1927 if (sourceBC.IsNull()) {
1928 return NS_OK;
1931 // We might be called after the source BC has been discarded, but before we've
1932 // destroyed our in-process instance of the BrowsingContext object in some
1933 // situations (e.g. after creating a new pop-up with window.open while the
1934 // window is being closed). In these situations we want to still perform the
1935 // sandboxing check against our in-process copy. If we've forgotten about the
1936 // context already, assume it is sanboxed. (bug 1643450)
1937 BrowsingContext* bc = sourceBC.GetMaybeDiscarded();
1938 if (!bc || bc->IsSandboxedFrom(this)) {
1939 return NS_ERROR_DOM_SECURITY_ERR;
1941 return NS_OK;
1944 nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
1945 bool aSetNavigating) {
1946 // Per spec, most load attempts are silently ignored when a BrowsingContext is
1947 // null (which in our code corresponds to discarded), so we simply fail
1948 // silently in those cases. Regardless, we cannot trigger loads in/from
1949 // discarded BrowsingContexts via IPC, so we need to abort in any case.
1950 if (IsDiscarded()) {
1951 return NS_OK;
1954 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
1955 "Targeting occurs in InternalLoad");
1956 aLoadState->AssertProcessCouldTriggerLoadIfSystem();
1958 if (mDocShell) {
1959 nsCOMPtr<nsIDocShell> docShell = mDocShell;
1960 return docShell->LoadURI(aLoadState, aSetNavigating);
1963 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
1964 // document-specific sandbox flags are only available in the process
1965 // triggering the load, and we don't want the target process to have to trust
1966 // the triggering process to do the appropriate checks for the
1967 // BrowsingContext's sandbox flags.
1968 MOZ_TRY(CheckSandboxFlags(aLoadState));
1969 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
1970 aLoadState->PrincipalToInherit(),
1971 aLoadState->GetLoadIdentifier());
1973 const auto& sourceBC = aLoadState->SourceBrowsingContext();
1975 if (net::SchemeIsJavascript(aLoadState->URI())) {
1976 if (!XRE_IsParentProcess()) {
1977 // Web content should only be able to load javascript: URIs into documents
1978 // whose principals the caller principal subsumes, which by definition
1979 // excludes any document in a cross-process BrowsingContext.
1980 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
1982 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
1983 "Should never see a cross-process javascript: load "
1984 "triggered from content");
1987 MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group());
1988 if (sourceBC && sourceBC->IsInProcess()) {
1989 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
1990 if (WindowGlobalChild* wgc =
1991 win->GetCurrentInnerWindow()->GetWindowGlobalChild()) {
1992 if (!wgc->CanNavigate(this)) {
1993 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
1995 wgc->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating);
1997 } else if (XRE_IsParentProcess()) {
1998 if (Canonical()->LoadInParent(aLoadState, aSetNavigating)) {
1999 return NS_OK;
2002 if (ContentParent* cp = Canonical()->GetContentParent()) {
2003 // Attempt to initiate this load immediately in the parent, if it succeeds
2004 // it'll return a unique identifier so that we can find it later.
2005 uint64_t loadIdentifier = 0;
2006 if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) {
2007 MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome());
2008 loadIdentifier = GetCurrentLoadIdentifier().value();
2009 aLoadState->SetChannelInitialized(true);
2012 cp->TransmitBlobDataIfBlobURL(aLoadState->URI());
2014 // Setup a confirmation callback once the content process receives this
2015 // load. Normally we'd expect a PDocumentChannel actor to have been
2016 // created to claim the load identifier by that time. If not, then it
2017 // won't be coming, so make sure we clean up and deregister.
2018 cp->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating)
2019 ->Then(GetMainThreadSerialEventTarget(), __func__,
2020 [loadIdentifier](
2021 const PContentParent::LoadURIPromise::ResolveOrRejectValue&
2022 aValue) {
2023 if (loadIdentifier) {
2024 net::DocumentLoadListener::CleanupParentLoadAttempt(
2025 loadIdentifier);
2029 } else {
2030 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2031 if (!sourceBC) {
2032 return NS_ERROR_UNEXPECTED;
2034 // If we're in a content process and the source BC is no longer in-process,
2035 // just fail silently.
2037 return NS_OK;
2040 nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) {
2041 if (IsDiscarded()) {
2042 return NS_OK;
2044 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(),
2045 aLoadState->PrincipalToInherit(),
2046 aLoadState->GetLoadIdentifier());
2048 MOZ_DIAGNOSTIC_ASSERT(aLoadState->Target().IsEmpty(),
2049 "should already have retargeted");
2050 MOZ_DIAGNOSTIC_ASSERT(!aLoadState->TargetBrowsingContext().IsNull(),
2051 "should have target bc set");
2052 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext() == this,
2053 "must be targeting this BrowsingContext");
2054 aLoadState->AssertProcessCouldTriggerLoadIfSystem();
2056 if (mDocShell) {
2057 RefPtr<nsDocShell> docShell = nsDocShell::Cast(mDocShell);
2058 return docShell->InternalLoad(aLoadState);
2061 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since
2062 // document-specific sandbox flags are only available in the process
2063 // triggering the load, and we don't want the target process to have to trust
2064 // the triggering process to do the appropriate checks for the
2065 // BrowsingContext's sandbox flags.
2066 MOZ_TRY(CheckSandboxFlags(aLoadState));
2068 const auto& sourceBC = aLoadState->SourceBrowsingContext();
2070 if (net::SchemeIsJavascript(aLoadState->URI())) {
2071 if (!XRE_IsParentProcess()) {
2072 // Web content should only be able to load javascript: URIs into documents
2073 // whose principals the caller principal subsumes, which by definition
2074 // excludes any document in a cross-process BrowsingContext.
2075 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
2077 MOZ_DIAGNOSTIC_ASSERT(!sourceBC,
2078 "Should never see a cross-process javascript: load "
2079 "triggered from content");
2082 if (XRE_IsParentProcess()) {
2083 ContentParent* cp = Canonical()->GetContentParent();
2084 if (!cp || !cp->CanSend()) {
2085 return NS_ERROR_FAILURE;
2088 MOZ_ALWAYS_SUCCEEDS(
2089 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2090 Unused << cp->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
2091 } else {
2092 MOZ_DIAGNOSTIC_ASSERT(sourceBC);
2093 MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group());
2095 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow());
2096 WindowGlobalChild* wgc =
2097 win->GetCurrentInnerWindow()->GetWindowGlobalChild();
2098 if (!wgc || !wgc->CanSend()) {
2099 return NS_ERROR_FAILURE;
2101 if (!wgc->CanNavigate(this)) {
2102 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2105 MOZ_ALWAYS_SUCCEEDS(
2106 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier())));
2107 wgc->SendInternalLoad(mozilla::WrapNotNull(aLoadState));
2110 return NS_OK;
2113 void BrowsingContext::DisplayLoadError(const nsAString& aURI) {
2114 MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError"));
2115 MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
2116 MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess());
2118 if (mDocShell) {
2119 bool didDisplayLoadError = false;
2120 nsCOMPtr<nsIDocShell> docShell = mDocShell;
2121 docShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr,
2122 PromiseFlatString(aURI).get(), nullptr,
2123 &didDisplayLoadError);
2124 } else {
2125 if (ContentParent* cp = Canonical()->GetContentParent()) {
2126 Unused << cp->SendDisplayLoadError(this, PromiseFlatString(aURI));
2131 WindowProxyHolder BrowsingContext::Window() {
2132 return WindowProxyHolder(Self());
2135 WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) {
2136 return Window();
2139 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) {
2140 if (mIsDiscarded) {
2141 return;
2144 if (IsSubframe()) {
2145 // .close() on frames is a no-op.
2146 return;
2149 if (GetDOMWindow()) {
2150 nsGlobalWindowOuter::Cast(GetDOMWindow())
2151 ->CloseOuter(aCallerType == CallerType::System);
2152 return;
2155 // This is a bit of a hack for webcompat. Content needs to see an updated
2156 // |window.closed| value as early as possible, so we set this before we
2157 // actually send the DOMWindowClose event, which happens in the process where
2158 // the document for this browsing context is loaded.
2159 MOZ_ALWAYS_SUCCEEDS(SetClosed(true));
2161 if (ContentChild* cc = ContentChild::GetSingleton()) {
2162 cc->SendWindowClose(this, aCallerType == CallerType::System);
2163 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2164 Unused << cp->SendWindowClose(this, aCallerType == CallerType::System);
2168 template <typename FuncT>
2169 inline bool ApplyToDocumentsForPopup(Document* doc, FuncT func) {
2170 // HACK: Some pages using bogus library + UA sniffing call window.open()
2171 // from a blank iframe, only on Firefox, see bug 1685056.
2173 // This is a hack-around to preserve behavior in that particular and
2174 // specific case, by consuming activation on the parent document, so we
2175 // don't care about the InProcessParent bits not being fission-safe or what
2176 // not.
2177 if (func(doc)) {
2178 return true;
2180 if (!doc->IsInitialDocument()) {
2181 return false;
2183 Document* parentDoc = doc->GetInProcessParentDocument();
2184 if (!parentDoc || !parentDoc->NodePrincipal()->Equals(doc->NodePrincipal())) {
2185 return false;
2187 return func(parentDoc);
2190 PopupBlocker::PopupControlState BrowsingContext::RevisePopupAbuseLevel(
2191 PopupBlocker::PopupControlState aControl) {
2192 if (!IsContent()) {
2193 return PopupBlocker::openAllowed;
2196 RefPtr<Document> doc = GetExtantDocument();
2197 PopupBlocker::PopupControlState abuse = aControl;
2198 switch (abuse) {
2199 case PopupBlocker::openControlled:
2200 case PopupBlocker::openBlocked:
2201 case PopupBlocker::openOverridden:
2202 if (IsPopupAllowed()) {
2203 abuse = PopupBlocker::PopupControlState(abuse - 1);
2205 break;
2206 case PopupBlocker::openAbused:
2207 if (IsPopupAllowed() ||
2208 (doc && doc->HasValidTransientUserGestureActivation())) {
2209 // Skip PopupBlocker::openBlocked
2210 abuse = PopupBlocker::openControlled;
2212 break;
2213 case PopupBlocker::openAllowed:
2214 break;
2215 default:
2216 NS_WARNING("Strange PopupControlState!");
2219 // limit the number of simultaneously open popups
2220 if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked ||
2221 abuse == PopupBlocker::openControlled) {
2222 int32_t popupMax = StaticPrefs::dom_popup_maximum();
2223 if (popupMax >= 0 &&
2224 PopupBlocker::GetOpenPopupSpamCount() >= (uint32_t)popupMax) {
2225 abuse = PopupBlocker::openOverridden;
2229 // If we're currently in-process, attempt to consume transient user gesture
2230 // activations.
2231 if (doc) {
2232 auto ConsumeTransientUserActivationForMultiplePopupBlocking =
2233 [&]() -> bool {
2234 return ApplyToDocumentsForPopup(doc, [](Document* doc) {
2235 return doc->ConsumeTransientUserGestureActivation();
2239 // If this popup is allowed, let's block any other for this event, forcing
2240 // PopupBlocker::openBlocked state.
2241 if ((abuse == PopupBlocker::openAllowed ||
2242 abuse == PopupBlocker::openControlled) &&
2243 StaticPrefs::dom_block_multiple_popups() && !IsPopupAllowed() &&
2244 !ConsumeTransientUserActivationForMultiplePopupBlocking()) {
2245 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
2246 doc, nsContentUtils::eDOM_PROPERTIES,
2247 "MultiplePopupsBlockedNoUserActivation");
2248 abuse = PopupBlocker::openBlocked;
2252 return abuse;
2255 void BrowsingContext::GetUserActivationModifiersForPopup(
2256 UserActivation::Modifiers* aModifiers) {
2257 RefPtr<Document> doc = GetExtantDocument();
2258 if (doc) {
2259 // Unlike RevisePopupAbuseLevel, modifiers can always be used regardless
2260 // of PopupControlState.
2261 (void)ApplyToDocumentsForPopup(doc, [&](Document* doc) {
2262 return doc->GetTransientUserGestureActivationModifiers(aModifiers);
2267 void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() {
2268 Unused << SetHistoryEntryCount(GetHistoryEntryCount() + 1);
2271 std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) {
2272 nsFocusManager* fm = nsFocusManager::GetFocusManager();
2273 if (!fm) {
2274 return {false, false};
2277 nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal());
2278 BrowsingContext* callerBC = caller ? caller->GetBrowsingContext() : nullptr;
2279 RefPtr<BrowsingContext> openerBC = GetOpener();
2280 MOZ_DIAGNOSTIC_ASSERT(!openerBC || openerBC->Group() == Group());
2282 // Enforce dom.disable_window_flip (for non-chrome), but still allow the
2283 // window which opened us to raise us at times when popups are allowed
2284 // (bugs 355482 and 369306).
2285 bool canFocus = aCallerType == CallerType::System ||
2286 !Preferences::GetBool("dom.disable_window_flip", true);
2287 if (!canFocus && openerBC == callerBC) {
2288 canFocus =
2289 (callerBC ? callerBC : this)
2290 ->RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) <
2291 PopupBlocker::openBlocked;
2294 bool isActive = false;
2295 if (XRE_IsParentProcess()) {
2296 CanonicalBrowsingContext* chromeTop = Canonical()->TopCrossChromeBoundary();
2297 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = fm->GetActiveWindow();
2298 isActive = activeWindow == chromeTop->GetDOMWindow();
2299 } else {
2300 isActive = fm->GetActiveBrowsingContext() == Top();
2303 return {canFocus, isActive};
2306 void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) {
2307 // These checks need to happen before the RequestFrameFocus call, which
2308 // is why they are done in an untrusted process. If we wanted to enforce
2309 // these in the parent, we'd need to do the checks there _also_.
2310 // These should be kept in sync with nsGlobalWindowOuter::FocusOuter.
2312 auto [canFocus, isActive] = CanFocusCheck(aCallerType);
2314 if (!(canFocus || isActive)) {
2315 return;
2318 // Permission check passed
2320 if (mEmbedderElement) {
2321 // Make the activeElement in this process update synchronously.
2322 nsContentUtils::RequestFrameFocus(*mEmbedderElement, true, aCallerType);
2324 uint64_t actionId = nsFocusManager::GenerateFocusActionId();
2325 if (ContentChild* cc = ContentChild::GetSingleton()) {
2326 cc->SendWindowFocus(this, aCallerType, actionId);
2327 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2328 Unused << cp->SendWindowFocus(this, aCallerType, actionId);
2332 bool BrowsingContext::CanBlurCheck(CallerType aCallerType) {
2333 // If dom.disable_window_flip == true, then content should not be allowed
2334 // to do blur (this would allow popunders, bug 369306)
2335 return aCallerType == CallerType::System ||
2336 !Preferences::GetBool("dom.disable_window_flip", true);
2339 void BrowsingContext::Blur(CallerType aCallerType, ErrorResult& aError) {
2340 if (!CanBlurCheck(aCallerType)) {
2341 return;
2344 if (ContentChild* cc = ContentChild::GetSingleton()) {
2345 cc->SendWindowBlur(this, aCallerType);
2346 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2347 Unused << cp->SendWindowBlur(this, aCallerType);
2351 Nullable<WindowProxyHolder> BrowsingContext::GetWindow() {
2352 if (XRE_IsParentProcess() && !IsInProcess()) {
2353 return nullptr;
2355 return WindowProxyHolder(this);
2358 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) {
2359 if (mIsDiscarded) {
2360 return nullptr;
2363 // We never return null or throw an error, but the implementation in
2364 // nsGlobalWindow does and we need to use the same signature.
2365 return WindowProxyHolder(Top());
2368 void BrowsingContext::GetOpener(JSContext* aCx,
2369 JS::MutableHandle<JS::Value> aOpener,
2370 ErrorResult& aError) const {
2371 RefPtr<BrowsingContext> opener = GetOpener();
2372 if (!opener) {
2373 aOpener.setNull();
2374 return;
2377 if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) {
2378 aError.NoteJSContextException(aCx);
2382 // We never throw an error, but the implementation in nsGlobalWindow does and
2383 // we need to use the same signature.
2384 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) {
2385 if (mIsDiscarded) {
2386 return nullptr;
2389 if (GetParent()) {
2390 return WindowProxyHolder(GetParent());
2392 return WindowProxyHolder(this);
2395 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2396 JS::Handle<JS::Value> aMessage,
2397 const nsAString& aTargetOrigin,
2398 const Sequence<JSObject*>& aTransfer,
2399 nsIPrincipal& aSubjectPrincipal,
2400 ErrorResult& aError) {
2401 if (mIsDiscarded) {
2402 return;
2405 RefPtr<BrowsingContext> sourceBc;
2406 PostMessageData data;
2407 data.targetOrigin() = aTargetOrigin;
2408 data.subjectPrincipal() = &aSubjectPrincipal;
2409 RefPtr<nsGlobalWindowInner> callerInnerWindow;
2410 nsAutoCString scriptLocation;
2411 // We don't need to get the caller's agentClusterId since that is used for
2412 // checking whether it's okay to sharing memory (and it's not allowed to share
2413 // memory cross processes)
2414 if (!nsGlobalWindowOuter::GatherPostMessageData(
2415 aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(),
2416 getter_AddRefs(data.targetOriginURI()),
2417 getter_AddRefs(data.callerPrincipal()),
2418 getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()),
2419 /* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) {
2420 return;
2422 if (sourceBc && sourceBc->IsDiscarded()) {
2423 return;
2425 data.source() = sourceBc;
2426 data.isFromPrivateWindow() =
2427 callerInnerWindow &&
2428 nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow);
2429 data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0;
2430 data.scriptLocation() = scriptLocation;
2431 JS::Rooted<JS::Value> transferArray(aCx);
2432 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
2433 &transferArray);
2434 if (NS_WARN_IF(aError.Failed())) {
2435 return;
2438 JS::CloneDataPolicy clonePolicy;
2439 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) {
2440 clonePolicy.allowSharedMemoryObjects();
2443 // We will see if the message is required to be in the same process or it can
2444 // be in the different process after Write().
2445 ipc::StructuredCloneData message = ipc::StructuredCloneData(
2446 StructuredCloneHolder::StructuredCloneScope::UnknownDestination,
2447 StructuredCloneHolder::TransferringSupported);
2448 message.Write(aCx, aMessage, transferArray, clonePolicy, aError);
2449 if (NS_WARN_IF(aError.Failed())) {
2450 return;
2453 ClonedOrErrorMessageData messageData;
2454 if (ContentChild* cc = ContentChild::GetSingleton()) {
2455 // The clone scope gets set when we write the message data based on the
2456 // requirements of that data that we're writing.
2457 // If the message data contains a shared memory object, then CloneScope
2458 // would return SameProcess. Otherwise, it returns DifferentProcess.
2459 if (message.CloneScope() ==
2460 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2461 ClonedMessageData clonedMessageData;
2462 if (!message.BuildClonedMessageData(clonedMessageData)) {
2463 aError.Throw(NS_ERROR_FAILURE);
2464 return;
2467 messageData = std::move(clonedMessageData);
2468 } else {
2469 MOZ_ASSERT(message.CloneScope() ==
2470 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2472 messageData = ErrorMessageData();
2474 nsContentUtils::ReportToConsole(
2475 nsIScriptError::warningFlag, "DOM Window"_ns,
2476 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2477 nsContentUtils::eDOM_PROPERTIES,
2478 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2481 cc->SendWindowPostMessage(this, messageData, data);
2482 } else if (ContentParent* cp = Canonical()->GetContentParent()) {
2483 if (message.CloneScope() ==
2484 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) {
2485 ClonedMessageData clonedMessageData;
2486 if (!message.BuildClonedMessageData(clonedMessageData)) {
2487 aError.Throw(NS_ERROR_FAILURE);
2488 return;
2491 messageData = std::move(clonedMessageData);
2492 } else {
2493 MOZ_ASSERT(message.CloneScope() ==
2494 StructuredCloneHolder::StructuredCloneScope::SameProcess);
2496 messageData = ErrorMessageData();
2498 nsContentUtils::ReportToConsole(
2499 nsIScriptError::warningFlag, "DOM Window"_ns,
2500 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr,
2501 nsContentUtils::eDOM_PROPERTIES,
2502 "PostMessageSharedMemoryObjectToCrossOriginWarning");
2505 Unused << cp->SendWindowPostMessage(this, messageData, data);
2509 void BrowsingContext::PostMessageMoz(JSContext* aCx,
2510 JS::Handle<JS::Value> aMessage,
2511 const WindowPostMessageOptions& aOptions,
2512 nsIPrincipal& aSubjectPrincipal,
2513 ErrorResult& aError) {
2514 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer,
2515 aSubjectPrincipal, aError);
2518 void BrowsingContext::SendCommitTransaction(ContentParent* aParent,
2519 const BaseTransaction& aTxn,
2520 uint64_t aEpoch) {
2521 Unused << aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2524 void BrowsingContext::SendCommitTransaction(ContentChild* aChild,
2525 const BaseTransaction& aTxn,
2526 uint64_t aEpoch) {
2527 aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch);
2530 BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() {
2531 MOZ_DIAGNOSTIC_ASSERT(mEverAttached);
2532 MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content);
2534 IPCInitializer init;
2535 init.mId = Id();
2536 init.mParentId = mParentWindow ? mParentWindow->Id() : 0;
2537 init.mWindowless = mWindowless;
2538 init.mUseRemoteTabs = mUseRemoteTabs;
2539 init.mUseRemoteSubframes = mUseRemoteSubframes;
2540 init.mCreatedDynamically = mCreatedDynamically;
2541 init.mChildOffset = mChildOffset;
2542 init.mOriginAttributes = mOriginAttributes;
2543 if (mChildSessionHistory && mozilla::SessionHistoryInParent()) {
2544 init.mSessionHistoryIndex = mChildSessionHistory->Index();
2545 init.mSessionHistoryCount = mChildSessionHistory->Count();
2547 init.mRequestContextId = mRequestContextId;
2548 init.mFields = mFields.RawValues();
2549 return init;
2552 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() {
2553 RefPtr<WindowContext> parent;
2554 if (mParentId != 0) {
2555 parent = WindowContext::GetById(mParentId);
2556 MOZ_RELEASE_ASSERT(parent);
2558 return parent.forget();
2561 already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() {
2562 RefPtr<BrowsingContext> opener;
2563 if (GetOpenerId() != 0) {
2564 opener = BrowsingContext::Get(GetOpenerId());
2565 MOZ_RELEASE_ASSERT(opener);
2567 return opener.forget();
2570 void BrowsingContext::StartDelayedAutoplayMediaComponents() {
2571 if (!mDocShell) {
2572 return;
2574 AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64,
2575 XRE_IsParentProcess() ? "Parent" : "Child", Id());
2576 mDocShell->StartDelayedAutoplayMediaComponents();
2579 nsresult BrowsingContext::ResetGVAutoplayRequestStatus() {
2580 MOZ_ASSERT(IsTop(),
2581 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2582 "browsing context");
2584 Transaction txn;
2585 txn.SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2586 txn.SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN);
2587 return txn.Commit(this);
2590 template <typename Callback>
2591 void BrowsingContext::WalkPresContexts(Callback&& aCallback) {
2592 PreOrderWalk([&](BrowsingContext* aContext) {
2593 if (nsIDocShell* shell = aContext->GetDocShell()) {
2594 if (RefPtr pc = shell->GetPresContext()) {
2595 aCallback(pc.get());
2601 void BrowsingContext::PresContextAffectingFieldChanged() {
2602 WalkPresContexts([&](nsPresContext* aPc) {
2603 aPc->RecomputeBrowsingContextDependentData();
2607 void BrowsingContext::DidSet(FieldIndex<IDX_SessionStoreEpoch>,
2608 uint32_t aOldValue) {
2609 if (!mCurrentWindowContext) {
2610 return;
2612 SessionStoreChild* sessionStoreChild =
2613 SessionStoreChild::From(mCurrentWindowContext->GetWindowGlobalChild());
2614 if (!sessionStoreChild) {
2615 return;
2618 sessionStoreChild->SetEpoch(GetSessionStoreEpoch());
2621 void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) {
2622 MOZ_ASSERT(IsTop(),
2623 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2624 "browsing context");
2627 void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) {
2628 MOZ_ASSERT(IsTop(),
2629 "Should only set GVAudibleAutoplayRequestStatus in the top-level "
2630 "browsing context");
2633 bool BrowsingContext::CanSet(FieldIndex<IDX_ExplicitActive>,
2634 const ExplicitActiveStatus&,
2635 ContentParent* aSource) {
2636 return XRE_IsParentProcess() && IsTop() && !aSource;
2639 void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>,
2640 ExplicitActiveStatus aOldValue) {
2641 MOZ_ASSERT(IsTop());
2643 const bool isActive = IsActive();
2644 const bool wasActive = [&] {
2645 if (aOldValue != ExplicitActiveStatus::None) {
2646 return aOldValue == ExplicitActiveStatus::Active;
2648 return GetParent() && GetParent()->IsActive();
2649 }();
2651 if (isActive == wasActive) {
2652 return;
2655 Group()->UpdateToplevelsSuspendedIfNeeded();
2656 if (XRE_IsParentProcess()) {
2657 if (BrowserParent* bp = Canonical()->GetBrowserParent()) {
2658 bp->RecomputeProcessPriority();
2659 #if defined(XP_WIN) && defined(ACCESSIBILITY)
2660 if (a11y::Compatibility::IsDolphin()) {
2661 // update active accessible documents on windows
2662 if (a11y::DocAccessibleParent* tabDoc =
2663 bp->GetTopLevelDocAccessible()) {
2664 HWND window = tabDoc->GetEmulatedWindowHandle();
2665 MOZ_ASSERT(window);
2666 if (window) {
2667 if (isActive) {
2668 a11y::nsWinUtils::ShowNativeWindow(window);
2669 } else {
2670 a11y::nsWinUtils::HideNativeWindow(window);
2675 #endif
2678 // NOTE(emilio): Ideally we'd want to reuse the ExplicitActiveStatus::None
2679 // set-up, but that's non-trivial to do because in content processes we
2680 // can't access the top-cross-chrome-boundary bc.
2681 auto manageTopDescendant = [&](auto* aChild) {
2682 if (!aChild->ManuallyManagesActiveness()) {
2683 aChild->SetIsActiveInternal(isActive, IgnoreErrors());
2684 if (BrowserParent* bp = aChild->GetBrowserParent()) {
2685 bp->SetRenderLayers(isActive);
2688 return CallState::Continue;
2690 Canonical()->CallOnAllTopDescendants(manageTopDescendant,
2691 /* aIncludeNestedBrowsers = */ false);
2694 PreOrderWalk([&](BrowsingContext* aContext) {
2695 if (nsCOMPtr<nsIDocShell> ds = aContext->GetDocShell()) {
2696 nsDocShell::Cast(ds)->ActivenessMaybeChanged();
2701 void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) {
2702 MOZ_ASSERT(IsTop(),
2703 "Should only set InRDMPane in the top-level browsing context");
2704 if (GetInRDMPane() == aOldValue) {
2705 return;
2707 PresContextAffectingFieldChanged();
2710 bool BrowsingContext::CanSet(FieldIndex<IDX_PageAwakeRequestCount>,
2711 uint32_t aNewValue, ContentParent* aSource) {
2712 return IsTop() && XRE_IsParentProcess() && !aSource;
2715 void BrowsingContext::DidSet(FieldIndex<IDX_PageAwakeRequestCount>,
2716 uint32_t aOldValue) {
2717 if (!IsTop() || aOldValue == GetPageAwakeRequestCount()) {
2718 return;
2720 Group()->UpdateToplevelsSuspendedIfNeeded();
2723 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue,
2724 ContentParent* aSource) -> CanSetResult {
2725 if (mozilla::SessionHistoryInParent()) {
2726 return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow
2727 : CanSetResult::Deny;
2730 // Without Session History in Parent, session restore code still needs to set
2731 // this from content processes.
2732 return LegacyRevertIfNotOwningOrParentProcess(aSource);
2735 void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) {
2736 RecomputeCanExecuteScripts();
2739 void BrowsingContext::RecomputeCanExecuteScripts() {
2740 const bool old = mCanExecuteScripts;
2741 if (!AllowJavascript()) {
2742 // Scripting has been explicitly disabled on our BrowsingContext.
2743 mCanExecuteScripts = false;
2744 } else if (GetParentWindowContext()) {
2745 // Otherwise, inherit parent.
2746 mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts();
2747 } else {
2748 // Otherwise, we're the root of the tree, and we haven't explicitly disabled
2749 // script. Allow.
2750 mCanExecuteScripts = true;
2753 if (old != mCanExecuteScripts) {
2754 for (WindowContext* wc : GetWindowContexts()) {
2755 wc->RecomputeCanExecuteScripts();
2760 bool BrowsingContext::InactiveForSuspend() const {
2761 if (!StaticPrefs::dom_suspend_inactive_enabled()) {
2762 return false;
2764 // We should suspend a page only when it's inactive and doesn't have any awake
2765 // request that is used to prevent page from being suspended because web page
2766 // might still need to run their script. Eg. waiting for media keys to resume
2767 // media, playing web audio, waiting in a video call conference room.
2768 return !IsActive() && GetPageAwakeRequestCount() == 0;
2771 bool BrowsingContext::CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2772 dom::TouchEventsOverride, ContentParent* aSource) {
2773 return XRE_IsParentProcess() && !aSource;
2776 void BrowsingContext::DidSet(FieldIndex<IDX_TouchEventsOverrideInternal>,
2777 dom::TouchEventsOverride&& aOldValue) {
2778 if (GetTouchEventsOverrideInternal() == aOldValue) {
2779 return;
2781 WalkPresContexts([&](nsPresContext* aPc) {
2782 aPc->MediaFeatureValuesChanged(
2783 {MediaFeatureChangeReason::SystemMetricsChange},
2784 // We're already iterating through sub documents, so we don't need to
2785 // propagate the change again.
2786 MediaFeatureChangePropagation::JustThisDocument);
2790 void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderColorSchemes>,
2791 EmbedderColorSchemes&& aOldValue) {
2792 if (GetEmbedderColorSchemes() == aOldValue) {
2793 return;
2795 PresContextAffectingFieldChanged();
2798 void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>,
2799 dom::PrefersColorSchemeOverride aOldValue) {
2800 MOZ_ASSERT(IsTop());
2801 if (PrefersColorSchemeOverride() == aOldValue) {
2802 return;
2804 PresContextAffectingFieldChanged();
2807 void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>,
2808 nsString&& aOldValue) {
2809 MOZ_ASSERT(IsTop());
2810 if (GetMediumOverride() == aOldValue) {
2811 return;
2813 PresContextAffectingFieldChanged();
2816 void BrowsingContext::DidSet(FieldIndex<IDX_DisplayMode>,
2817 enum DisplayMode aOldValue) {
2818 MOZ_ASSERT(IsTop());
2820 if (GetDisplayMode() == aOldValue) {
2821 return;
2824 WalkPresContexts([&](nsPresContext* aPc) {
2825 aPc->MediaFeatureValuesChanged(
2826 {MediaFeatureChangeReason::DisplayModeChange},
2827 // We're already iterating through sub documents, so we don't need
2828 // to propagate the change again.
2830 // Images and other resources don't change their display-mode
2831 // evaluation, display-mode is a property of the browsing context.
2832 MediaFeatureChangePropagation::JustThisDocument);
2836 void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) {
2837 MOZ_ASSERT(IsTop(), "Set muted flag on non top-level context!");
2838 USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64,
2839 GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child",
2840 Id());
2841 PreOrderWalk([&](BrowsingContext* aContext) {
2842 nsPIDOMWindowOuter* win = aContext->GetDOMWindow();
2843 if (win) {
2844 win->RefreshMediaElementsVolume();
2849 bool BrowsingContext::CanSet(FieldIndex<IDX_IsAppTab>, const bool& aValue,
2850 ContentParent* aSource) {
2851 return XRE_IsParentProcess() && !aSource && IsTop();
2854 bool BrowsingContext::CanSet(FieldIndex<IDX_HasSiblings>, const bool& aValue,
2855 ContentParent* aSource) {
2856 return XRE_IsParentProcess() && !aSource && IsTop();
2859 bool BrowsingContext::CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2860 const bool& aValue, ContentParent* aSource) {
2861 return IsTop();
2864 void BrowsingContext::DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>,
2865 bool aOldValue) {
2866 MOZ_ASSERT(IsTop(), "Set attribute on non top-level context!");
2867 if (aOldValue == GetShouldDelayMediaFromStart()) {
2868 return;
2870 if (!GetShouldDelayMediaFromStart()) {
2871 PreOrderWalk([&](BrowsingContext* aContext) {
2872 if (nsPIDOMWindowOuter* win = aContext->GetDOMWindow()) {
2873 win->ActivateMediaComponents();
2879 bool BrowsingContext::CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue,
2880 ContentParent* aSource) {
2881 return XRE_IsParentProcess() && !aSource && IsTop();
2884 void BrowsingContext::DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue) {
2885 MOZ_ASSERT(IsTop());
2886 if (GetOverrideDPPX() == aOldValue) {
2887 return;
2889 PresContextAffectingFieldChanged();
2892 void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent,
2893 ErrorResult& aRv) {
2894 Top()->SetUserAgentOverride(aUserAgent, aRv);
2897 nsresult BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) {
2898 return Top()->SetUserAgentOverride(aUserAgent);
2901 void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
2902 MOZ_ASSERT(IsTop());
2904 PreOrderWalk([&](BrowsingContext* aContext) {
2905 nsIDocShell* shell = aContext->GetDocShell();
2906 if (shell) {
2907 shell->ClearCachedUserAgent();
2912 bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool,
2913 ContentParent* aSource) {
2914 return IsTop() && !aSource && mozilla::BFCacheInParent();
2917 void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
2918 MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent());
2919 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2921 const bool isInBFCache = GetIsInBFCache();
2922 if (!isInBFCache) {
2923 UpdateCurrentTopByBrowserId(this);
2924 PreOrderWalk([&](BrowsingContext* aContext) {
2925 aContext->mIsInBFCache = false;
2926 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2927 if (shell) {
2928 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true);
2933 if (isInBFCache && XRE_IsContentProcess() && mDocShell) {
2934 nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide();
2937 if (isInBFCache) {
2938 PreOrderWalk([&](BrowsingContext* aContext) {
2939 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2940 if (shell) {
2941 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false);
2944 } else {
2945 PostOrderWalk([&](BrowsingContext* aContext) {
2946 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2947 if (shell) {
2948 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true);
2953 if (isInBFCache) {
2954 PreOrderWalk([&](BrowsingContext* aContext) {
2955 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
2956 if (shell) {
2957 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false);
2958 if (nsPresContext* pc = shell->GetPresContext()) {
2959 pc->EventStateManager()->ResetHoverState();
2962 aContext->mIsInBFCache = true;
2963 Document* doc = aContext->GetDocument();
2964 if (doc) {
2965 // Notifying needs to happen after mIsInBFCache is set to true.
2966 doc->NotifyActivityChanged();
2970 if (XRE_IsParentProcess()) {
2971 if (mCurrentWindowContext &&
2972 mCurrentWindowContext->Canonical()->Fullscreen()) {
2973 mCurrentWindowContext->Canonical()->ExitTopChromeDocumentFullscreen();
2979 void BrowsingContext::DidSet(FieldIndex<IDX_SyntheticDocumentContainer>) {
2980 if (WindowContext* parentWindowContext = GetParentWindowContext()) {
2981 parentWindowContext->UpdateChildSynthetic(this,
2982 GetSyntheticDocumentContainer());
2986 void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
2987 ErrorResult& aRv) {
2988 Top()->SetPlatformOverride(aPlatform, aRv);
2991 void BrowsingContext::DidSet(FieldIndex<IDX_PlatformOverride>) {
2992 MOZ_ASSERT(IsTop());
2994 PreOrderWalk([&](BrowsingContext* aContext) {
2995 nsIDocShell* shell = aContext->GetDocShell();
2996 if (shell) {
2997 shell->ClearCachedPlatform();
3002 auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess(
3003 ContentParent* aSource) -> CanSetResult {
3004 if (aSource) {
3005 MOZ_ASSERT(XRE_IsParentProcess());
3007 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
3008 return CanSetResult::Revert;
3010 } else if (!IsInProcess() && !XRE_IsParentProcess()) {
3011 // Don't allow this to be set from content processes that
3012 // don't own the BrowsingContext.
3013 return CanSetResult::Deny;
3016 return CanSetResult::Allow;
3019 bool BrowsingContext::CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
3020 const bool& aValue, ContentParent* aSource) {
3021 // Should only be set in the parent process.
3022 return XRE_IsParentProcess() && !aSource && IsTop();
3025 void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>,
3026 bool aOldValue) {
3027 bool isActivateEvent = GetIsActiveBrowserWindowInternal();
3028 // The browser window containing this context has changed
3029 // activation state so update window inactive document states
3030 // for all in-process documents.
3031 PreOrderWalk([isActivateEvent](BrowsingContext* aContext) {
3032 if (RefPtr<Document> doc = aContext->GetExtantDocument()) {
3033 doc->UpdateDocumentStates(DocumentState::WINDOW_INACTIVE, true);
3035 RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
3036 if (win) {
3037 RefPtr<MediaDevices> devices;
3038 if (isActivateEvent && (devices = win->GetExtantMediaDevices())) {
3039 devices->BrowserWindowBecameActive();
3042 if (XRE_IsContentProcess() &&
3043 (!aContext->GetParent() || !aContext->GetParent()->IsInProcess())) {
3044 // Send the inner window an activate/deactivate event if
3045 // the context is the top of a sub-tree of in-process
3046 // contexts.
3047 nsContentUtils::DispatchEventOnlyToChrome(
3048 doc, nsGlobalWindowInner::Cast(win),
3049 isActivateEvent ? u"activate"_ns : u"deactivate"_ns,
3050 CanBubble::eYes, Cancelable::eYes, nullptr);
3057 bool BrowsingContext::CanSet(FieldIndex<IDX_OpenerPolicy>,
3058 nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
3059 ContentParent* aSource) {
3060 // A potentially cross-origin isolated BC can't change opener policy, nor can
3061 // a BC become potentially cross-origin isolated. An unchanged policy is
3062 // always OK.
3063 return GetOpenerPolicy() == aPolicy ||
3064 (GetOpenerPolicy() !=
3065 nsILoadInfo::
3066 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP &&
3067 aPolicy !=
3068 nsILoadInfo::
3069 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP);
3072 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>,
3073 const bool& aAllowContentRetargeting,
3074 ContentParent* aSource) -> CanSetResult {
3075 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3078 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>,
3079 const bool& aAllowContentRetargetingOnChildren,
3080 ContentParent* aSource) -> CanSetResult {
3081 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3084 bool BrowsingContext::CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>,
3085 const bool& aAllowed, ContentParent* aSource) {
3086 return CheckOnlyEmbedderCanSet(aSource);
3089 bool BrowsingContext::CanSet(FieldIndex<IDX_UseErrorPages>,
3090 const bool& aUseErrorPages,
3091 ContentParent* aSource) {
3092 return CheckOnlyEmbedderCanSet(aSource);
3095 TouchEventsOverride BrowsingContext::TouchEventsOverride() const {
3096 for (const auto* bc = this; bc; bc = bc->GetParent()) {
3097 auto tev = bc->GetTouchEventsOverrideInternal();
3098 if (tev != dom::TouchEventsOverride::None) {
3099 return tev;
3102 return dom::TouchEventsOverride::None;
3105 bool BrowsingContext::TargetTopLevelLinkClicksToBlank() const {
3106 return Top()->GetTargetTopLevelLinkClicksToBlankInternal();
3109 // We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal`
3110 // BC field. And we map it to the top level BrowsingContext.
3111 bool BrowsingContext::WatchedByDevTools() {
3112 return Top()->GetWatchedByDevToolsInternal();
3115 // Enforce that the watchedByDevTools BC field can only be set on the top level
3116 // Browsing Context.
3117 bool BrowsingContext::CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>,
3118 const bool& aWatchedByDevTools,
3119 ContentParent* aSource) {
3120 return IsTop();
3122 void BrowsingContext::SetWatchedByDevTools(bool aWatchedByDevTools,
3123 ErrorResult& aRv) {
3124 if (!IsTop()) {
3125 aRv.ThrowInvalidModificationError(
3126 "watchedByDevTools can only be set on top BrowsingContext");
3127 return;
3129 SetWatchedByDevToolsInternal(aWatchedByDevTools, aRv);
3132 auto BrowsingContext::CanSet(FieldIndex<IDX_DefaultLoadFlags>,
3133 const uint32_t& aDefaultLoadFlags,
3134 ContentParent* aSource) -> CanSetResult {
3135 // Bug 1623565 - Are these flags only used by the debugger, which makes it
3136 // possible that this field can only be settable by the parent process?
3137 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3140 void BrowsingContext::DidSet(FieldIndex<IDX_DefaultLoadFlags>) {
3141 auto loadFlags = GetDefaultLoadFlags();
3142 if (GetDocShell()) {
3143 nsDocShell::Cast(GetDocShell())->SetLoadGroupDefaultLoadFlags(loadFlags);
3146 if (XRE_IsParentProcess()) {
3147 PreOrderWalk([&](BrowsingContext* aContext) {
3148 if (aContext != this) {
3149 // Setting load flags on a discarded context has no effect.
3150 Unused << aContext->SetDefaultLoadFlags(loadFlags);
3156 bool BrowsingContext::CanSet(FieldIndex<IDX_UseGlobalHistory>,
3157 const bool& aUseGlobalHistory,
3158 ContentParent* aSource) {
3159 // Should only be set in the parent process.
3160 // return XRE_IsParentProcess() && !aSource;
3161 return true;
3164 auto BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>,
3165 const nsString& aUserAgent, ContentParent* aSource)
3166 -> CanSetResult {
3167 if (!IsTop()) {
3168 return CanSetResult::Deny;
3171 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3174 auto BrowsingContext::CanSet(FieldIndex<IDX_PlatformOverride>,
3175 const nsString& aPlatform, ContentParent* aSource)
3176 -> CanSetResult {
3177 if (!IsTop()) {
3178 return CanSetResult::Deny;
3181 return LegacyRevertIfNotOwningOrParentProcess(aSource);
3184 bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) {
3185 if (XRE_IsParentProcess()) {
3186 uint64_t childId = aSource ? aSource->ChildID() : 0;
3187 return Canonical()->IsEmbeddedInProcess(childId);
3189 return mEmbeddedByThisProcess;
3192 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>,
3193 const uint64_t& aValue, ContentParent* aSource) {
3194 // If we have a parent window, our embedder inner window ID must match it.
3195 if (mParentWindow) {
3196 return mParentWindow->Id() == aValue;
3199 // For toplevel BrowsingContext instances, this value may only be set by the
3200 // parent process, or initialized to `0`.
3201 return CheckOnlyEmbedderCanSet(aSource);
3204 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>,
3205 const Maybe<nsString>&, ContentParent* aSource) {
3206 return CheckOnlyEmbedderCanSet(aSource);
3209 auto BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>,
3210 const uint64_t& aValue, ContentParent* aSource)
3211 -> CanSetResult {
3212 // Generally allow clearing this. We may want to be more precise about this
3213 // check in the future.
3214 if (aValue == 0) {
3215 return CanSetResult::Allow;
3218 // We must have access to the specified context.
3219 RefPtr<WindowContext> window = WindowContext::GetById(aValue);
3220 if (!window || window->GetBrowsingContext() != this) {
3221 return CanSetResult::Deny;
3224 if (aSource) {
3225 // If the sending process is no longer the current owner, revert
3226 MOZ_ASSERT(XRE_IsParentProcess());
3227 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) {
3228 return CanSetResult::Revert;
3230 } else if (XRE_IsContentProcess() && !IsOwnedByProcess()) {
3231 return CanSetResult::Deny;
3234 return CanSetResult::Allow;
3237 bool BrowsingContext::CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>,
3238 const uint64_t& aValue, ContentParent* aSource) {
3239 return XRE_IsParentProcess() && !aSource;
3242 void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) {
3243 RefPtr<WindowContext> prevWindowContext = mCurrentWindowContext.forget();
3244 mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId());
3245 MOZ_ASSERT(
3246 !mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext),
3247 "WindowContext not registered?");
3249 // Clear our cached `children` value, to ensure that JS sees the up-to-date
3250 // value.
3251 BrowsingContext_Binding::ClearCachedChildrenValue(this);
3253 if (XRE_IsParentProcess()) {
3254 if (prevWindowContext != mCurrentWindowContext) {
3255 if (prevWindowContext) {
3256 prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false);
3258 if (mCurrentWindowContext) {
3259 // We set a timer when we set the current inner window. This
3260 // will then flush the session storage to session store to
3261 // make sure that we don't miss to store session storage to
3262 // session store that is a result of navigation. This is due
3263 // to Bug 1700623. We wish to fix this in Bug 1711886, where
3264 // making sure to store everything would make this timer
3265 // unnecessary.
3266 Canonical()->MaybeScheduleSessionStoreUpdate();
3267 mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true);
3270 BrowserParent::UpdateFocusFromBrowsingContext();
3274 bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue,
3275 ContentParent* aSource) {
3276 // Ensure that we only mark a browsing context as popup spam once and never
3277 // unmark it.
3278 return aValue && !GetIsPopupSpam();
3281 void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) {
3282 if (GetIsPopupSpam()) {
3283 PopupBlocker::RegisterOpenPopupSpam();
3287 bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>,
3288 const nsString& aMessageManagerGroup,
3289 ContentParent* aSource) {
3290 // Should only be set in the parent process on toplevel.
3291 return XRE_IsParentProcess() && !aSource && IsTopContent();
3294 bool BrowsingContext::CanSet(
3295 FieldIndex<IDX_OrientationLock>,
3296 const mozilla::hal::ScreenOrientation& aOrientationLock,
3297 ContentParent* aSource) {
3298 return IsTop();
3301 bool BrowsingContext::IsLoading() {
3302 if (GetLoading()) {
3303 return true;
3306 // If we're in the same process as the page, we're possibly just
3307 // updating the flag.
3308 nsIDocShell* shell = GetDocShell();
3309 if (shell) {
3310 Document* doc = shell->GetDocument();
3311 return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE;
3314 return false;
3317 void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) {
3318 if (mFields.Get<IDX_Loading>()) {
3319 return;
3322 while (!mDeprioritizedLoadRunner.isEmpty()) {
3323 nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst();
3324 NS_DispatchToCurrentThread(runner.forget());
3327 if (IsTop()) {
3328 Group()->FlushPostMessageEvents();
3332 // Inform the Document for this context of the (potential) change in
3333 // loading state
3334 void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) {
3335 nsPIDOMWindowOuter* outer = GetDOMWindow();
3336 if (!outer) {
3337 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3338 ("DidSetAncestorLoading BC: %p -- No outer window", (void*)this));
3339 return;
3341 Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc();
3342 if (document) {
3343 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
3344 ("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)",
3345 (void*)this, GetAncestorLoading(), document->GetReadyStateEnum(),
3346 document->GetReadyStateEnum()));
3347 document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(),
3348 document->GetReadyStateEnum());
3352 void BrowsingContext::DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>) {
3353 MOZ_ASSERT(IsTop(),
3354 "Should only set AuthorStyleDisabledDefault in the top "
3355 "browsing context");
3357 // We don't need to handle changes to this field, since PageStyleChild.sys.mjs
3358 // will respond to the PageStyle:Disable message in all content processes.
3360 // But we store the state here on the top BrowsingContext so that the
3361 // docshell has somewhere to look for the current author style disabling
3362 // state when new iframes are inserted.
3365 void BrowsingContext::DidSet(FieldIndex<IDX_TextZoom>, float aOldValue) {
3366 if (GetTextZoom() == aOldValue) {
3367 return;
3370 if (IsInProcess()) {
3371 if (nsIDocShell* shell = GetDocShell()) {
3372 if (nsPresContext* pc = shell->GetPresContext()) {
3373 pc->RecomputeBrowsingContextDependentData();
3377 for (BrowsingContext* child : Children()) {
3378 // Setting text zoom on a discarded context has no effect.
3379 Unused << child->SetTextZoom(GetTextZoom());
3383 if (IsTop() && XRE_IsParentProcess()) {
3384 if (Element* element = GetEmbedderElement()) {
3385 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"TextZoomChange"_ns,
3386 CanBubble::eYes,
3387 ChromeOnlyDispatch::eYes);
3392 // TODO(emilio): It'd be potentially nicer and cheaper to allow to set this only
3393 // on the Top() browsing context, but there are a lot of tests that rely on
3394 // zooming a subframe so...
3395 void BrowsingContext::DidSet(FieldIndex<IDX_FullZoom>, float aOldValue) {
3396 if (GetFullZoom() == aOldValue) {
3397 return;
3400 if (IsInProcess()) {
3401 if (nsIDocShell* shell = GetDocShell()) {
3402 if (nsPresContext* pc = shell->GetPresContext()) {
3403 pc->RecomputeBrowsingContextDependentData();
3407 for (BrowsingContext* child : Children()) {
3408 // Setting full zoom on a discarded context has no effect.
3409 Unused << child->SetFullZoom(GetFullZoom());
3413 if (IsTop() && XRE_IsParentProcess()) {
3414 if (Element* element = GetEmbedderElement()) {
3415 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"FullZoomChange"_ns,
3416 CanBubble::eYes,
3417 ChromeOnlyDispatch::eYes);
3422 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) {
3423 MOZ_ASSERT(IsLoading());
3424 MOZ_ASSERT(Top() == this);
3426 RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner);
3427 mDeprioritizedLoadRunner.insertBack(runner);
3428 NS_DispatchToCurrentThreadQueue(
3429 runner.forget(), StaticPrefs::page_load_deprioritization_period(),
3430 EventQueuePriority::Idle);
3433 bool BrowsingContext::IsDynamic() const {
3434 const BrowsingContext* current = this;
3435 do {
3436 if (current->CreatedDynamically()) {
3437 return true;
3439 } while ((current = current->GetParent()));
3441 return false;
3444 bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const {
3445 for (const BrowsingContext* current = this; current && current->GetParent();
3446 current = current->GetParent()) {
3447 if (current->CreatedDynamically()) {
3448 return false;
3450 aPath.AppendElement(current->ChildOffset());
3452 return true;
3455 void BrowsingContext::GetHistoryID(JSContext* aCx,
3456 JS::MutableHandle<JS::Value> aVal,
3457 ErrorResult& aError) {
3458 if (!xpc::ID2JSValue(aCx, GetHistoryID(), aVal)) {
3459 aError.Throw(NS_ERROR_OUT_OF_MEMORY);
3463 void BrowsingContext::InitSessionHistory() {
3464 MOZ_ASSERT(!IsDiscarded());
3465 MOZ_ASSERT(IsTop());
3466 MOZ_ASSERT(EverAttached());
3468 if (!GetHasSessionHistory()) {
3469 MOZ_ALWAYS_SUCCEEDS(SetHasSessionHistory(true));
3473 ChildSHistory* BrowsingContext::GetChildSessionHistory() {
3474 if (!mozilla::SessionHistoryInParent()) {
3475 // For now we're checking that the session history object for the child
3476 // process is available before returning the ChildSHistory object, because
3477 // it is the actual implementation that ChildSHistory forwards to. This can
3478 // be removed once session history is stored exclusively in the parent
3479 // process.
3480 return mChildSessionHistory && mChildSessionHistory->IsInProcess()
3481 ? mChildSessionHistory.get()
3482 : nullptr;
3485 return mChildSessionHistory;
3488 void BrowsingContext::CreateChildSHistory() {
3489 MOZ_ASSERT(IsTop());
3490 MOZ_ASSERT(GetHasSessionHistory());
3491 MOZ_ASSERT(!mChildSessionHistory);
3493 // Because session history is global in a browsing context tree, every process
3494 // that has access to a browsing context tree needs access to its session
3495 // history. That is why we create the ChildSHistory object in every process
3496 // where we have access to this browsing context (which is the top one).
3497 mChildSessionHistory = new ChildSHistory(this);
3499 // If the top browsing context (this one) is loaded in this process then we
3500 // also create the session history implementation for the child process.
3501 // This can be removed once session history is stored exclusively in the
3502 // parent process.
3503 mChildSessionHistory->SetIsInProcess(IsInProcess());
3506 void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>,
3507 bool aOldValue) {
3508 MOZ_ASSERT(GetHasSessionHistory() || !aOldValue,
3509 "We don't support turning off session history.");
3511 if (GetHasSessionHistory() && !aOldValue) {
3512 CreateChildSHistory();
3516 bool BrowsingContext::CanSet(
3517 FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>,
3518 const bool& aTargetTopLevelLinkClicksToBlankInternal,
3519 ContentParent* aSource) {
3520 return XRE_IsParentProcess() && !aSource && IsTop();
3523 bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue,
3524 ContentParent* aSource) {
3525 // We should only be able to set this for toplevel contexts which don't have
3526 // an ID yet.
3527 return GetBrowserId() == 0 && IsTop() && Children().IsEmpty();
3530 bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>,
3531 bool aNewValue, ContentParent* aSource) {
3532 // Can only be cleared from `true` to `false`, and should only ever be set on
3533 // the toplevel BrowsingContext.
3534 return IsTop() && GetPendingInitialization() && !aNewValue;
3537 bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue,
3538 ContentParent* aSource) {
3539 return IsTop();
3542 bool BrowsingContext::CanSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
3543 const bool& aIsUnderHiddenEmbedderElement,
3544 ContentParent* aSource) {
3545 return true;
3548 bool BrowsingContext::CanSet(FieldIndex<IDX_ForceOffline>, bool aNewValue,
3549 ContentParent* aSource) {
3550 return XRE_IsParentProcess() && !aSource;
3553 void BrowsingContext::DidSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>,
3554 bool aOldValue) {
3555 nsIDocShell* shell = GetDocShell();
3556 if (!shell) {
3557 return;
3559 const bool newValue = IsUnderHiddenEmbedderElement();
3560 if (NS_WARN_IF(aOldValue == newValue)) {
3561 return;
3564 if (auto* bc = BrowserChild::GetFrom(shell)) {
3565 bc->UpdateVisibility();
3568 if (PresShell* presShell = shell->GetPresShell()) {
3569 presShell->SetIsUnderHiddenEmbedderElement(newValue);
3572 // Propagate to children.
3573 for (BrowsingContext* child : Children()) {
3574 Element* embedderElement = child->GetEmbedderElement();
3575 if (!embedderElement) {
3576 // TODO: We shouldn't need to null check here since `child` and the
3577 // element returned by `child->GetEmbedderElement()` are in our
3578 // process (the actual browsing context represented by `child` may not
3579 // be, but that doesn't matter). However, there are currently a very
3580 // small number of crashes due to `embedderElement` being null, somehow
3581 // - see bug 1551241. For now we wallpaper the crash.
3582 continue;
3585 bool embedderFrameIsHidden = true;
3586 if (auto* embedderFrame = embedderElement->GetPrimaryFrame()) {
3587 embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
3590 bool hidden = IsUnderHiddenEmbedderElement() || embedderFrameIsHidden;
3591 if (child->IsUnderHiddenEmbedderElement() != hidden) {
3592 Unused << child->SetIsUnderHiddenEmbedderElement(hidden);
3597 bool BrowsingContext::IsPopupAllowed() {
3598 for (auto* context = GetCurrentWindowContext(); context;
3599 context = context->GetParentWindowContext()) {
3600 if (context->CanShowPopup()) {
3601 return true;
3605 return false;
3608 /* static */
3609 bool BrowsingContext::ShouldAddEntryForRefresh(
3610 nsIURI* aPreviousURI, const SessionHistoryInfo& aInfo) {
3611 return ShouldAddEntryForRefresh(aPreviousURI, aInfo.GetURI(),
3612 aInfo.HasPostData());
3615 /* static */
3616 bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aPreviousURI,
3617 nsIURI* aNewURI,
3618 bool aHasPostData) {
3619 if (aHasPostData) {
3620 return true;
3623 bool equalsURI = false;
3624 if (aPreviousURI) {
3625 aPreviousURI->Equals(aNewURI, &equalsURI);
3627 return !equalsURI;
3630 void BrowsingContext::SessionHistoryCommit(
3631 const LoadingSessionHistoryInfo& aInfo, uint32_t aLoadType,
3632 nsIURI* aPreviousURI, SessionHistoryInfo* aPreviousActiveEntry,
3633 bool aPersist, bool aCloneEntryChildren, bool aChannelExpired,
3634 uint32_t aCacheKey) {
3635 nsID changeID = {};
3636 if (XRE_IsContentProcess()) {
3637 RefPtr<ChildSHistory> rootSH = Top()->GetChildSessionHistory();
3638 if (rootSH) {
3639 if (!aInfo.mLoadIsFromSessionHistory) {
3640 // We try to mimic as closely as possible what will happen in
3641 // CanonicalBrowsingContext::SessionHistoryCommit. We'll be
3642 // incrementing the session history length if we're not replacing,
3643 // this is a top-level load or it's not the initial load in an iframe,
3644 // ShouldUpdateSessionHistory(loadType) returns true and it's not a
3645 // refresh for which ShouldAddEntryForRefresh returns false.
3646 // It is possible that this leads to wrong length temporarily, but
3647 // so would not having the check for replace.
3648 // Note that nsSHistory::AddEntry does a replace load if the current
3649 // entry is not marked as a persisted entry. The child process does
3650 // not have access to the current entry, so we use the previous active
3651 // entry as the best approximation. When that's not the current entry
3652 // then the length might be wrong briefly, until the parent process
3653 // commits the actual length.
3654 if (!LOAD_TYPE_HAS_FLAGS(
3655 aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) &&
3656 (IsTop()
3657 ? (!aPreviousActiveEntry || aPreviousActiveEntry->GetPersist())
3658 : !!aPreviousActiveEntry) &&
3659 ShouldUpdateSessionHistory(aLoadType) &&
3660 (!LOAD_TYPE_HAS_FLAGS(aLoadType,
3661 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) ||
3662 ShouldAddEntryForRefresh(aPreviousURI, aInfo.mInfo))) {
3663 changeID = rootSH->AddPendingHistoryChange();
3665 } else {
3666 // History load doesn't change the length, only index.
3667 changeID = rootSH->AddPendingHistoryChange(aInfo.mOffset, 0);
3670 ContentChild* cc = ContentChild::GetSingleton();
3671 mozilla::Unused << cc->SendHistoryCommit(
3672 this, aInfo.mLoadId, changeID, aLoadType, aPersist, aCloneEntryChildren,
3673 aChannelExpired, aCacheKey);
3674 } else {
3675 Canonical()->SessionHistoryCommit(aInfo.mLoadId, changeID, aLoadType,
3676 aPersist, aCloneEntryChildren,
3677 aChannelExpired, aCacheKey);
3681 void BrowsingContext::SetActiveSessionHistoryEntry(
3682 const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo,
3683 uint32_t aLoadType, uint32_t aUpdatedCacheKey, bool aUpdateLength) {
3684 if (XRE_IsContentProcess()) {
3685 // XXX Why we update cache key only in content process case?
3686 if (aUpdatedCacheKey != 0) {
3687 aInfo->SetCacheKey(aUpdatedCacheKey);
3690 nsID changeID = {};
3691 if (aUpdateLength) {
3692 RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory();
3693 if (shistory) {
3694 changeID = shistory->AddPendingHistoryChange();
3697 ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry(
3698 this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey,
3699 changeID);
3700 } else {
3701 Canonical()->SetActiveSessionHistoryEntry(
3702 aPreviousScrollPos, aInfo, aLoadType, aUpdatedCacheKey, nsID());
3706 void BrowsingContext::ReplaceActiveSessionHistoryEntry(
3707 SessionHistoryInfo* aInfo) {
3708 if (XRE_IsContentProcess()) {
3709 ContentChild::GetSingleton()->SendReplaceActiveSessionHistoryEntry(this,
3710 *aInfo);
3711 } else {
3712 Canonical()->ReplaceActiveSessionHistoryEntry(aInfo);
3716 void BrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
3717 if (XRE_IsContentProcess()) {
3718 ContentChild::GetSingleton()
3719 ->SendRemoveDynEntriesFromActiveSessionHistoryEntry(this);
3720 } else {
3721 Canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
3725 void BrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) {
3726 if (XRE_IsContentProcess()) {
3727 ContentChild::GetSingleton()->SendRemoveFromSessionHistory(this, aChangeID);
3728 } else {
3729 Canonical()->RemoveFromSessionHistory(aChangeID);
3733 void BrowsingContext::HistoryGo(
3734 int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction,
3735 bool aUserActivation, std::function<void(Maybe<int32_t>&&)>&& aResolver) {
3736 if (XRE_IsContentProcess()) {
3737 ContentChild::GetSingleton()->SendHistoryGo(
3738 this, aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3739 std::move(aResolver),
3740 [](mozilla::ipc::
3741 ResponseRejectReason) { /* FIXME Is ignoring this fine? */ });
3742 } else {
3743 RefPtr<CanonicalBrowsingContext> self = Canonical();
3744 aResolver(self->HistoryGo(
3745 aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
3746 Canonical()->GetContentParent()
3747 ? Some(Canonical()->GetContentParent()->ChildID())
3748 : Nothing()));
3752 void BrowsingContext::SetChildSHistory(ChildSHistory* aChildSHistory) {
3753 mChildSessionHistory = aChildSHistory;
3754 mChildSessionHistory->SetBrowsingContext(this);
3755 mFields.SetWithoutSyncing<IDX_HasSessionHistory>(true);
3758 bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) {
3759 // We don't update session history on reload unless we're loading
3760 // an iframe in shift-reload case.
3761 return nsDocShell::ShouldUpdateGlobalHistory(aLoadType) &&
3762 (!(aLoadType & nsIDocShell::LOAD_CMD_RELOAD) ||
3763 (IsForceReloadType(aLoadType) && IsSubframe()));
3766 nsresult BrowsingContext::CheckLocationChangeRateLimit(CallerType aCallerType) {
3767 // We only rate limit non system callers
3768 if (aCallerType == CallerType::System) {
3769 return NS_OK;
3772 // Fetch rate limiting preferences
3773 uint32_t limitCount =
3774 StaticPrefs::dom_navigation_locationChangeRateLimit_count();
3775 uint32_t timeSpanSeconds =
3776 StaticPrefs::dom_navigation_locationChangeRateLimit_timespan();
3778 // Disable throttling if either of the preferences is set to 0.
3779 if (limitCount == 0 || timeSpanSeconds == 0) {
3780 return NS_OK;
3783 TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds);
3785 if (mLocationChangeRateLimitSpanStart.IsNull() ||
3786 ((TimeStamp::Now() - mLocationChangeRateLimitSpanStart) > throttleSpan)) {
3787 // Initial call or timespan exceeded, reset counter and timespan.
3788 mLocationChangeRateLimitSpanStart = TimeStamp::Now();
3789 mLocationChangeRateLimitCount = 1;
3790 return NS_OK;
3793 if (mLocationChangeRateLimitCount >= limitCount) {
3794 // Rate limit reached
3796 Document* doc = GetDocument();
3797 if (doc) {
3798 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc,
3799 nsContentUtils::eDOM_PROPERTIES,
3800 "LocChangeFloodingPrevented");
3803 return NS_ERROR_DOM_SECURITY_ERR;
3806 mLocationChangeRateLimitCount++;
3807 return NS_OK;
3810 void BrowsingContext::ResetLocationChangeRateLimit() {
3811 // Resetting the timestamp object will cause the check function to
3812 // init again and reset the rate limit.
3813 mLocationChangeRateLimitSpanStart = TimeStamp();
3816 void BrowsingContext::LocationCreated(dom::Location* aLocation) {
3817 MOZ_ASSERT(!aLocation->isInList());
3818 mLocations.insertBack(aLocation);
3821 void BrowsingContext::ClearCachedValuesOfLocations() {
3822 for (dom::Location* loc = mLocations.getFirst(); loc; loc = loc->getNext()) {
3823 loc->ClearCachedValues();
3827 } // namespace dom
3829 namespace ipc {
3831 void IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Write(
3832 IPC::MessageWriter* aWriter, IProtocol* aActor,
3833 const dom::MaybeDiscarded<dom::BrowsingContext>& aParam) {
3834 MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() ||
3835 aParam.GetMaybeDiscarded()->EverAttached());
3836 uint64_t id = aParam.ContextId();
3837 WriteIPDLParam(aWriter, aActor, id);
3840 bool IPDLParamTraits<dom::MaybeDiscarded<dom::BrowsingContext>>::Read(
3841 IPC::MessageReader* aReader, IProtocol* aActor,
3842 dom::MaybeDiscarded<dom::BrowsingContext>* aResult) {
3843 uint64_t id = 0;
3844 if (!ReadIPDLParam(aReader, aActor, &id)) {
3845 return false;
3848 if (id == 0) {
3849 *aResult = nullptr;
3850 } else if (RefPtr<dom::BrowsingContext> bc = dom::BrowsingContext::Get(id)) {
3851 *aResult = std::move(bc);
3852 } else {
3853 aResult->SetDiscarded(id);
3855 return true;
3858 void IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Write(
3859 IPC::MessageWriter* aWriter, IProtocol* aActor,
3860 const dom::BrowsingContext::IPCInitializer& aInit) {
3861 // Write actor ID parameters.
3862 WriteIPDLParam(aWriter, aActor, aInit.mId);
3863 WriteIPDLParam(aWriter, aActor, aInit.mParentId);
3864 WriteIPDLParam(aWriter, aActor, aInit.mWindowless);
3865 WriteIPDLParam(aWriter, aActor, aInit.mUseRemoteTabs);
3866 WriteIPDLParam(aWriter, aActor, aInit.mUseRemoteSubframes);
3867 WriteIPDLParam(aWriter, aActor, aInit.mCreatedDynamically);
3868 WriteIPDLParam(aWriter, aActor, aInit.mChildOffset);
3869 WriteIPDLParam(aWriter, aActor, aInit.mOriginAttributes);
3870 WriteIPDLParam(aWriter, aActor, aInit.mRequestContextId);
3871 WriteIPDLParam(aWriter, aActor, aInit.mSessionHistoryIndex);
3872 WriteIPDLParam(aWriter, aActor, aInit.mSessionHistoryCount);
3873 WriteIPDLParam(aWriter, aActor, aInit.mFields);
3876 bool IPDLParamTraits<dom::BrowsingContext::IPCInitializer>::Read(
3877 IPC::MessageReader* aReader, IProtocol* aActor,
3878 dom::BrowsingContext::IPCInitializer* aInit) {
3879 // Read actor ID parameters.
3880 if (!ReadIPDLParam(aReader, aActor, &aInit->mId) ||
3881 !ReadIPDLParam(aReader, aActor, &aInit->mParentId) ||
3882 !ReadIPDLParam(aReader, aActor, &aInit->mWindowless) ||
3883 !ReadIPDLParam(aReader, aActor, &aInit->mUseRemoteTabs) ||
3884 !ReadIPDLParam(aReader, aActor, &aInit->mUseRemoteSubframes) ||
3885 !ReadIPDLParam(aReader, aActor, &aInit->mCreatedDynamically) ||
3886 !ReadIPDLParam(aReader, aActor, &aInit->mChildOffset) ||
3887 !ReadIPDLParam(aReader, aActor, &aInit->mOriginAttributes) ||
3888 !ReadIPDLParam(aReader, aActor, &aInit->mRequestContextId) ||
3889 !ReadIPDLParam(aReader, aActor, &aInit->mSessionHistoryIndex) ||
3890 !ReadIPDLParam(aReader, aActor, &aInit->mSessionHistoryCount) ||
3891 !ReadIPDLParam(aReader, aActor, &aInit->mFields)) {
3892 return false;
3894 return true;
3897 template struct IPDLParamTraits<dom::BrowsingContext::BaseTransaction>;
3899 } // namespace ipc
3900 } // namespace mozilla