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