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/CanonicalBrowsingContext.h"
9 #include "mozilla/CheckedInt.h"
10 #include "mozilla/ErrorResult.h"
11 #include "mozilla/EventForwards.h"
12 #include "mozilla/AsyncEventDispatcher.h"
13 #include "mozilla/dom/BrowserParent.h"
14 #include "mozilla/dom/BrowsingContextBinding.h"
15 #include "mozilla/dom/BrowsingContextGroup.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/dom/EventTarget.h"
18 #include "mozilla/dom/PBrowserParent.h"
19 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
20 #include "mozilla/dom/PWindowGlobalParent.h"
21 #include "mozilla/dom/WindowGlobalParent.h"
22 #include "mozilla/dom/ContentProcessManager.h"
23 #include "mozilla/dom/MediaController.h"
24 #include "mozilla/dom/MediaControlService.h"
25 #include "mozilla/dom/ContentPlaybackController.h"
26 #include "mozilla/dom/SessionStorageManager.h"
27 #include "mozilla/ipc/ProtocolUtils.h"
29 # include "mozilla/layout/RemotePrintJobParent.h"
31 #include "mozilla/net/DocumentLoadListener.h"
32 #include "mozilla/NullPrincipal.h"
33 #include "mozilla/StaticPrefs_docshell.h"
34 #include "mozilla/StaticPrefs_fission.h"
35 #include "mozilla/Telemetry.h"
36 #include "nsILayoutHistoryState.h"
37 #include "nsIPrintSettings.h"
38 #include "nsIPrintSettingsService.h"
39 #include "nsISupports.h"
40 #include "nsIWebNavigation.h"
41 #include "mozilla/MozPromiseInlines.h"
42 #include "nsDocShell.h"
43 #include "nsFrameLoader.h"
44 #include "nsFrameLoaderOwner.h"
45 #include "nsGlobalWindowOuter.h"
46 #include "nsIWebBrowserChrome.h"
47 #include "nsIXULRuntime.h"
48 #include "nsNetUtil.h"
49 #include "nsSHistory.h"
50 #include "nsSecureBrowserUI.h"
51 #include "nsQueryObject.h"
52 #include "nsBrowserStatusFilter.h"
53 #include "nsIBrowser.h"
54 #include "nsTHashSet.h"
55 #include "SessionStoreFunctions.h"
56 #include "nsIXPConnect.h"
57 #include "nsImportModule.h"
58 #include "UnitTransforms.h"
60 using namespace mozilla::ipc
;
62 extern mozilla::LazyLogModule gAutoplayPermissionLog
;
63 extern mozilla::LazyLogModule gSHLog
;
64 extern mozilla::LazyLogModule gSHIPBFCacheLog
;
66 #define AUTOPLAY_LOG(msg, ...) \
67 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
69 static mozilla::LazyLogModule
sPBContext("PBContext");
71 // Global count of canonical browsing contexts with the private attribute set
72 static uint32_t gNumberOfPrivateContexts
= 0;
74 // Current parent process epoch for parent initiated navigations
75 static uint64_t gParentInitiatedNavigationEpoch
= 0;
77 static void IncreasePrivateCount() {
78 gNumberOfPrivateContexts
++;
79 MOZ_LOG(sPBContext
, mozilla::LogLevel::Debug
,
80 ("%s: Private browsing context count %d -> %d", __func__
,
81 gNumberOfPrivateContexts
- 1, gNumberOfPrivateContexts
));
82 if (gNumberOfPrivateContexts
> 1) {
86 static bool sHasSeenPrivateContext
= false;
87 if (!sHasSeenPrivateContext
) {
88 sHasSeenPrivateContext
= true;
89 mozilla::Telemetry::ScalarSet(
90 mozilla::Telemetry::ScalarID::DOM_PARENTPROCESS_PRIVATE_WINDOW_USED
,
95 static void DecreasePrivateCount() {
96 MOZ_ASSERT(gNumberOfPrivateContexts
> 0);
97 gNumberOfPrivateContexts
--;
99 MOZ_LOG(sPBContext
, mozilla::LogLevel::Debug
,
100 ("%s: Private browsing context count %d -> %d", __func__
,
101 gNumberOfPrivateContexts
+ 1, gNumberOfPrivateContexts
));
102 if (!gNumberOfPrivateContexts
&&
103 !mozilla::Preferences::GetBool("browser.privatebrowsing.autostart")) {
104 nsCOMPtr
<nsIObserverService
> observerService
=
105 mozilla::services::GetObserverService();
106 if (observerService
) {
107 MOZ_LOG(sPBContext
, mozilla::LogLevel::Debug
,
108 ("%s: last-pb-context-exited fired", __func__
));
109 observerService
->NotifyObservers(nullptr, "last-pb-context-exited",
118 extern mozilla::LazyLogModule gUserInteractionPRLog
;
120 #define USER_ACTIVATION_LOG(msg, ...) \
121 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
123 CanonicalBrowsingContext::CanonicalBrowsingContext(WindowContext
* aParentWindow
,
124 BrowsingContextGroup
* aGroup
,
125 uint64_t aBrowsingContextId
,
126 uint64_t aOwnerProcessId
,
127 uint64_t aEmbedderProcessId
,
128 BrowsingContext::Type aType
,
130 : BrowsingContext(aParentWindow
, aGroup
, aBrowsingContextId
, aType
,
132 mProcessId(aOwnerProcessId
),
133 mEmbedderProcessId(aEmbedderProcessId
),
134 mPermanentKey(JS::NullValue()) {
135 // You are only ever allowed to create CanonicalBrowsingContexts in the
137 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
139 // The initial URI in a BrowsingContext is always "about:blank".
141 NS_NewURI(getter_AddRefs(mCurrentRemoteURI
), "about:blank"));
143 mozilla::HoldJSObjects(this);
146 CanonicalBrowsingContext::~CanonicalBrowsingContext() {
147 mPermanentKey
.setNull();
149 mozilla::DropJSObjects(this);
151 if (mSessionHistory
) {
152 mSessionHistory
->SetBrowsingContext(nullptr);
157 already_AddRefed
<CanonicalBrowsingContext
> CanonicalBrowsingContext::Get(
159 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
160 return BrowsingContext::Get(aId
).downcast
<CanonicalBrowsingContext
>();
164 CanonicalBrowsingContext
* CanonicalBrowsingContext::Cast(
165 BrowsingContext
* aContext
) {
166 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
167 return static_cast<CanonicalBrowsingContext
*>(aContext
);
171 const CanonicalBrowsingContext
* CanonicalBrowsingContext::Cast(
172 const BrowsingContext
* aContext
) {
173 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
174 return static_cast<const CanonicalBrowsingContext
*>(aContext
);
177 already_AddRefed
<CanonicalBrowsingContext
> CanonicalBrowsingContext::Cast(
178 already_AddRefed
<BrowsingContext
>&& aContext
) {
179 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
180 return aContext
.downcast
<CanonicalBrowsingContext
>();
183 ContentParent
* CanonicalBrowsingContext::GetContentParent() const {
184 if (mProcessId
== 0) {
188 ContentProcessManager
* cpm
= ContentProcessManager::GetSingleton();
192 return cpm
->GetContentProcessById(ContentParentId(mProcessId
));
195 void CanonicalBrowsingContext::GetCurrentRemoteType(nsACString
& aRemoteType
,
196 ErrorResult
& aRv
) const {
197 // If we're in the parent process, dump out the void string.
198 if (mProcessId
== 0) {
199 aRemoteType
= NOT_REMOTE_TYPE
;
203 ContentParent
* cp
= GetContentParent();
205 aRv
.Throw(NS_ERROR_UNEXPECTED
);
209 aRemoteType
= cp
->GetRemoteType();
212 void CanonicalBrowsingContext::SetOwnerProcessId(uint64_t aProcessId
) {
213 MOZ_LOG(GetLog(), LogLevel::Debug
,
214 ("SetOwnerProcessId for 0x%08" PRIx64
" (0x%08" PRIx64
215 " -> 0x%08" PRIx64
")",
216 Id(), mProcessId
, aProcessId
));
218 mProcessId
= aProcessId
;
221 nsISecureBrowserUI
* CanonicalBrowsingContext::GetSecureBrowserUI() {
225 if (!mSecureBrowserUI
) {
226 mSecureBrowserUI
= new nsSecureBrowserUI(this);
228 return mSecureBrowserUI
;
232 // The DocShellProgressBridge is attached to a root content docshell loaded in
233 // the parent process. Notifications are paired up with the docshell which they
234 // came from, so that they can be fired to the correct
235 // BrowsingContextWebProgress and bubble through this tree separately.
237 // Notifications are filtered by a nsBrowserStatusFilter before being received
238 // by the DocShellProgressBridge.
239 class DocShellProgressBridge
: public nsIWebProgressListener
{
242 // NOTE: This relies in the expansion of `NS_FORWARD_SAFE` and all listener
243 // methods accepting an `aWebProgress` argument. If this changes in the
244 // future, this may need to be written manually.
245 NS_FORWARD_SAFE_NSIWEBPROGRESSLISTENER(GetTargetContext(aWebProgress
))
247 explicit DocShellProgressBridge(uint64_t aTopContextId
)
248 : mTopContextId(aTopContextId
) {}
251 virtual ~DocShellProgressBridge() = default;
253 nsIWebProgressListener
* GetTargetContext(nsIWebProgress
* aWebProgress
) {
254 RefPtr
<CanonicalBrowsingContext
> context
;
255 if (nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(aWebProgress
)) {
256 context
= docShell
->GetBrowsingContext()->Canonical();
258 context
= CanonicalBrowsingContext::Get(mTopContextId
);
260 return context
&& !context
->IsDiscarded() ? context
->GetWebProgress()
264 uint64_t mTopContextId
= 0;
267 NS_IMPL_ISUPPORTS(DocShellProgressBridge
, nsIWebProgressListener
)
270 void CanonicalBrowsingContext::MaybeAddAsProgressListener(
271 nsIWebProgress
* aWebProgress
) {
272 // Only add as a listener if the created docshell is a toplevel content
273 // docshell. We'll get notifications for all of our subframes through a single
275 if (!IsTopContent()) {
279 if (!mDocShellProgressBridge
) {
280 mDocShellProgressBridge
= new DocShellProgressBridge(Id());
281 mStatusFilter
= new nsBrowserStatusFilter();
282 mStatusFilter
->AddProgressListener(mDocShellProgressBridge
,
283 nsIWebProgress::NOTIFY_ALL
);
286 aWebProgress
->AddProgressListener(mStatusFilter
, nsIWebProgress::NOTIFY_ALL
);
289 void CanonicalBrowsingContext::ReplacedBy(
290 CanonicalBrowsingContext
* aNewContext
,
291 const NavigationIsolationOptions
& aRemotenessOptions
) {
292 MOZ_ASSERT(!aNewContext
->mWebProgress
);
293 MOZ_ASSERT(!aNewContext
->mSessionHistory
);
294 MOZ_ASSERT(IsTop() && aNewContext
->IsTop());
297 aNewContext
->mIsReplaced
= false;
300 mStatusFilter
->RemoveProgressListener(mDocShellProgressBridge
);
301 mStatusFilter
= nullptr;
304 mWebProgress
->ContextReplaced(aNewContext
);
305 aNewContext
->mWebProgress
= std::move(mWebProgress
);
307 // Use the Transaction for the fields which need to be updated whether or not
308 // the new context has been attached before.
309 // SetWithoutSyncing can be used if context hasn't been attached.
311 txn
.SetBrowserId(GetBrowserId());
312 txn
.SetHistoryID(GetHistoryID());
313 txn
.SetExplicitActive(GetExplicitActive());
314 txn
.SetEmbedderColorScheme(GetEmbedderColorScheme());
315 txn
.SetHasRestoreData(GetHasRestoreData());
316 txn
.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart());
317 // As this is a different BrowsingContext, set InitialSandboxFlags to the
318 // current flags in the new context so that they also apply to any initial
319 // about:blank documents created in it.
320 txn
.SetSandboxFlags(GetSandboxFlags());
321 txn
.SetInitialSandboxFlags(GetSandboxFlags());
322 txn
.SetTargetTopLevelLinkClicksToBlankInternal(
323 TargetTopLevelLinkClicksToBlank());
324 if (aNewContext
->EverAttached()) {
325 MOZ_ALWAYS_SUCCEEDS(txn
.Commit(aNewContext
));
327 txn
.CommitWithoutSyncing(aNewContext
);
330 aNewContext
->mRestoreState
= mRestoreState
.forget();
331 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
333 // XXXBFCache name handling is still a bit broken in Fission in general,
334 // at least in case name should be cleared.
335 if (aRemotenessOptions
.mTryUseBFCache
) {
336 MOZ_ASSERT(!aNewContext
->EverAttached());
337 aNewContext
->mFields
.SetWithoutSyncing
<IDX_Name
>(GetName());
338 // We don't copy over HasLoadedNonInitialDocument here, we'll actually end
339 // up loading a new initial document at this point, before the real load.
340 // The real load will then end up setting HasLoadedNonInitialDocument to
344 if (mSessionHistory
) {
345 mSessionHistory
->SetBrowsingContext(aNewContext
);
346 // At this point we will be creating a new ChildSHistory in the child.
347 // That means that the child's epoch will be reset, so it makes sense to
348 // reset the epoch in the parent too.
349 mSessionHistory
->SetEpoch(0, Nothing());
350 mSessionHistory
.swap(aNewContext
->mSessionHistory
);
351 RefPtr
<ChildSHistory
> childSHistory
= ForgetChildSHistory();
352 aNewContext
->SetChildSHistory(childSHistory
);
355 if (mozilla::SessionHistoryInParent()) {
356 BackgroundSessionStorageManager::PropagateManager(Id(), aNewContext
->Id());
359 // Transfer the ownership of the priority active status from the old context
360 // to the new context.
361 aNewContext
->mPriorityActive
= mPriorityActive
;
362 mPriorityActive
= false;
364 MOZ_ASSERT(aNewContext
->mLoadingEntries
.IsEmpty());
365 mLoadingEntries
.SwapElements(aNewContext
->mLoadingEntries
);
366 MOZ_ASSERT(!aNewContext
->mActiveEntry
);
367 mActiveEntry
.swap(aNewContext
->mActiveEntry
);
369 aNewContext
->mPermanentKey
= mPermanentKey
;
370 mPermanentKey
.setNull();
373 void CanonicalBrowsingContext::UpdateSecurityState() {
374 if (mSecureBrowserUI
) {
375 mSecureBrowserUI
->RecomputeSecurityFlags();
379 void CanonicalBrowsingContext::GetWindowGlobals(
380 nsTArray
<RefPtr
<WindowGlobalParent
>>& aWindows
) {
381 aWindows
.SetCapacity(GetWindowContexts().Length());
382 for (auto& window
: GetWindowContexts()) {
383 aWindows
.AppendElement(static_cast<WindowGlobalParent
*>(window
.get()));
387 WindowGlobalParent
* CanonicalBrowsingContext::GetCurrentWindowGlobal() const {
388 return static_cast<WindowGlobalParent
*>(GetCurrentWindowContext());
391 WindowGlobalParent
* CanonicalBrowsingContext::GetParentWindowContext() {
392 return static_cast<WindowGlobalParent
*>(
393 BrowsingContext::GetParentWindowContext());
396 WindowGlobalParent
* CanonicalBrowsingContext::GetTopWindowContext() {
397 return static_cast<WindowGlobalParent
*>(
398 BrowsingContext::GetTopWindowContext());
401 already_AddRefed
<nsIWidget
>
402 CanonicalBrowsingContext::GetParentProcessWidgetContaining() {
403 // If our document is loaded in-process, such as chrome documents, get the
404 // widget directly from our outer window. Otherwise, try to get the widget
405 // from the toplevel content's browser's element.
406 nsCOMPtr
<nsIWidget
> widget
;
407 if (nsGlobalWindowOuter
* window
= nsGlobalWindowOuter::Cast(GetDOMWindow())) {
408 widget
= window
->GetNearestWidget();
409 } else if (Element
* topEmbedder
= Top()->GetEmbedderElement()) {
410 widget
= nsContentUtils::WidgetForContent(topEmbedder
);
412 widget
= nsContentUtils::WidgetForDocument(topEmbedder
->OwnerDoc());
417 widget
= widget
->GetTopLevelWidget();
420 return widget
.forget();
423 already_AddRefed
<nsIBrowserDOMWindow
>
424 CanonicalBrowsingContext::GetBrowserDOMWindow() {
425 RefPtr
<CanonicalBrowsingContext
> chromeTop
= TopCrossChromeBoundary();
426 if (nsCOMPtr
<nsIDOMChromeWindow
> chromeWin
=
427 do_QueryInterface(chromeTop
->GetDOMWindow())) {
428 nsCOMPtr
<nsIBrowserDOMWindow
> bdw
;
429 if (NS_SUCCEEDED(chromeWin
->GetBrowserDOMWindow(getter_AddRefs(bdw
)))) {
436 already_AddRefed
<WindowGlobalParent
>
437 CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
438 uint64_t windowId
= GetEmbedderInnerWindowId();
443 return WindowGlobalParent::GetByInnerWindowId(windowId
);
446 already_AddRefed
<CanonicalBrowsingContext
>
447 CanonicalBrowsingContext::GetParentCrossChromeBoundary() {
449 return do_AddRef(Cast(GetParent()));
451 if (GetEmbedderElement()) {
453 Cast(GetEmbedderElement()->OwnerDoc()->GetBrowsingContext()));
458 already_AddRefed
<CanonicalBrowsingContext
>
459 CanonicalBrowsingContext::TopCrossChromeBoundary() {
460 RefPtr
<CanonicalBrowsingContext
> bc(this);
461 while (RefPtr
<CanonicalBrowsingContext
> parent
=
462 bc
->GetParentCrossChromeBoundary()) {
463 bc
= parent
.forget();
468 Nullable
<WindowProxyHolder
> CanonicalBrowsingContext::GetTopChromeWindow() {
469 RefPtr
<CanonicalBrowsingContext
> bc
= TopCrossChromeBoundary();
470 if (bc
->IsChrome()) {
471 return WindowProxyHolder(bc
.forget());
476 nsISHistory
* CanonicalBrowsingContext::GetSessionHistory() {
478 return Cast(Top())->GetSessionHistory();
481 // Check GetChildSessionHistory() to make sure that this BrowsingContext has
482 // session history enabled.
483 if (!mSessionHistory
&& GetChildSessionHistory()) {
484 mSessionHistory
= new nsSHistory(this);
487 return mSessionHistory
;
490 SessionHistoryEntry
* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() {
494 void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
495 SessionHistoryEntry
* aEntry
) {
496 mActiveEntry
= aEntry
;
499 bool CanonicalBrowsingContext::HasHistoryEntry(nsISHEntry
* aEntry
) {
500 // XXX Should we check also loading entries?
501 return aEntry
&& mActiveEntry
== aEntry
;
504 void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry
* aOldEntry
,
505 nsISHEntry
* aNewEntry
) {
506 // XXX Should we check also loading entries?
507 if (mActiveEntry
== aOldEntry
) {
508 nsCOMPtr
<SessionHistoryEntry
> newEntry
= do_QueryInterface(aNewEntry
);
509 mActiveEntry
= newEntry
.forget();
513 void CanonicalBrowsingContext::AddLoadingSessionHistoryEntry(
514 uint64_t aLoadId
, SessionHistoryEntry
* aEntry
) {
515 Unused
<< SetHistoryID(aEntry
->DocshellID());
516 mLoadingEntries
.AppendElement(LoadingSessionHistoryEntry
{aLoadId
, aEntry
});
519 void CanonicalBrowsingContext::GetLoadingSessionHistoryInfoFromParent(
520 Maybe
<LoadingSessionHistoryInfo
>& aLoadingInfo
) {
521 nsISHistory
* shistory
= GetSessionHistory();
522 if (!shistory
|| !GetParent()) {
526 SessionHistoryEntry
* parentSHE
=
527 GetParent()->Canonical()->GetActiveSessionHistoryEntry();
530 for (BrowsingContext
* sibling
: GetParent()->Children()) {
532 if (sibling
== this) {
533 nsCOMPtr
<nsISHEntry
> shEntry
;
534 parentSHE
->GetChildSHEntryIfHasNoDynamicallyAddedChild(
535 index
, getter_AddRefs(shEntry
));
536 nsCOMPtr
<SessionHistoryEntry
> she
= do_QueryInterface(shEntry
);
538 aLoadingInfo
.emplace(she
);
539 mLoadingEntries
.AppendElement(LoadingSessionHistoryEntry
{
540 aLoadingInfo
.value().mLoadId
, she
.get()});
541 Unused
<< SetHistoryID(she
->DocshellID());
549 UniquePtr
<LoadingSessionHistoryInfo
>
550 CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
551 nsDocShellLoadState
* aLoadState
, nsIChannel
* aChannel
) {
552 RefPtr
<SessionHistoryEntry
> entry
;
553 const LoadingSessionHistoryInfo
* existingLoadingInfo
=
554 aLoadState
->GetLoadingSessionHistoryInfo();
555 if (existingLoadingInfo
) {
556 entry
= SessionHistoryEntry::GetByLoadId(existingLoadingInfo
->mLoadId
);
557 MOZ_LOG(gSHLog
, LogLevel::Verbose
,
558 ("SHEntry::GetByLoadId(%" PRIu64
") -> %p",
559 existingLoadingInfo
->mLoadId
, entry
.get()));
563 Unused
<< SetHistoryEntryCount(entry
->BCHistoryLength());
564 } else if (aLoadState
->LoadType() == LOAD_REFRESH
&&
565 !ShouldAddEntryForRefresh(aLoadState
->URI(),
566 aLoadState
->PostDataStream()) &&
568 entry
= mActiveEntry
;
570 entry
= new SessionHistoryEntry(aLoadState
, aChannel
);
572 // Only top level pages care about Get/SetPersist.
574 nsDocShell::ShouldAddToSessionHistory(aLoadState
->URI(), aChannel
));
575 } else if (mActiveEntry
|| !mLoadingEntries
.IsEmpty()) {
576 entry
->SetIsSubFrame(true);
578 entry
->SetDocshellID(GetHistoryID());
579 entry
->SetIsDynamicallyAdded(CreatedDynamically());
580 entry
->SetForInitialLoad(true);
582 MOZ_DIAGNOSTIC_ASSERT(entry
);
584 UniquePtr
<LoadingSessionHistoryInfo
> loadingInfo
;
585 if (existingLoadingInfo
) {
586 loadingInfo
= MakeUnique
<LoadingSessionHistoryInfo
>(*existingLoadingInfo
);
588 loadingInfo
= MakeUnique
<LoadingSessionHistoryInfo
>(entry
);
589 mLoadingEntries
.AppendElement(
590 LoadingSessionHistoryEntry
{loadingInfo
->mLoadId
, entry
});
593 MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(loadingInfo
->mLoadId
) == entry
);
598 UniquePtr
<LoadingSessionHistoryInfo
>
599 CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
600 LoadingSessionHistoryInfo
* aInfo
, nsIChannel
* aNewChannel
) {
602 MOZ_ASSERT(aNewChannel
);
604 SessionHistoryInfo newInfo
= SessionHistoryInfo(
605 aNewChannel
, aInfo
->mInfo
.LoadType(),
606 aInfo
->mInfo
.GetPartitionedPrincipalToInherit(), aInfo
->mInfo
.GetCsp());
608 for (size_t i
= 0; i
< mLoadingEntries
.Length(); ++i
) {
609 if (mLoadingEntries
[i
].mLoadId
== aInfo
->mLoadId
) {
610 RefPtr
<SessionHistoryEntry
> loadingEntry
= mLoadingEntries
[i
].mEntry
;
611 loadingEntry
->SetInfo(&newInfo
);
614 // Only top level pages care about Get/SetPersist.
615 nsCOMPtr
<nsIURI
> uri
;
616 aNewChannel
->GetURI(getter_AddRefs(uri
));
617 loadingEntry
->SetPersist(
618 nsDocShell::ShouldAddToSessionHistory(uri
, aNewChannel
));
620 loadingEntry
->SetIsSubFrame(aInfo
->mInfo
.IsSubFrame());
622 loadingEntry
->SetDocshellID(GetHistoryID());
623 loadingEntry
->SetIsDynamicallyAdded(CreatedDynamically());
624 return MakeUnique
<LoadingSessionHistoryInfo
>(loadingEntry
, aInfo
);
630 using PrintPromise
= CanonicalBrowsingContext::PrintPromise
;
632 class PrintListenerAdapter final
: public nsIWebProgressListener
{
634 explicit PrintListenerAdapter(PrintPromise::Private
* aPromise
)
635 : mPromise(aPromise
) {}
639 // NS_DECL_NSIWEBPROGRESSLISTENER
640 NS_IMETHOD
OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
641 uint32_t aStateFlags
, nsresult aStatus
) override
{
642 if (aStateFlags
& nsIWebProgressListener::STATE_STOP
&&
643 aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
&& mPromise
) {
644 mPromise
->Resolve(true, __func__
);
649 NS_IMETHOD
OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
651 const char16_t
* aMessage
) override
{
652 if (aStatus
!= NS_OK
&& mPromise
) {
653 mPromise
->Reject(aStatus
, __func__
);
658 NS_IMETHOD
OnProgressChange(nsIWebProgress
* aWebProgress
,
659 nsIRequest
* aRequest
, int32_t aCurSelfProgress
,
660 int32_t aMaxSelfProgress
,
661 int32_t aCurTotalProgress
,
662 int32_t aMaxTotalProgress
) override
{
665 NS_IMETHOD
OnLocationChange(nsIWebProgress
* aWebProgress
,
666 nsIRequest
* aRequest
, nsIURI
* aLocation
,
667 uint32_t aFlags
) override
{
670 NS_IMETHOD
OnSecurityChange(nsIWebProgress
* aWebProgress
,
671 nsIRequest
* aRequest
, uint32_t aState
) override
{
674 NS_IMETHOD
OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
675 nsIRequest
* aRequest
,
676 uint32_t aEvent
) override
{
681 ~PrintListenerAdapter() = default;
683 RefPtr
<PrintPromise::Private
> mPromise
;
686 NS_IMPL_ISUPPORTS(PrintListenerAdapter
, nsIWebProgressListener
)
689 already_AddRefed
<Promise
> CanonicalBrowsingContext::PrintJS(
690 nsIPrintSettings
* aPrintSettings
, ErrorResult
& aRv
) {
691 RefPtr
<Promise
> promise
= Promise::Create(GetIncumbentGlobal(), aRv
);
692 if (NS_WARN_IF(aRv
.Failed())) {
693 return promise
.forget();
696 Print(aPrintSettings
)
698 GetCurrentSerialEventTarget(), __func__
,
699 [promise
](bool) { promise
->MaybeResolveWithUndefined(); },
700 [promise
](nsresult aResult
) { promise
->MaybeReject(aResult
); });
701 return promise
.forget();
704 RefPtr
<PrintPromise
> CanonicalBrowsingContext::Print(
705 nsIPrintSettings
* aPrintSettings
) {
707 return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
, __func__
);
710 auto promise
= MakeRefPtr
<PrintPromise::Private
>(__func__
);
711 auto listener
= MakeRefPtr
<PrintListenerAdapter
>(promise
);
713 RefPtr
<nsGlobalWindowOuter
> outerWindow
=
714 nsGlobalWindowOuter::Cast(GetDOMWindow());
715 if (NS_WARN_IF(!outerWindow
)) {
716 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
721 outerWindow
->Print(aPrintSettings
,
722 /* aRemotePrintJob = */ nullptr, listener
,
723 /* aDocShellToCloneInto = */ nullptr,
724 nsGlobalWindowOuter::IsPreview::No
,
725 nsGlobalWindowOuter::IsForWindowDotPrint::No
,
726 /* aPrintPreviewCallback = */ nullptr, rv
);
728 promise
->Reject(rv
.StealNSResult(), __func__
);
733 auto* browserParent
= GetBrowserParent();
734 if (NS_WARN_IF(!browserParent
)) {
735 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
739 nsCOMPtr
<nsIPrintSettingsService
> printSettingsSvc
=
740 do_GetService("@mozilla.org/gfx/printsettings-service;1");
741 if (NS_WARN_IF(!printSettingsSvc
)) {
742 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
747 nsCOMPtr
<nsIPrintSettings
> printSettings
= aPrintSettings
;
748 if (!printSettings
) {
750 printSettingsSvc
->CreateNewPrintSettings(getter_AddRefs(printSettings
));
751 if (NS_WARN_IF(NS_FAILED(rv
))) {
752 promise
->Reject(rv
, __func__
);
757 embedding::PrintData printData
;
758 rv
= printSettingsSvc
->SerializeToPrintData(printSettings
, &printData
);
759 if (NS_WARN_IF(NS_FAILED(rv
))) {
760 promise
->Reject(rv
, __func__
);
764 layout::RemotePrintJobParent
* remotePrintJob
=
765 new layout::RemotePrintJobParent(printSettings
);
766 printData
.remotePrintJobParent() =
767 browserParent
->Manager()->SendPRemotePrintJobConstructor(remotePrintJob
);
770 remotePrintJob
->RegisterListener(listener
);
773 if (NS_WARN_IF(!browserParent
->SendPrint(this, printData
))) {
774 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
776 return promise
.forget();
780 void CanonicalBrowsingContext::CallOnAllTopDescendants(
781 const std::function
<mozilla::CallState(CanonicalBrowsingContext
*)>&
784 RefPtr
<CanonicalBrowsingContext
> parent
= GetParentCrossChromeBoundary();
785 MOZ_ASSERT(!parent
, "Should only call on top chrome BC");
788 nsTArray
<RefPtr
<BrowsingContextGroup
>> groups
;
789 BrowsingContextGroup::GetAllGroups(groups
);
790 for (auto& browsingContextGroup
: groups
) {
791 for (auto& bc
: browsingContextGroup
->Toplevels()) {
793 // Cannot be a descendent of myself so skip.
797 RefPtr
<CanonicalBrowsingContext
> top
=
798 bc
->Canonical()->TopCrossChromeBoundary();
800 if (aCallback(bc
->Canonical()) == CallState::Stop
) {
808 void CanonicalBrowsingContext::SessionHistoryCommit(
809 uint64_t aLoadId
, const nsID
& aChangeID
, uint32_t aLoadType
, bool aPersist
,
810 bool aCloneEntryChildren
, bool aChannelExpired
, uint32_t aCacheKey
) {
811 MOZ_LOG(gSHLog
, LogLevel::Verbose
,
812 ("CanonicalBrowsingContext::SessionHistoryCommit %p %" PRIu64
, this,
814 MOZ_ASSERT(aLoadId
!= UINT64_MAX
,
815 "Must not send special about:blank loadinfo to parent.");
816 for (size_t i
= 0; i
< mLoadingEntries
.Length(); ++i
) {
817 if (mLoadingEntries
[i
].mLoadId
== aLoadId
) {
818 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
820 SessionHistoryEntry::RemoveLoadId(aLoadId
);
821 mLoadingEntries
.RemoveElementAt(i
);
825 RefPtr
<SessionHistoryEntry
> newActiveEntry
= mLoadingEntries
[i
].mEntry
;
826 if (aCacheKey
!= 0) {
827 newActiveEntry
->SetCacheKey(aCacheKey
);
830 if (aChannelExpired
) {
831 newActiveEntry
->SharedInfo()->mExpired
= true;
834 bool loadFromSessionHistory
= !newActiveEntry
->ForInitialLoad();
835 newActiveEntry
->SetForInitialLoad(false);
836 SessionHistoryEntry::RemoveLoadId(aLoadId
);
837 mLoadingEntries
.RemoveElementAt(i
);
839 int32_t indexOfHistoryLoad
= -1;
840 if (loadFromSessionHistory
) {
841 nsCOMPtr
<nsISHEntry
> root
= nsSHistory::GetRootSHEntry(newActiveEntry
);
842 indexOfHistoryLoad
= shistory
->GetIndexOfEntry(root
);
843 if (indexOfHistoryLoad
< 0) {
844 // Entry has been removed from the session history.
849 CallerWillNotifyHistoryIndexAndLengthChanges
caller(shistory
);
851 // If there is a name in the new entry, clear the name of all contiguous
852 // entries. This is for https://html.spec.whatwg.org/#history-traversal
854 nsAutoString nameOfNewEntry
;
855 newActiveEntry
->GetName(nameOfNewEntry
);
856 if (!nameOfNewEntry
.IsEmpty()) {
857 nsSHistory::WalkContiguousEntries(
859 [](nsISHEntry
* aEntry
) { aEntry
->SetName(EmptyString()); });
862 bool addEntry
= ShouldUpdateSessionHistory(aLoadType
);
864 if (mActiveEntry
&& !mActiveEntry
->GetFrameLoader()) {
865 bool sharesDocument
= true;
866 mActiveEntry
->SharesDocumentWith(newActiveEntry
, &sharesDocument
);
867 if (!sharesDocument
) {
868 // If the old page won't be in the bfcache,
869 // clear the dynamic entries.
870 RemoveDynEntriesFromActiveSessionHistoryEntry();
874 if (LOAD_TYPE_HAS_FLAGS(aLoadType
,
875 nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY
)) {
876 // Replace the current entry with the new entry.
877 int32_t index
= shistory
->GetIndexForReplace();
879 // If we're trying to replace an inexistant shistory entry then we
880 // should append instead.
881 addEntry
= index
< 0;
883 shistory
->ReplaceEntry(index
, newActiveEntry
);
885 mActiveEntry
= newActiveEntry
;
886 } else if (LOAD_TYPE_HAS_FLAGS(
887 aLoadType
, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH
) &&
888 !ShouldAddEntryForRefresh(newActiveEntry
) && mActiveEntry
) {
890 mActiveEntry
->ReplaceWith(*newActiveEntry
);
892 mActiveEntry
= newActiveEntry
;
895 if (loadFromSessionHistory
) {
896 // XXX Synchronize browsing context tree and session history tree?
897 shistory
->InternalSetRequestedIndex(indexOfHistoryLoad
);
898 shistory
->UpdateIndex();
901 mActiveEntry
->SetWireframe(Nothing());
903 } else if (addEntry
) {
904 shistory
->AddEntry(mActiveEntry
, aPersist
);
905 shistory
->InternalSetRequestedIndex(-1);
908 // FIXME The old implementations adds it to the parent's mLSHE if there
909 // is one, need to figure out if that makes sense here (peterv
910 // doesn't think it would).
911 if (loadFromSessionHistory
) {
913 // mActiveEntry is null if we're loading iframes from session
914 // history while also parent page is loading from session history.
915 // In that case there isn't anything to sync.
916 mActiveEntry
->SyncTreesForSubframeNavigation(newActiveEntry
, Top(),
919 mActiveEntry
= newActiveEntry
;
921 shistory
->InternalSetRequestedIndex(indexOfHistoryLoad
);
922 // FIXME UpdateIndex() here may update index too early (but even the
923 // old implementation seems to have similar issues).
924 shistory
->UpdateIndex();
925 } else if (addEntry
) {
927 if (LOAD_TYPE_HAS_FLAGS(
928 aLoadType
, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY
) ||
929 (LOAD_TYPE_HAS_FLAGS(aLoadType
,
930 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH
) &&
931 !ShouldAddEntryForRefresh(newActiveEntry
))) {
932 // FIXME We need to make sure that when we create the info we
933 // make a copy of the shared state.
934 mActiveEntry
->ReplaceWith(*newActiveEntry
);
936 // AddChildSHEntryHelper does update the index of the session
938 shistory
->AddChildSHEntryHelper(mActiveEntry
, newActiveEntry
,
939 Top(), aCloneEntryChildren
);
940 mActiveEntry
= newActiveEntry
;
943 SessionHistoryEntry
* parentEntry
= GetParent()->mActiveEntry
;
944 // XXX What should happen if parent doesn't have mActiveEntry?
945 // Or can that even happen ever?
947 mActiveEntry
= newActiveEntry
;
948 // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite
949 // right, but aUseRemoteSubframes should be going away.
950 parentEntry
->AddChild(
952 CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
956 shistory
->InternalSetRequestedIndex(-1);
960 ResetSHEntryHasUserInteractionCache();
962 HistoryCommitIndexAndLength(aChangeID
, caller
);
964 shistory
->LogHistory();
968 // XXX Should the loading entries before [i] be removed?
970 // FIXME Should we throw an error if we don't find an entry for
971 // aSessionHistoryEntryId?
974 static already_AddRefed
<nsDocShellLoadState
> CreateLoadInfo(
975 SessionHistoryEntry
* aEntry
) {
976 const SessionHistoryInfo
& info
= aEntry
->Info();
977 RefPtr
<nsDocShellLoadState
> loadState(new nsDocShellLoadState(info
.GetURI()));
978 info
.FillLoadInfo(*loadState
);
979 UniquePtr
<LoadingSessionHistoryInfo
> loadingInfo
;
980 loadingInfo
= MakeUnique
<LoadingSessionHistoryInfo
>(aEntry
);
981 loadState
->SetLoadingSessionHistoryInfo(std::move(loadingInfo
));
983 return loadState
.forget();
986 void CanonicalBrowsingContext::NotifyOnHistoryReload(
987 bool aForceReload
, bool& aCanReload
,
988 Maybe
<RefPtr
<nsDocShellLoadState
>>& aLoadState
,
989 Maybe
<bool>& aReloadActiveEntry
) {
990 MOZ_DIAGNOSTIC_ASSERT(!aLoadState
);
993 nsISHistory
* shistory
= GetSessionHistory();
994 NS_ENSURE_TRUE_VOID(shistory
);
996 shistory
->NotifyOnHistoryReload(&aCanReload
);
1002 aLoadState
.emplace(CreateLoadInfo(mActiveEntry
));
1003 aReloadActiveEntry
.emplace(true);
1005 shistory
->RemoveFrameEntries(mActiveEntry
);
1007 } else if (!mLoadingEntries
.IsEmpty()) {
1008 const LoadingSessionHistoryEntry
& loadingEntry
=
1009 mLoadingEntries
.LastElement();
1010 aLoadState
.emplace(CreateLoadInfo(loadingEntry
.mEntry
));
1011 aReloadActiveEntry
.emplace(false);
1013 SessionHistoryEntry
* entry
=
1014 SessionHistoryEntry::GetByLoadId(loadingEntry
.mLoadId
);
1016 shistory
->RemoveFrameEntries(entry
);
1022 // Use 0 as the offset, since aLoadState will be be used for reload.
1023 aLoadState
.ref()->SetLoadIsFromSessionHistory(0,
1024 aReloadActiveEntry
.value());
1026 // If we don't have an active entry and we don't have a loading entry then
1027 // the nsDocShell will create a load state based on its document.
1030 void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
1031 const Maybe
<nsPoint
>& aPreviousScrollPos
, SessionHistoryInfo
* aInfo
,
1032 uint32_t aLoadType
, uint32_t aUpdatedCacheKey
, const nsID
& aChangeID
) {
1033 nsISHistory
* shistory
= GetSessionHistory();
1037 CallerWillNotifyHistoryIndexAndLengthChanges
caller(shistory
);
1039 RefPtr
<SessionHistoryEntry
> oldActiveEntry
= mActiveEntry
;
1040 if (aPreviousScrollPos
.isSome() && oldActiveEntry
) {
1041 oldActiveEntry
->SetScrollPosition(aPreviousScrollPos
.ref().x
,
1042 aPreviousScrollPos
.ref().y
);
1044 mActiveEntry
= new SessionHistoryEntry(aInfo
);
1045 mActiveEntry
->SetDocshellID(GetHistoryID());
1046 mActiveEntry
->AdoptBFCacheEntry(oldActiveEntry
);
1047 if (aUpdatedCacheKey
!= 0) {
1048 mActiveEntry
->SharedInfo()->mCacheKey
= aUpdatedCacheKey
;
1052 Maybe
<int32_t> previousEntryIndex
, loadedEntryIndex
;
1053 shistory
->AddToRootSessionHistory(
1054 true, oldActiveEntry
, this, mActiveEntry
, aLoadType
,
1055 nsDocShell::ShouldAddToSessionHistory(aInfo
->GetURI(), nullptr),
1056 &previousEntryIndex
, &loadedEntryIndex
);
1058 if (oldActiveEntry
) {
1059 shistory
->AddChildSHEntryHelper(oldActiveEntry
, mActiveEntry
, Top(),
1061 } else if (GetParent() && GetParent()->mActiveEntry
) {
1062 GetParent()->mActiveEntry
->AddChild(
1063 mActiveEntry
, CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
1064 UseRemoteSubframes());
1068 ResetSHEntryHasUserInteractionCache();
1070 shistory
->InternalSetRequestedIndex(-1);
1072 // FIXME Need to do the equivalent of EvictContentViewersOrReplaceEntry.
1073 HistoryCommitIndexAndLength(aChangeID
, caller
);
1075 static_cast<nsSHistory
*>(shistory
)->LogHistory();
1078 void CanonicalBrowsingContext::ReplaceActiveSessionHistoryEntry(
1079 SessionHistoryInfo
* aInfo
) {
1080 if (!mActiveEntry
) {
1084 mActiveEntry
->SetInfo(aInfo
);
1085 // Notify children of the update
1086 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
1088 shistory
->NotifyOnHistoryReplaceEntry();
1089 shistory
->UpdateRootBrowsingContextState();
1092 ResetSHEntryHasUserInteractionCache();
1095 mActiveEntry
->SetWireframe(Nothing());
1098 // FIXME Need to do the equivalent of EvictContentViewersOrReplaceEntry.
1101 void CanonicalBrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
1102 nsISHistory
* shistory
= GetSessionHistory();
1103 // In theory shistory can be null here if the method is called right after
1104 // CanonicalBrowsingContext::ReplacedBy call.
1105 NS_ENSURE_TRUE_VOID(shistory
);
1106 nsCOMPtr
<nsISHEntry
> root
= nsSHistory::GetRootSHEntry(mActiveEntry
);
1107 shistory
->RemoveDynEntries(shistory
->GetIndexOfEntry(root
), mActiveEntry
);
1110 void CanonicalBrowsingContext::RemoveFromSessionHistory(const nsID
& aChangeID
) {
1111 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
1113 CallerWillNotifyHistoryIndexAndLengthChanges
caller(shistory
);
1114 nsCOMPtr
<nsISHEntry
> root
= nsSHistory::GetRootSHEntry(mActiveEntry
);
1116 AutoTArray
<nsID
, 16> ids({GetHistoryID()});
1117 shistory
->RemoveEntries(ids
, shistory
->GetIndexOfEntry(root
), &didRemove
);
1119 RefPtr
<BrowsingContext
> rootBC
= shistory
->GetBrowsingContext();
1121 if (!rootBC
->IsInProcess()) {
1122 if (ContentParent
* cp
= rootBC
->Canonical()->GetContentParent()) {
1123 Unused
<< cp
->SendDispatchLocationChangeEvent(rootBC
);
1125 } else if (rootBC
->GetDocShell()) {
1126 rootBC
->GetDocShell()->DispatchLocationChangeEvent();
1130 HistoryCommitIndexAndLength(aChangeID
, caller
);
1134 void CanonicalBrowsingContext::HistoryGo(
1135 int32_t aOffset
, uint64_t aHistoryEpoch
, bool aRequireUserInteraction
,
1136 bool aUserActivation
, Maybe
<ContentParentId
> aContentId
,
1137 std::function
<void(int32_t&&)>&& aResolver
) {
1138 if (aRequireUserInteraction
&& aOffset
!= -1 && aOffset
!= 1) {
1140 "aRequireUserInteraction may only be used with an offset of -1 or 1");
1144 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
1149 CheckedInt
<int32_t> index
= shistory
->GetRequestedIndex() >= 0
1150 ? shistory
->GetRequestedIndex()
1151 : shistory
->Index();
1152 MOZ_LOG(gSHLog
, LogLevel::Debug
,
1153 ("HistoryGo(%d->%d) epoch %" PRIu64
"/id %" PRIu64
, aOffset
,
1154 (index
+ aOffset
).value(), aHistoryEpoch
,
1155 (uint64_t)(aContentId
.isSome() ? aContentId
.value() : 0)));
1159 if (!index
.isValid()) {
1160 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Invalid index"));
1164 // Check for user interaction if desired, except for the first and last
1165 // history entries. We compare with >= to account for the case where
1166 // aOffset >= length.
1167 if (!aRequireUserInteraction
|| index
.value() >= shistory
->Length() - 1 ||
1168 index
.value() <= 0) {
1171 if (shistory
->HasUserInteractionAtIndex(index
.value())) {
1176 // Implement aborting additional history navigations from within the same
1177 // event spin of the content process.
1180 bool sameEpoch
= false;
1181 Maybe
<ContentParentId
> id
;
1182 shistory
->GetEpoch(epoch
, id
);
1184 if (aContentId
== id
&& epoch
>= aHistoryEpoch
) {
1186 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Same epoch/id"));
1188 // Don't update the epoch until we know if the target index is valid
1190 // GoToIndex checks that index is >= 0 and < length.
1191 nsTArray
<nsSHistory::LoadEntryResult
> loadResults
;
1192 nsresult rv
= shistory
->GotoIndex(index
.value(), loadResults
, sameEpoch
,
1193 aOffset
== 0, aUserActivation
);
1194 if (NS_FAILED(rv
)) {
1195 MOZ_LOG(gSHLog
, LogLevel::Debug
,
1196 ("Dropping HistoryGo - bad index or same epoch (not in same doc)"));
1199 if (epoch
< aHistoryEpoch
|| aContentId
!= id
) {
1200 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Set epoch"));
1201 shistory
->SetEpoch(aHistoryEpoch
, aContentId
);
1203 aResolver(shistory
->GetRequestedIndex());
1204 nsSHistory::LoadURIs(loadResults
);
1207 JSObject
* CanonicalBrowsingContext::WrapObject(
1208 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
1209 return CanonicalBrowsingContext_Binding::Wrap(aCx
, this, aGivenProto
);
1212 void CanonicalBrowsingContext::DispatchWheelZoomChange(bool aIncrease
) {
1213 Element
* element
= Top()->GetEmbedderElement();
1218 auto event
= aIncrease
? u
"DoZoomEnlargeBy10"_ns
: u
"DoZoomReduceBy10"_ns
;
1219 auto dispatcher
= MakeRefPtr
<AsyncEventDispatcher
>(
1220 element
, event
, CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
1221 dispatcher
->PostDOMEvent();
1224 void CanonicalBrowsingContext::CanonicalDiscard() {
1225 if (mTabMediaController
) {
1226 mTabMediaController
->Shutdown();
1227 mTabMediaController
= nullptr;
1231 mCurrentLoad
->Cancel(NS_BINDING_ABORTED
);
1235 RefPtr
<BrowsingContextWebProgress
> progress
= mWebProgress
;
1236 progress
->ContextDiscarded();
1240 BackgroundSessionStorageManager::RemoveManager(Id());
1243 CancelSessionStoreUpdate();
1245 if (UsePrivateBrowsing() && EverAttached() && IsContent()) {
1246 DecreasePrivateCount();
1250 void CanonicalBrowsingContext::CanonicalAttach() {
1251 if (UsePrivateBrowsing() && IsContent()) {
1252 IncreasePrivateCount();
1256 void CanonicalBrowsingContext::AddPendingDiscard() {
1257 MOZ_ASSERT(!mFullyDiscarded
);
1261 void CanonicalBrowsingContext::RemovePendingDiscard() {
1263 if (!mPendingDiscards
) {
1264 mFullyDiscarded
= true;
1265 auto listeners
= std::move(mFullyDiscardedListeners
);
1266 for (const auto& listener
: listeners
) {
1272 void CanonicalBrowsingContext::AddFinalDiscardListener(
1273 std::function
<void(uint64_t)>&& aListener
) {
1274 if (mFullyDiscarded
) {
1278 mFullyDiscardedListeners
.AppendElement(std::move(aListener
));
1281 net::EarlyHintsService
* CanonicalBrowsingContext::GetEarlyHintsService() {
1282 return &mEarlyHintsService
;
1285 void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
1286 bool aPrivateBrowsing
) {
1287 if (IsDiscarded() || !EverAttached() || IsChrome()) {
1291 MOZ_DIAGNOSTIC_ASSERT(aPrivateBrowsing
== UsePrivateBrowsing());
1292 if (aPrivateBrowsing
) {
1293 IncreasePrivateCount();
1295 DecreasePrivateCount();
1299 void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
1300 WindowContext
* windowContext
= GetCurrentWindowContext();
1301 if (!windowContext
) {
1305 // As this function would only be called when user click the play icon on the
1306 // tab bar. That's clear user intent to play, so gesture activate the window
1307 // context so that the block-autoplay logic allows the media to autoplay.
1308 windowContext
->NotifyUserGestureActivation();
1309 AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64
,
1311 StartDelayedAutoplayMediaComponents();
1312 // Notfiy all content browsing contexts which are related with the canonical
1313 // browsing content tree to start delayed autoplay media.
1315 Group()->EachParent([&](ContentParent
* aParent
) {
1316 Unused
<< aParent
->SendStartDelayedAutoplayMediaComponents(this);
1320 void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted
,
1322 MOZ_ASSERT(!GetParent(),
1323 "Notify media mute change on non top-level context!");
1324 SetMuted(aMuted
, aRv
);
1327 uint32_t CanonicalBrowsingContext::CountSiteOrigins(
1328 GlobalObject
& aGlobal
,
1329 const Sequence
<OwningNonNull
<BrowsingContext
>>& aRoots
) {
1330 nsTHashSet
<nsCString
> uniqueSiteOrigins
;
1332 for (const auto& root
: aRoots
) {
1333 root
->PreOrderWalk([&](BrowsingContext
* aContext
) {
1334 WindowGlobalParent
* windowGlobalParent
=
1335 aContext
->Canonical()->GetCurrentWindowGlobal();
1336 if (windowGlobalParent
) {
1337 nsIPrincipal
* documentPrincipal
=
1338 windowGlobalParent
->DocumentPrincipal();
1340 bool isContentPrincipal
= documentPrincipal
->GetIsContentPrincipal();
1341 if (isContentPrincipal
) {
1342 nsCString siteOrigin
;
1343 documentPrincipal
->GetSiteOrigin(siteOrigin
);
1344 uniqueSiteOrigins
.Insert(siteOrigin
);
1350 return uniqueSiteOrigins
.Count();
1353 void CanonicalBrowsingContext::UpdateMediaControlAction(
1354 const MediaControlAction
& aAction
) {
1355 if (IsDiscarded()) {
1358 ContentMediaControlKeyHandler::HandleMediaControlAction(this, aAction
);
1359 Group()->EachParent([&](ContentParent
* aParent
) {
1360 Unused
<< aParent
->SendUpdateMediaControlAction(this, aAction
);
1364 void CanonicalBrowsingContext::LoadURI(const nsAString
& aURI
,
1365 const LoadURIOptions
& aOptions
,
1366 ErrorResult
& aError
) {
1367 RefPtr
<nsDocShellLoadState
> loadState
;
1368 nsresult rv
= nsDocShellLoadState::CreateFromLoadURIOptions(
1369 this, aURI
, aOptions
, getter_AddRefs(loadState
));
1371 if (rv
== NS_ERROR_MALFORMED_URI
) {
1372 DisplayLoadError(aURI
);
1376 if (NS_FAILED(rv
)) {
1381 LoadURI(loadState
, true);
1384 void CanonicalBrowsingContext::GoBack(
1385 const Optional
<int32_t>& aCancelContentJSEpoch
,
1386 bool aRequireUserInteraction
, bool aUserActivation
) {
1387 if (IsDiscarded()) {
1391 // Stop any known network loads if necessary.
1393 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
);
1396 if (nsDocShell
* docShell
= nsDocShell::Cast(GetDocShell())) {
1397 if (aCancelContentJSEpoch
.WasPassed()) {
1398 docShell
->SetCancelContentJSEpoch(aCancelContentJSEpoch
.Value());
1400 docShell
->GoBack(aRequireUserInteraction
, aUserActivation
);
1401 } else if (ContentParent
* cp
= GetContentParent()) {
1402 Maybe
<int32_t> cancelContentJSEpoch
;
1403 if (aCancelContentJSEpoch
.WasPassed()) {
1404 cancelContentJSEpoch
= Some(aCancelContentJSEpoch
.Value());
1406 Unused
<< cp
->SendGoBack(this, cancelContentJSEpoch
,
1407 aRequireUserInteraction
, aUserActivation
);
1410 void CanonicalBrowsingContext::GoForward(
1411 const Optional
<int32_t>& aCancelContentJSEpoch
,
1412 bool aRequireUserInteraction
, bool aUserActivation
) {
1413 if (IsDiscarded()) {
1417 // Stop any known network loads if necessary.
1419 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
);
1422 if (auto* docShell
= nsDocShell::Cast(GetDocShell())) {
1423 if (aCancelContentJSEpoch
.WasPassed()) {
1424 docShell
->SetCancelContentJSEpoch(aCancelContentJSEpoch
.Value());
1426 docShell
->GoForward(aRequireUserInteraction
, aUserActivation
);
1427 } else if (ContentParent
* cp
= GetContentParent()) {
1428 Maybe
<int32_t> cancelContentJSEpoch
;
1429 if (aCancelContentJSEpoch
.WasPassed()) {
1430 cancelContentJSEpoch
.emplace(aCancelContentJSEpoch
.Value());
1432 Unused
<< cp
->SendGoForward(this, cancelContentJSEpoch
,
1433 aRequireUserInteraction
, aUserActivation
);
1436 void CanonicalBrowsingContext::GoToIndex(
1437 int32_t aIndex
, const Optional
<int32_t>& aCancelContentJSEpoch
,
1438 bool aUserActivation
) {
1439 if (IsDiscarded()) {
1443 // Stop any known network loads if necessary.
1445 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
);
1448 if (auto* docShell
= nsDocShell::Cast(GetDocShell())) {
1449 if (aCancelContentJSEpoch
.WasPassed()) {
1450 docShell
->SetCancelContentJSEpoch(aCancelContentJSEpoch
.Value());
1452 docShell
->GotoIndex(aIndex
, aUserActivation
);
1453 } else if (ContentParent
* cp
= GetContentParent()) {
1454 Maybe
<int32_t> cancelContentJSEpoch
;
1455 if (aCancelContentJSEpoch
.WasPassed()) {
1456 cancelContentJSEpoch
.emplace(aCancelContentJSEpoch
.Value());
1458 Unused
<< cp
->SendGoToIndex(this, aIndex
, cancelContentJSEpoch
,
1462 void CanonicalBrowsingContext::Reload(uint32_t aReloadFlags
) {
1463 if (IsDiscarded()) {
1467 // Stop any known network loads if necessary.
1469 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
);
1472 if (auto* docShell
= nsDocShell::Cast(GetDocShell())) {
1473 docShell
->Reload(aReloadFlags
);
1474 } else if (ContentParent
* cp
= GetContentParent()) {
1475 Unused
<< cp
->SendReload(this, aReloadFlags
);
1479 void CanonicalBrowsingContext::Stop(uint32_t aStopFlags
) {
1480 if (IsDiscarded()) {
1484 // Stop any known network loads if necessary.
1485 if (mCurrentLoad
&& (aStopFlags
& nsIWebNavigation::STOP_NETWORK
)) {
1486 mCurrentLoad
->Cancel(NS_BINDING_ABORTED
);
1489 // Ask the docshell to stop to handle loads that haven't
1490 // yet reached here, as well as non-network activity.
1491 if (auto* docShell
= nsDocShell::Cast(GetDocShell())) {
1492 docShell
->Stop(aStopFlags
);
1493 } else if (ContentParent
* cp
= GetContentParent()) {
1494 Unused
<< cp
->SendStopLoad(this, aStopFlags
);
1498 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() {
1503 if (mContentParent
) {
1504 // If our new content process is still unloading from a previous process
1505 // switch, wait for that unload to complete before continuing.
1506 auto found
= mTarget
->FindUnloadingHost(mContentParent
->ChildID());
1507 if (found
!= mTarget
->mUnloadingHosts
.end()) {
1508 found
->mCallbacks
.AppendElement(
1509 [self
= RefPtr
{this}]() { self
->ProcessReady(); });
1517 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() {
1522 MOZ_ASSERT(!mProcessReady
);
1523 mProcessReady
= true;
1527 void CanonicalBrowsingContext::PendingRemotenessChange::MaybeFinish() {
1532 if (!mProcessReady
|| mWaitingForPrepareToChange
) {
1536 // If this BrowsingContext is embedded within the parent process, perform the
1537 // process switch directly.
1538 nsresult rv
= mTarget
->IsTopContent() ? FinishTopContent() : FinishSubframe();
1539 if (NS_FAILED(rv
)) {
1540 NS_WARNING("Error finishing PendingRemotenessChange!");
1547 // Logic for finishing a toplevel process change embedded within the parent
1548 // process. Due to frontend integration the logic differs substantially from
1549 // subframe process switches, and is handled separately.
1550 nsresult
CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
1551 MOZ_DIAGNOSTIC_ASSERT(mTarget
->IsTop(),
1552 "We shouldn't be trying to change the remoteness of "
1553 "non-remote iframes");
1555 // Abort if our ContentParent died while process switching.
1556 if (mContentParent
&& NS_WARN_IF(mContentParent
->IsDead())) {
1557 return NS_ERROR_FAILURE
;
1560 // While process switching, we need to check if any of our ancestors are
1561 // discarded or no longer current, in which case the process switch needs to
1563 RefPtr
<CanonicalBrowsingContext
> target(mTarget
);
1564 if (target
->IsDiscarded() || !target
->AncestorsAreCurrent()) {
1565 return NS_ERROR_FAILURE
;
1568 Element
* browserElement
= target
->GetEmbedderElement();
1569 if (!browserElement
) {
1570 return NS_ERROR_FAILURE
;
1573 nsCOMPtr
<nsIBrowser
> browser
= browserElement
->AsBrowser();
1575 return NS_ERROR_FAILURE
;
1578 RefPtr
<nsFrameLoaderOwner
> frameLoaderOwner
= do_QueryObject(browserElement
);
1579 MOZ_RELEASE_ASSERT(frameLoaderOwner
,
1580 "embedder browser must be nsFrameLoaderOwner");
1582 // If we're process switching a browsing context in private browsing
1583 // mode we might decrease the private browsing count to '0', which
1584 // would make us fire "last-pb-context-exited" and drop the private
1585 // session. To prevent that we artificially increment the number of
1586 // private browsing contexts with '1' until the process switch is done.
1587 bool usePrivateBrowsing
= mTarget
->UsePrivateBrowsing();
1588 if (usePrivateBrowsing
) {
1589 IncreasePrivateCount();
1592 auto restorePrivateCount
= MakeScopeExit([usePrivateBrowsing
]() {
1593 if (usePrivateBrowsing
) {
1594 DecreasePrivateCount();
1598 // Tell frontend code that this browser element is about to change process.
1599 nsresult rv
= browser
->BeforeChangeRemoteness();
1600 if (NS_FAILED(rv
)) {
1604 // Some frontend code checks the value of the `remote` attribute on the
1605 // browser to determine if it is remote, so update the value.
1606 browserElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::remote
,
1607 mContentParent
? u
"true"_ns
: u
"false"_ns
,
1610 // The process has been created, hand off to nsFrameLoaderOwner to finish
1611 // the process switch.
1613 frameLoaderOwner
->ChangeRemotenessToProcess(mContentParent
, mOptions
,
1614 mSpecificGroup
, error
);
1615 if (error
.Failed()) {
1616 return error
.StealNSResult();
1619 // Tell frontend the load is done.
1620 bool loadResumed
= false;
1621 rv
= browser
->FinishChangeRemoteness(mPendingSwitchId
, &loadResumed
);
1622 if (NS_WARN_IF(NS_FAILED(rv
))) {
1626 // We did it! The process switch is complete.
1627 RefPtr
<nsFrameLoader
> frameLoader
= frameLoaderOwner
->GetFrameLoader();
1628 RefPtr
<BrowserParent
> newBrowser
= frameLoader
->GetBrowserParent();
1630 if (mContentParent
) {
1631 // Failed to create the BrowserParent somehow! Abort the process switch
1633 return NS_ERROR_UNEXPECTED
;
1637 RefPtr
<nsDocShell
> newDocShell
= frameLoader
->GetDocShell(error
);
1638 if (error
.Failed()) {
1639 return error
.StealNSResult();
1642 rv
= newDocShell
->ResumeRedirectedLoad(mPendingSwitchId
,
1643 /* aHistoryIndex */ -1);
1644 if (NS_FAILED(rv
)) {
1648 } else if (!loadResumed
) {
1649 newBrowser
->ResumeLoad(mPendingSwitchId
);
1652 mPromise
->Resolve(newBrowser
, __func__
);
1656 nsresult
CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
1657 MOZ_DIAGNOSTIC_ASSERT(!mOptions
.mReplaceBrowsingContext
,
1658 "Cannot replace BC for subframe");
1659 MOZ_DIAGNOSTIC_ASSERT(!mTarget
->IsTop());
1661 // While process switching, we need to check if any of our ancestors are
1662 // discarded or no longer current, in which case the process switch needs to
1664 RefPtr
<CanonicalBrowsingContext
> target(mTarget
);
1665 if (target
->IsDiscarded() || !target
->AncestorsAreCurrent()) {
1666 return NS_ERROR_FAILURE
;
1669 if (NS_WARN_IF(!mContentParent
)) {
1670 return NS_ERROR_FAILURE
;
1673 RefPtr
<WindowGlobalParent
> embedderWindow
= target
->GetParentWindowContext();
1674 if (NS_WARN_IF(!embedderWindow
) || NS_WARN_IF(!embedderWindow
->CanSend())) {
1675 return NS_ERROR_FAILURE
;
1678 RefPtr
<BrowserParent
> embedderBrowser
= embedderWindow
->GetBrowserParent();
1679 if (NS_WARN_IF(!embedderBrowser
)) {
1680 return NS_ERROR_FAILURE
;
1683 // If we're creating a new remote browser, and the host process is already
1684 // dead, abort the process switch.
1685 if (mContentParent
!= embedderBrowser
->Manager() &&
1686 NS_WARN_IF(mContentParent
->IsDead())) {
1687 return NS_ERROR_FAILURE
;
1690 RefPtr
<BrowserParent
> oldBrowser
= target
->GetBrowserParent();
1691 target
->SetCurrentBrowserParent(nullptr);
1693 // If we were in a remote frame, trigger unloading of the remote window. The
1694 // previous BrowserParent is registered in `mUnloadingHosts` and will only be
1695 // cleared when the BrowserParent is fully destroyed.
1696 bool wasRemote
= oldBrowser
&& oldBrowser
->GetBrowsingContext() == target
;
1698 MOZ_DIAGNOSTIC_ASSERT(oldBrowser
!= embedderBrowser
);
1699 MOZ_DIAGNOSTIC_ASSERT(oldBrowser
->IsDestroyed() ||
1700 oldBrowser
->GetBrowserBridgeParent());
1702 // `oldBrowser` will clear the `UnloadingHost` status once the actor has
1704 if (oldBrowser
->CanSend()) {
1705 target
->StartUnloadingHost(oldBrowser
->Manager()->ChildID());
1706 Unused
<< oldBrowser
->SendWillChangeProcess();
1707 oldBrowser
->Destroy();
1711 // Update which process is considered the current owner
1712 target
->SetOwnerProcessId(mContentParent
->ChildID());
1714 // If we're switching from remote to local, we don't need to create a
1715 // BrowserBridge, and can instead perform the switch directly.
1716 if (mContentParent
== embedderBrowser
->Manager()) {
1717 MOZ_DIAGNOSTIC_ASSERT(
1719 "We always have a PendingSwitchId, except for print-preview loads, "
1720 "which will never perform a process-switch to being in-process with "
1722 MOZ_DIAGNOSTIC_ASSERT(wasRemote
,
1723 "Attempt to process-switch from local to local?");
1725 target
->SetCurrentBrowserParent(embedderBrowser
);
1726 Unused
<< embedderWindow
->SendMakeFrameLocal(target
, mPendingSwitchId
);
1727 mPromise
->Resolve(embedderBrowser
, __func__
);
1731 // The BrowsingContext will be remote, either as an already-remote frame
1732 // changing processes, or as a local frame becoming remote. Construct a new
1733 // BrowserBridgeParent to host the remote content.
1734 target
->SetCurrentBrowserParent(nullptr);
1736 MOZ_DIAGNOSTIC_ASSERT(target
->UseRemoteTabs() && target
->UseRemoteSubframes(),
1737 "Not supported without fission");
1738 uint32_t chromeFlags
= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW
|
1739 nsIWebBrowserChrome::CHROME_FISSION_WINDOW
;
1740 if (target
->UsePrivateBrowsing()) {
1741 chromeFlags
|= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW
;
1744 nsCOMPtr
<nsIPrincipal
> initialPrincipal
=
1745 NullPrincipal::CreateWithInheritedAttributes(
1746 target
->OriginAttributesRef(),
1747 /* isFirstParty */ false);
1748 WindowGlobalInit windowInit
=
1749 WindowGlobalActor::AboutBlankInitializer(target
, initialPrincipal
);
1751 // Create and initialize our new BrowserBridgeParent.
1752 TabId
tabId(nsContentUtils::GenerateTabId());
1753 RefPtr
<BrowserBridgeParent
> bridge
= new BrowserBridgeParent();
1754 nsresult rv
= bridge
->InitWithProcess(embedderBrowser
, mContentParent
,
1755 windowInit
, chromeFlags
, tabId
);
1756 if (NS_WARN_IF(NS_FAILED(rv
))) {
1757 // If we've already destroyed our previous document, make a best-effort
1758 // attempt to recover from this failure and show the crashed tab UI. We only
1759 // do this in the previously-remote case, as previously in-process frames
1760 // will have their navigation cancelled, and will remain visible.
1762 target
->ShowSubframeCrashedUI(oldBrowser
->GetBrowserBridgeParent());
1767 // Tell the embedder process a remoteness change is in-process. When this is
1768 // acknowledged, reset the in-flight ID if it used to be an in-process load.
1769 RefPtr
<BrowserParent
> newBrowser
= bridge
->GetBrowserParent();
1771 // If we weren't remote, mark our embedder window browser as unloading until
1772 // our embedder process has acked our MakeFrameRemote message.
1773 Maybe
<uint64_t> clearChildID
;
1775 clearChildID
= Some(embedderBrowser
->Manager()->ChildID());
1776 target
->StartUnloadingHost(*clearChildID
);
1778 auto callback
= [target
, clearChildID
](auto&&) {
1780 target
->ClearUnloadingHost(*clearChildID
);
1784 ManagedEndpoint
<PBrowserBridgeChild
> endpoint
=
1785 embedderBrowser
->OpenPBrowserBridgeEndpoint(bridge
);
1786 MOZ_DIAGNOSTIC_ASSERT(endpoint
.IsValid());
1787 embedderWindow
->SendMakeFrameRemote(target
, std::move(endpoint
), tabId
,
1788 newBrowser
->GetLayersId(), callback
,
1792 // Resume the pending load in our new process.
1793 if (mPendingSwitchId
) {
1794 newBrowser
->ResumeLoad(mPendingSwitchId
);
1797 // We did it! The process switch is complete.
1798 mPromise
->Resolve(newBrowser
, __func__
);
1802 void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv
) {
1807 mPromise
->Reject(aRv
, __func__
);
1811 void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
1812 // Make sure we don't die while we're doing cleanup.
1813 RefPtr
<PendingRemotenessChange
> kungFuDeathGrip(this);
1815 MOZ_DIAGNOSTIC_ASSERT(mTarget
->mPendingRemotenessChange
== this);
1816 mTarget
->mPendingRemotenessChange
= nullptr;
1819 // When this PendingRemotenessChange was created, it was given a
1820 // `mContentParent`.
1821 if (mContentParent
) {
1822 mContentParent
->RemoveKeepAlive();
1823 mContentParent
= nullptr;
1826 // If we were given a specific group, stop keeping that group alive manually.
1827 if (mSpecificGroup
) {
1828 mSpecificGroup
->RemoveKeepAlive();
1829 mSpecificGroup
= nullptr;
1836 CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange(
1837 CanonicalBrowsingContext
* aTarget
, RemotenessPromise::Private
* aPromise
,
1838 uint64_t aPendingSwitchId
, const NavigationIsolationOptions
& aOptions
)
1841 mPendingSwitchId(aPendingSwitchId
),
1842 mOptions(aOptions
) {}
1844 CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
1845 MOZ_ASSERT(!mPromise
&& !mTarget
&& !mContentParent
&& !mSpecificGroup
,
1846 "should've already been Cancel() or Complete()-ed");
1849 BrowserParent
* CanonicalBrowsingContext::GetBrowserParent() const {
1850 return mCurrentBrowserParent
;
1853 void CanonicalBrowsingContext::SetCurrentBrowserParent(
1854 BrowserParent
* aBrowserParent
) {
1855 MOZ_DIAGNOSTIC_ASSERT(!mCurrentBrowserParent
|| !aBrowserParent
,
1856 "BrowsingContext already has a current BrowserParent!");
1857 MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent
, aBrowserParent
->CanSend());
1858 MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent
,
1859 aBrowserParent
->Manager()->ChildID() == mProcessId
);
1861 // BrowserParent must either be directly for this BrowsingContext, or the
1862 // manager out our embedder WindowGlobal.
1863 MOZ_DIAGNOSTIC_ASSERT_IF(
1864 aBrowserParent
&& aBrowserParent
->GetBrowsingContext() != this,
1865 GetParentWindowContext() &&
1866 GetParentWindowContext()->Manager() == aBrowserParent
);
1868 mCurrentBrowserParent
= aBrowserParent
;
1871 RefPtr
<CanonicalBrowsingContext::RemotenessPromise
>
1872 CanonicalBrowsingContext::ChangeRemoteness(
1873 const NavigationIsolationOptions
& aOptions
, uint64_t aPendingSwitchId
) {
1874 MOZ_DIAGNOSTIC_ASSERT(IsContent(),
1875 "cannot change the process of chrome contexts");
1876 MOZ_DIAGNOSTIC_ASSERT(
1877 IsTop() == IsEmbeddedInProcess(0),
1878 "toplevel content must be embedded in the parent process");
1879 MOZ_DIAGNOSTIC_ASSERT(!aOptions
.mReplaceBrowsingContext
|| IsTop(),
1880 "Cannot replace BrowsingContext for subframes");
1881 MOZ_DIAGNOSTIC_ASSERT(
1882 aOptions
.mSpecificGroupId
== 0 || aOptions
.mReplaceBrowsingContext
,
1883 "Cannot specify group ID unless replacing BC");
1884 MOZ_DIAGNOSTIC_ASSERT(aPendingSwitchId
|| !IsTop(),
1885 "Should always have aPendingSwitchId for top-level "
1888 if (!AncestorsAreCurrent()) {
1889 NS_WARNING("An ancestor context is no longer current");
1890 return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
1893 // Ensure our embedder hasn't been destroyed already.
1894 RefPtr
<WindowGlobalParent
> embedderWindowGlobal
= GetEmbedderWindowGlobal();
1895 if (!embedderWindowGlobal
) {
1896 NS_WARNING("Non-embedded BrowsingContext");
1897 return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED
, __func__
);
1900 if (!embedderWindowGlobal
->CanSend()) {
1901 NS_WARNING("Embedder already been destroyed.");
1902 return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
, __func__
);
1905 if (aOptions
.mRemoteType
.IsEmpty() && (!IsTop() || !GetEmbedderElement())) {
1906 NS_WARNING("Cannot load non-remote subframes");
1907 return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
1910 // Cancel ongoing remoteness changes.
1911 if (mPendingRemotenessChange
) {
1912 mPendingRemotenessChange
->Cancel(NS_ERROR_ABORT
);
1913 MOZ_DIAGNOSTIC_ASSERT(!mPendingRemotenessChange
, "Should have cleared");
1916 auto promise
= MakeRefPtr
<RemotenessPromise::Private
>(__func__
);
1917 promise
->UseDirectTaskDispatch(__func__
);
1919 RefPtr
<PendingRemotenessChange
> change
=
1920 new PendingRemotenessChange(this, promise
, aPendingSwitchId
, aOptions
);
1921 mPendingRemotenessChange
= change
;
1923 // If a specific BrowsingContextGroup ID was specified for this load, make
1924 // sure to keep it alive until the process switch is completed.
1925 if (aOptions
.mSpecificGroupId
) {
1926 change
->mSpecificGroup
=
1927 BrowsingContextGroup::GetOrCreate(aOptions
.mSpecificGroupId
);
1928 change
->mSpecificGroup
->AddKeepAlive();
1931 // Call `prepareToChangeRemoteness` in parallel with starting a new process
1932 // for <browser> loads.
1933 if (IsTop() && GetEmbedderElement()) {
1934 nsCOMPtr
<nsIBrowser
> browser
= GetEmbedderElement()->AsBrowser();
1936 change
->Cancel(NS_ERROR_FAILURE
);
1937 return promise
.forget();
1940 RefPtr
<Promise
> blocker
;
1941 nsresult rv
= browser
->PrepareToChangeRemoteness(getter_AddRefs(blocker
));
1942 if (NS_FAILED(rv
)) {
1944 return promise
.forget();
1947 // Mark prepareToChange as unresolved, and wait for it to become resolved.
1948 if (blocker
&& blocker
->State() != Promise::PromiseState::Resolved
) {
1949 change
->mWaitingForPrepareToChange
= true;
1950 RefPtr
<DomPromiseListener
> listener
= new DomPromiseListener(
1951 [change
](JSContext
* aCx
, JS::HandleValue aValue
) {
1952 change
->mWaitingForPrepareToChange
= false;
1953 change
->MaybeFinish();
1955 [change
](nsresult aRv
) { change
->Cancel(aRv
); });
1956 blocker
->AppendNativeHandler(listener
);
1960 // Switching a subframe to be local within it's embedding process.
1961 RefPtr
<BrowserParent
> embedderBrowser
=
1962 embedderWindowGlobal
->GetBrowserParent();
1963 if (embedderBrowser
&&
1964 aOptions
.mRemoteType
== embedderBrowser
->Manager()->GetRemoteType()) {
1965 MOZ_DIAGNOSTIC_ASSERT(
1967 "We always have a PendingSwitchId, except for print-preview loads, "
1968 "which will never perform a process-switch to being in-process with "
1970 MOZ_DIAGNOSTIC_ASSERT(!aOptions
.mReplaceBrowsingContext
);
1971 MOZ_DIAGNOSTIC_ASSERT(!aOptions
.mRemoteType
.IsEmpty());
1972 MOZ_DIAGNOSTIC_ASSERT(!change
->mWaitingForPrepareToChange
);
1973 MOZ_DIAGNOSTIC_ASSERT(!change
->mSpecificGroup
);
1975 // Switching to local, so we don't need to create a new process, and will
1976 // instead use our embedder process.
1977 change
->mContentParent
= embedderBrowser
->Manager();
1978 change
->mContentParent
->AddKeepAlive();
1979 change
->ProcessLaunched();
1980 return promise
.forget();
1983 // Switching to the parent process.
1984 if (aOptions
.mRemoteType
.IsEmpty()) {
1985 change
->ProcessLaunched();
1986 return promise
.forget();
1989 // If we're aiming to end up in a new process of the same type as our old
1990 // process, and then putting our previous document in the BFCache, try to stay
1991 // in the same process to avoid creating new processes unnecessarily.
1992 RefPtr
<ContentParent
> existingProcess
= GetContentParent();
1993 if (existingProcess
&& existingProcess
->IsAlive() &&
1994 aOptions
.mReplaceBrowsingContext
&&
1995 aOptions
.mRemoteType
== existingProcess
->GetRemoteType()) {
1996 change
->mContentParent
= existingProcess
;
1997 change
->mContentParent
->AddKeepAlive();
1998 change
->ProcessLaunched();
1999 return promise
.forget();
2002 // Try to predict which BrowsingContextGroup will be used for the final load
2003 // in this BrowsingContext. This has to be accurate if switching into an
2004 // existing group, as it will control what pool of processes will be used
2005 // for process selection.
2007 // It's _technically_ OK to provide a group here if we're actually going to
2008 // switch into a brand new group, though it's sub-optimal, as it can
2009 // restrict the set of processes we're using.
2010 BrowsingContextGroup
* finalGroup
=
2011 aOptions
.mReplaceBrowsingContext
? change
->mSpecificGroup
.get() : Group();
2014 StaticPrefs::browser_tabs_remote_subframesPreferUsed() && !IsTop();
2016 change
->mContentParent
= ContentParent::GetNewOrUsedLaunchingBrowserProcess(
2017 /* aRemoteType = */ aOptions
.mRemoteType
,
2018 /* aGroup = */ finalGroup
,
2019 /* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND
,
2020 /* aPreferUsed = */ preferUsed
);
2021 if (!change
->mContentParent
) {
2022 change
->Cancel(NS_ERROR_FAILURE
);
2023 return promise
.forget();
2026 // Add a KeepAlive used by this ContentParent, which will be cleared when
2027 // the change is complete. This should prevent the process dying before
2028 // we're ready to use it.
2029 change
->mContentParent
->AddKeepAlive();
2030 if (change
->mContentParent
->IsLaunching()) {
2031 change
->mContentParent
->WaitForLaunchAsync()->Then(
2032 GetMainThreadSerialEventTarget(), __func__
,
2033 [change
](ContentParent
*) { change
->ProcessLaunched(); },
2034 [change
](LaunchError
) { change
->Cancel(NS_ERROR_FAILURE
); });
2036 change
->ProcessLaunched();
2038 return promise
.forget();
2041 void CanonicalBrowsingContext::MaybeSetPermanentKey(Element
* aEmbedder
) {
2042 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2045 if (nsCOMPtr
<nsIBrowser
> browser
= aEmbedder
->AsBrowser()) {
2046 JS::RootedValue
key(RootingCx());
2047 if (NS_SUCCEEDED(browser
->GetPermanentKey(&key
)) && key
.isObject()) {
2048 mPermanentKey
= key
;
2054 MediaController
* CanonicalBrowsingContext::GetMediaController() {
2055 // We would only create one media controller per tab, so accessing the
2056 // controller via the top-level browsing context.
2058 return Cast(Top())->GetMediaController();
2061 MOZ_ASSERT(!GetParent(),
2062 "Must access the controller from the top-level browsing context!");
2063 // Only content browsing context can create media controller, we won't create
2064 // controller for chrome document, such as the browser UI.
2065 if (!mTabMediaController
&& !IsDiscarded() && IsContent()) {
2066 mTabMediaController
= new MediaController(Id());
2068 return mTabMediaController
;
2071 bool CanonicalBrowsingContext::HasCreatedMediaController() const {
2072 return !!mTabMediaController
;
2075 bool CanonicalBrowsingContext::SupportsLoadingInParent(
2076 nsDocShellLoadState
* aLoadState
, uint64_t* aOuterWindowId
) {
2077 // We currently don't support initiating loads in the parent when they are
2078 // watched by devtools. This is because devtools tracks loads using content
2079 // process notifications, which happens after the load is initiated in this
2080 // case. Devtools clears all prior requests when it detects a new navigation,
2081 // so it drops the main document load that happened here.
2082 if (WatchedByDevTools()) {
2086 // Session-history-in-parent implementation relies currently on getting a
2087 // round trip through a child process.
2088 if (aLoadState
->LoadIsFromSessionHistory()) {
2092 // DocumentChannel currently only supports connecting channels into the
2093 // content process, so we can only support schemes that will always be loaded
2094 // there for now. Restrict to just http(s) for simplicity.
2095 if (!net::SchemeIsHTTP(aLoadState
->URI()) &&
2096 !net::SchemeIsHTTPS(aLoadState
->URI())) {
2100 if (WindowGlobalParent
* global
= GetCurrentWindowGlobal()) {
2101 nsCOMPtr
<nsIURI
> currentURI
= global
->GetDocumentURI();
2103 bool newURIHasRef
= false;
2104 aLoadState
->URI()->GetHasRef(&newURIHasRef
);
2105 bool equalsExceptRef
= false;
2106 aLoadState
->URI()->EqualsExceptRef(currentURI
, &equalsExceptRef
);
2108 if (equalsExceptRef
&& newURIHasRef
) {
2109 // This navigation is same-doc WRT the current one, we should pass it
2110 // down to the docshell to be handled.
2114 // If the current document has a beforeunload listener, then we need to
2115 // start the load in that process after we fire the event.
2116 if (global
->HasBeforeUnload()) {
2120 *aOuterWindowId
= global
->OuterWindowId();
2125 bool CanonicalBrowsingContext::LoadInParent(nsDocShellLoadState
* aLoadState
,
2126 bool aSetNavigating
) {
2127 // We currently only support starting loads directly from the
2128 // CanonicalBrowsingContext for top-level BCs.
2129 // We currently only support starting loads directly from the
2130 // CanonicalBrowsingContext for top-level BCs.
2131 if (!IsTopContent() || !GetContentParent() ||
2132 !StaticPrefs::browser_tabs_documentchannel_parent_controlled()) {
2136 uint64_t outerWindowId
= 0;
2137 if (!SupportsLoadingInParent(aLoadState
, &outerWindowId
)) {
2141 MOZ_ASSERT(!net::SchemeIsJavascript(aLoadState
->URI()));
2143 MOZ_ALWAYS_SUCCEEDS(
2144 SetParentInitiatedNavigationEpoch(++gParentInitiatedNavigationEpoch
));
2145 // Note: If successful, this will recurse into StartDocumentLoad and
2146 // set mCurrentLoad to the DocumentLoadListener instance created.
2147 // Ideally in the future we will only start loads from here, and we can
2148 // just set this directly instead.
2149 return net::DocumentLoadListener::LoadInParent(this, aLoadState
,
2153 bool CanonicalBrowsingContext::AttemptSpeculativeLoadInParent(
2154 nsDocShellLoadState
* aLoadState
) {
2155 // We currently only support starting loads directly from the
2156 // CanonicalBrowsingContext for top-level BCs.
2157 // We currently only support starting loads directly from the
2158 // CanonicalBrowsingContext for top-level BCs.
2159 if (!IsTopContent() || !GetContentParent() ||
2160 (StaticPrefs::browser_tabs_documentchannel_parent_controlled())) {
2164 uint64_t outerWindowId
= 0;
2165 if (!SupportsLoadingInParent(aLoadState
, &outerWindowId
)) {
2169 // If we successfully open the DocumentChannel, then it'll register
2170 // itself using aLoadIdentifier and be kept alive until it completes
2172 return net::DocumentLoadListener::SpeculativeLoadInParent(this, aLoadState
);
2175 bool CanonicalBrowsingContext::StartDocumentLoad(
2176 net::DocumentLoadListener
* aLoad
) {
2177 // If we're controlling loads from the parent, then starting a new load means
2178 // that we need to cancel any existing ones.
2179 if (StaticPrefs::browser_tabs_documentchannel_parent_controlled() &&
2181 // Make sure we are not loading a javascript URI.
2182 MOZ_ASSERT(!aLoad
->IsLoadingJSURI());
2184 // If we want to do a download, don't cancel the current navigation.
2185 if (!aLoad
->IsDownload()) {
2186 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
);
2189 mCurrentLoad
= aLoad
;
2191 if (NS_FAILED(SetCurrentLoadIdentifier(Some(aLoad
->GetLoadIdentifier())))) {
2192 mCurrentLoad
= nullptr;
2199 void CanonicalBrowsingContext::EndDocumentLoad(bool aContinueNavigating
) {
2200 mCurrentLoad
= nullptr;
2202 if (!aContinueNavigating
) {
2203 // Resetting the current load identifier on a discarded context
2204 // has no effect when a document load has finished.
2205 Unused
<< SetCurrentLoadIdentifier(Nothing());
2209 already_AddRefed
<nsIURI
> CanonicalBrowsingContext::GetCurrentURI() const {
2210 nsCOMPtr
<nsIURI
> currentURI
;
2211 if (nsIDocShell
* docShell
= GetDocShell()) {
2212 MOZ_ALWAYS_SUCCEEDS(
2213 nsDocShell::Cast(docShell
)->GetCurrentURI(getter_AddRefs(currentURI
)));
2215 currentURI
= mCurrentRemoteURI
;
2217 return currentURI
.forget();
2220 void CanonicalBrowsingContext::SetCurrentRemoteURI(nsIURI
* aCurrentRemoteURI
) {
2221 MOZ_ASSERT(!GetDocShell());
2222 mCurrentRemoteURI
= aCurrentRemoteURI
;
2225 void CanonicalBrowsingContext::ResetSHEntryHasUserInteractionCache() {
2226 WindowContext
* topWc
= GetTopWindowContext();
2227 if (topWc
&& !topWc
->IsDiscarded()) {
2228 MOZ_ALWAYS_SUCCEEDS(topWc
->SetSHEntryHasUserInteraction(false));
2232 void CanonicalBrowsingContext::HistoryCommitIndexAndLength() {
2234 CallerWillNotifyHistoryIndexAndLengthChanges
caller(nullptr);
2235 HistoryCommitIndexAndLength(changeID
, caller
);
2237 void CanonicalBrowsingContext::HistoryCommitIndexAndLength(
2238 const nsID
& aChangeID
,
2239 const CallerWillNotifyHistoryIndexAndLengthChanges
& aProofOfCaller
) {
2241 Cast(Top())->HistoryCommitIndexAndLength(aChangeID
, aProofOfCaller
);
2245 nsISHistory
* shistory
= GetSessionHistory();
2250 shistory
->GetIndex(&index
);
2251 int32_t length
= shistory
->GetCount();
2253 GetChildSessionHistory()->SetIndexAndLength(index
, length
, aChangeID
);
2255 shistory
->EvictOutOfRangeContentViewers(index
);
2257 Group()->EachParent([&](ContentParent
* aParent
) {
2258 Unused
<< aParent
->SendHistoryCommitIndexAndLength(this, index
, length
,
2263 void CanonicalBrowsingContext::SynchronizeLayoutHistoryState() {
2265 if (IsInProcess()) {
2266 nsIDocShell
* docShell
= GetDocShell();
2268 docShell
->PersistLayoutHistoryState();
2270 nsCOMPtr
<nsILayoutHistoryState
> state
;
2271 docShell
->GetLayoutHistoryState(getter_AddRefs(state
));
2273 mActiveEntry
->SetLayoutHistoryState(state
);
2276 } else if (ContentParent
* cp
= GetContentParent()) {
2277 cp
->SendGetLayoutHistoryState(this)->Then(
2278 GetCurrentSerialEventTarget(), __func__
,
2280 mActiveEntry
](const RefPtr
<nsILayoutHistoryState
>& aState
) {
2282 activeEntry
->SetLayoutHistoryState(aState
);
2290 void CanonicalBrowsingContext::ResetScalingZoom() {
2291 // This currently only ever gets called in the parent process, and we
2292 // pass the message on to the WindowGlobalChild for the rootmost browsing
2294 if (WindowGlobalParent
* topWindow
= GetTopWindowContext()) {
2295 Unused
<< topWindow
->SendResetScalingZoom();
2299 void CanonicalBrowsingContext::SetRestoreData(SessionStoreRestoreData
* aData
,
2300 ErrorResult
& aError
) {
2301 MOZ_DIAGNOSTIC_ASSERT(aData
);
2303 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(GetParentObject());
2304 RefPtr
<Promise
> promise
= Promise::Create(global
, aError
);
2305 if (aError
.Failed()) {
2309 if (NS_WARN_IF(NS_FAILED(SetHasRestoreData(true)))) {
2310 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2314 mRestoreState
= new RestoreState();
2315 mRestoreState
->mData
= aData
;
2316 mRestoreState
->mPromise
= promise
;
2319 already_AddRefed
<Promise
> CanonicalBrowsingContext::GetRestorePromise() {
2320 if (mRestoreState
) {
2321 return do_AddRef(mRestoreState
->mPromise
);
2326 void CanonicalBrowsingContext::ClearRestoreState() {
2327 if (!mRestoreState
) {
2328 MOZ_DIAGNOSTIC_ASSERT(!GetHasRestoreData());
2331 if (mRestoreState
->mPromise
) {
2332 mRestoreState
->mPromise
->MaybeRejectWithUndefined();
2334 mRestoreState
= nullptr;
2335 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
2338 void CanonicalBrowsingContext::RequestRestoreTabContent(
2339 WindowGlobalParent
* aWindow
) {
2340 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2342 if (IsDiscarded() || !mRestoreState
|| !mRestoreState
->mData
) {
2346 CanonicalBrowsingContext
* context
= aWindow
->GetBrowsingContext();
2347 MOZ_DIAGNOSTIC_ASSERT(!context
->IsDiscarded());
2349 RefPtr
<SessionStoreRestoreData
> data
=
2350 mRestoreState
->mData
->FindDataForChild(context
);
2352 if (context
->IsTop()) {
2353 MOZ_DIAGNOSTIC_ASSERT(context
== this);
2355 // We need to wait until the appropriate load event has fired before we
2356 // can "complete" the restore process, so if we're holding an empty data
2357 // object, just resolve the promise immediately.
2358 if (mRestoreState
->mData
->IsEmpty()) {
2359 MOZ_DIAGNOSTIC_ASSERT(!data
|| data
->IsEmpty());
2360 mRestoreState
->Resolve();
2361 ClearRestoreState();
2365 // Since we're following load event order, we'll only arrive here for a
2366 // toplevel context after we've already sent down data for all child frames,
2367 // so it's safe to clear this reference now. The completion callback below
2368 // relies on the mData field being null to determine if all requests have
2370 mRestoreState
->ClearData();
2371 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
2374 if (data
&& !data
->IsEmpty()) {
2375 auto onTabRestoreComplete
= [self
= RefPtr
{this},
2376 state
= RefPtr
{mRestoreState
}](auto) {
2378 if (!state
->mData
&& state
->mRequests
== state
->mResolves
) {
2380 if (state
== self
->mRestoreState
) {
2381 self
->ClearRestoreState();
2386 mRestoreState
->mRequests
++;
2388 if (data
->CanRestoreInto(aWindow
->GetDocumentURI())) {
2389 if (!aWindow
->IsInProcess()) {
2390 aWindow
->SendRestoreTabContent(data
, onTabRestoreComplete
,
2391 onTabRestoreComplete
);
2394 data
->RestoreInto(context
);
2397 // This must be called both when we're doing an in-process restore, and when
2398 // we didn't do a restore at all due to a URL mismatch.
2399 onTabRestoreComplete(true);
2403 void CanonicalBrowsingContext::RestoreState::Resolve() {
2404 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
2405 mPromise
->MaybeResolveWithUndefined();
2409 nsresult
CanonicalBrowsingContext::WriteSessionStorageToSessionStore(
2410 const nsTArray
<SSCacheCopy
>& aSesssionStorage
, uint32_t aEpoch
) {
2411 nsCOMPtr
<nsISessionStoreFunctions
> funcs
= do_ImportModule(
2412 "resource://gre/modules/SessionStoreFunctions.jsm", fallible
);
2414 return NS_ERROR_FAILURE
;
2417 nsCOMPtr
<nsIXPConnectWrappedJS
> wrapped
= do_QueryInterface(funcs
);
2419 if (!jsapi
.Init(wrapped
->GetJSObjectGlobal())) {
2420 return NS_ERROR_FAILURE
;
2423 JS::RootedValue
key(jsapi
.cx(), Top()->PermanentKey());
2425 Record
<nsCString
, Record
<nsString
, nsString
>> storage
;
2426 JS::RootedValue
update(jsapi
.cx());
2428 if (!aSesssionStorage
.IsEmpty()) {
2429 SessionStoreUtils::ConstructSessionStorageValues(this, aSesssionStorage
,
2431 if (!ToJSValue(jsapi
.cx(), storage
, &update
)) {
2432 return NS_ERROR_FAILURE
;
2438 return funcs
->UpdateSessionStoreForStorage(Top()->GetEmbedderElement(), this,
2439 key
, aEpoch
, update
);
2442 void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage(
2443 const std::function
<void()>& aDone
) {
2444 if constexpr (!SessionStoreUtils::NATIVE_LISTENER
) {
2449 using DataPromise
= BackgroundSessionStorageManager::DataPromise
;
2450 BackgroundSessionStorageManager::GetData(
2451 this, StaticPrefs::browser_sessionstore_dom_storage_limit(),
2452 /* aClearSessionStoreTimer = */ true)
2453 ->Then(GetCurrentSerialEventTarget(), __func__
,
2454 [self
= RefPtr
{this}, aDone
, epoch
= GetSessionStoreEpoch()](
2455 const DataPromise::ResolveOrRejectValue
& valueList
) {
2456 if (valueList
.IsResolve()) {
2457 self
->WriteSessionStorageToSessionStore(
2458 valueList
.ResolveValue(), epoch
);
2465 void CanonicalBrowsingContext::UpdateSessionStoreForStorage(
2466 uint64_t aBrowsingContextId
) {
2467 RefPtr
<CanonicalBrowsingContext
> browsingContext
= Get(aBrowsingContextId
);
2469 if (!browsingContext
) {
2473 browsingContext
->UpdateSessionStoreSessionStorage([]() {});
2476 void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() {
2477 if constexpr (!SessionStoreUtils::NATIVE_LISTENER
) {
2482 Top()->MaybeScheduleSessionStoreUpdate();
2486 if (IsInBFCache()) {
2490 if (mSessionStoreSessionStorageUpdateTimer
) {
2494 if (!StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
2495 auto result
= NS_NewTimerWithFuncCallback(
2496 [](nsITimer
*, void* aClosure
) {
2497 auto* context
= static_cast<CanonicalBrowsingContext
*>(aClosure
);
2498 context
->UpdateSessionStoreSessionStorage([]() {});
2500 this, StaticPrefs::browser_sessionstore_interval(),
2501 nsITimer::TYPE_ONE_SHOT
,
2502 "CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate");
2504 if (result
.isErr()) {
2508 mSessionStoreSessionStorageUpdateTimer
= result
.unwrap();
2512 void CanonicalBrowsingContext::CancelSessionStoreUpdate() {
2513 if (mSessionStoreSessionStorageUpdateTimer
) {
2514 mSessionStoreSessionStorageUpdateTimer
->Cancel();
2515 mSessionStoreSessionStorageUpdateTimer
= nullptr;
2519 void CanonicalBrowsingContext::SetContainerFeaturePolicy(
2520 FeaturePolicy
* aContainerFeaturePolicy
) {
2521 mContainerFeaturePolicy
= aContainerFeaturePolicy
;
2523 if (WindowGlobalParent
* current
= GetCurrentWindowGlobal()) {
2524 Unused
<< current
->SendSetContainerFeaturePolicy(mContainerFeaturePolicy
);
2528 void CanonicalBrowsingContext::SetCrossGroupOpenerId(uint64_t aOpenerId
) {
2529 MOZ_DIAGNOSTIC_ASSERT(IsTopContent());
2530 MOZ_DIAGNOSTIC_ASSERT(mCrossGroupOpenerId
== 0,
2531 "Can only set CrossGroupOpenerId once");
2532 mCrossGroupOpenerId
= aOpenerId
;
2535 void CanonicalBrowsingContext::SetCrossGroupOpener(
2536 CanonicalBrowsingContext
& aCrossGroupOpener
, ErrorResult
& aRv
) {
2537 if (!IsTopContent()) {
2538 aRv
.ThrowNotAllowedError(
2539 "Can only set crossGroupOpener on toplevel content");
2542 if (mCrossGroupOpenerId
!= 0) {
2543 aRv
.ThrowNotAllowedError("Can only set crossGroupOpener once");
2547 SetCrossGroupOpenerId(aCrossGroupOpener
.Id());
2550 auto CanonicalBrowsingContext::FindUnloadingHost(uint64_t aChildID
)
2551 -> nsTArray
<UnloadingHost
>::iterator
{
2552 return std::find_if(
2553 mUnloadingHosts
.begin(), mUnloadingHosts
.end(),
2554 [&](const auto& host
) { return host
.mChildID
== aChildID
; });
2557 void CanonicalBrowsingContext::ClearUnloadingHost(uint64_t aChildID
) {
2558 // Notify any callbacks which were waiting for the host to finish unloading
2560 auto found
= FindUnloadingHost(aChildID
);
2561 if (found
!= mUnloadingHosts
.end()) {
2562 auto callbacks
= std::move(found
->mCallbacks
);
2563 mUnloadingHosts
.RemoveElementAt(found
);
2564 for (const auto& callback
: callbacks
) {
2570 void CanonicalBrowsingContext::StartUnloadingHost(uint64_t aChildID
) {
2571 MOZ_DIAGNOSTIC_ASSERT(FindUnloadingHost(aChildID
) == mUnloadingHosts
.end());
2572 mUnloadingHosts
.AppendElement(UnloadingHost
{aChildID
, {}});
2575 void CanonicalBrowsingContext::BrowserParentDestroyed(
2576 BrowserParent
* aBrowserParent
, bool aAbnormalShutdown
) {
2577 ClearUnloadingHost(aBrowserParent
->Manager()->ChildID());
2579 // Handling specific to when the current BrowserParent has been destroyed.
2580 if (mCurrentBrowserParent
== aBrowserParent
) {
2581 mCurrentBrowserParent
= nullptr;
2583 // If this BrowserParent is for a subframe, attempt to recover from a
2584 // subframe crash by rendering the subframe crashed page in the embedding
2586 if (aAbnormalShutdown
) {
2587 ShowSubframeCrashedUI(aBrowserParent
->GetBrowserBridgeParent());
2592 void CanonicalBrowsingContext::ShowSubframeCrashedUI(
2593 BrowserBridgeParent
* aBridge
) {
2594 if (!aBridge
|| IsDiscarded() || !aBridge
->CanSend()) {
2598 MOZ_DIAGNOSTIC_ASSERT(!aBridge
->GetBrowsingContext() ||
2599 aBridge
->GetBrowsingContext() == this);
2601 // There is no longer a current inner window within this
2602 // BrowsingContext, update the `CurrentInnerWindowId` field to reflect
2604 MOZ_ALWAYS_SUCCEEDS(SetCurrentInnerWindowId(0));
2606 // The owning process will now be the embedder to render the subframe
2607 // crashed page, switch ownership back over.
2608 SetOwnerProcessId(aBridge
->Manager()->Manager()->ChildID());
2609 SetCurrentBrowserParent(aBridge
->Manager());
2611 Unused
<< aBridge
->SendSubFrameCrashed();
2614 static void LogBFCacheBlockingForDoc(BrowsingContext
* aBrowsingContext
,
2615 uint32_t aBFCacheCombo
, bool aIsSubDoc
) {
2617 nsAutoCString
uri("[no uri]");
2618 nsCOMPtr
<nsIURI
> currentURI
=
2619 aBrowsingContext
->Canonical()->GetCurrentURI();
2621 uri
= currentURI
->GetSpecOrDefault();
2623 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2624 (" ** Blocked for document %s", uri
.get()));
2626 if (aBFCacheCombo
& BFCacheStatus::EVENT_HANDLING_SUPPRESSED
) {
2627 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2628 (" * event handling suppression"));
2630 if (aBFCacheCombo
& BFCacheStatus::SUSPENDED
) {
2631 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * suspended Window"));
2633 if (aBFCacheCombo
& BFCacheStatus::UNLOAD_LISTENER
) {
2634 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * unload listener"));
2636 if (aBFCacheCombo
& BFCacheStatus::REQUEST
) {
2637 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * requests in the loadgroup"));
2639 if (aBFCacheCombo
& BFCacheStatus::ACTIVE_GET_USER_MEDIA
) {
2640 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * GetUserMedia"));
2642 if (aBFCacheCombo
& BFCacheStatus::ACTIVE_PEER_CONNECTION
) {
2643 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * PeerConnection"));
2645 if (aBFCacheCombo
& BFCacheStatus::CONTAINS_EME_CONTENT
) {
2646 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * EME content"));
2648 if (aBFCacheCombo
& BFCacheStatus::CONTAINS_MSE_CONTENT
) {
2649 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * MSE use"));
2651 if (aBFCacheCombo
& BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS
) {
2652 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * Speech use"));
2654 if (aBFCacheCombo
& BFCacheStatus::HAS_USED_VR
) {
2655 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * used VR"));
2657 if (aBFCacheCombo
& BFCacheStatus::BEFOREUNLOAD_LISTENER
) {
2658 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * beforeunload listener"));
2660 if (aBFCacheCombo
& BFCacheStatus::ACTIVE_LOCK
) {
2661 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * has active Web Locks"));
2665 bool CanonicalBrowsingContext::AllowedInBFCache(
2666 const Maybe
<uint64_t>& aChannelId
, nsIURI
* aNewURI
) {
2667 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
2668 nsAutoCString
uri("[no uri]");
2669 nsCOMPtr
<nsIURI
> currentURI
= GetCurrentURI();
2671 uri
= currentURI
->GetSpecOrDefault();
2673 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, ("Checking %s", uri
.get()));
2676 if (IsInProcess()) {
2680 uint32_t bfcacheCombo
= 0;
2681 if (mRestoreState
) {
2682 bfcacheCombo
|= BFCacheStatus::RESTORING
;
2683 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * during session restore"));
2686 if (Group()->Toplevels().Length() > 1) {
2687 bfcacheCombo
|= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG
;
2688 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2689 (" * auxiliary BrowsingContexts"));
2692 // There are not a lot of about:* pages that are allowed to load in
2693 // subframes, so it's OK to allow those few about:* pages enter BFCache.
2694 MOZ_ASSERT(IsTop(), "Trying to put a non top level BC into BFCache");
2696 WindowGlobalParent
* wgp
= GetCurrentWindowGlobal();
2697 if (wgp
&& wgp
->GetDocumentURI()) {
2698 nsCOMPtr
<nsIURI
> currentURI
= wgp
->GetDocumentURI();
2699 // Exempt about:* pages from bfcache, with the exception of about:blank
2700 if (currentURI
->SchemeIs("about") &&
2701 !currentURI
->GetSpecOrDefault().EqualsLiteral("about:blank")) {
2702 bfcacheCombo
|= BFCacheStatus::ABOUT_PAGE
;
2703 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * about:* page"));
2707 bool equalUri
= false;
2708 aNewURI
->Equals(currentURI
, &equalUri
);
2710 // When loading the same uri, disable bfcache so that
2711 // nsDocShell::OnNewURI transforms the load to LOAD_NORMAL_REPLACE.
2717 // For telemetry we're collecting all the flags for all the BCs hanging
2718 // from this top-level BC.
2719 PreOrderWalk([&](BrowsingContext
* aBrowsingContext
) {
2720 WindowGlobalParent
* wgp
=
2721 aBrowsingContext
->Canonical()->GetCurrentWindowGlobal();
2722 uint32_t subDocBFCacheCombo
= wgp
? wgp
->GetBFCacheStatus() : 0;
2724 const Maybe
<uint64_t>& singleChannelId
= wgp
->GetSingleChannelId();
2725 if (singleChannelId
.isSome()) {
2726 if (singleChannelId
.value() == 0 || aChannelId
.isNothing() ||
2727 singleChannelId
.value() != aChannelId
.value()) {
2728 subDocBFCacheCombo
|= BFCacheStatus::REQUEST
;
2733 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
2734 LogBFCacheBlockingForDoc(aBrowsingContext
, subDocBFCacheCombo
,
2735 aBrowsingContext
!= this);
2738 bfcacheCombo
|= subDocBFCacheCombo
;
2741 nsDocShell::ReportBFCacheComboTelemetry(bfcacheCombo
);
2742 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
2743 nsAutoCString
uri("[no uri]");
2744 nsCOMPtr
<nsIURI
> currentURI
= GetCurrentURI();
2746 uri
= currentURI
->GetSpecOrDefault();
2748 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2749 (" +> %s %s be blocked from going into the BFCache", uri
.get(),
2750 bfcacheCombo
== 0 ? "shouldn't" : "should"));
2753 if (StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners()) {
2754 bfcacheCombo
&= ~BFCacheStatus::UNLOAD_LISTENER
;
2757 return bfcacheCombo
== 0;
2760 void CanonicalBrowsingContext::SetTouchEventsOverride(
2761 dom::TouchEventsOverride aOverride
, ErrorResult
& aRv
) {
2762 SetTouchEventsOverrideInternal(aOverride
, aRv
);
2765 void CanonicalBrowsingContext::SetTargetTopLevelLinkClicksToBlank(
2766 bool aTargetTopLevelLinkClicksToBlank
, ErrorResult
& aRv
) {
2767 SetTargetTopLevelLinkClicksToBlankInternal(aTargetTopLevelLinkClicksToBlank
,
2771 void CanonicalBrowsingContext::AddPageAwakeRequest() {
2772 MOZ_ASSERT(IsTop());
2773 auto count
= GetPageAwakeRequestCount();
2774 MOZ_ASSERT(count
< UINT32_MAX
);
2775 Unused
<< SetPageAwakeRequestCount(++count
);
2778 void CanonicalBrowsingContext::RemovePageAwakeRequest() {
2779 MOZ_ASSERT(IsTop());
2780 auto count
= GetPageAwakeRequestCount();
2781 MOZ_ASSERT(count
> 0);
2782 Unused
<< SetPageAwakeRequestCount(--count
);
2785 void CanonicalBrowsingContext::CloneDocumentTreeInto(
2786 CanonicalBrowsingContext
* aSource
, const nsACString
& aRemoteType
,
2787 embedding::PrintData
&& aPrintData
) {
2788 NavigationIsolationOptions options
;
2789 options
.mRemoteType
= aRemoteType
;
2792 ChangeRemoteness(options
, /* aPendingSwitchId = */ 0)
2794 GetMainThreadSerialEventTarget(), __func__
,
2795 [source
= MaybeDiscardedBrowsingContext
{aSource
},
2796 data
= std::move(aPrintData
)](
2797 BrowserParent
* aBp
) -> RefPtr
<GenericNonExclusivePromise
> {
2798 RefPtr
<BrowserBridgeParent
> bridge
=
2799 aBp
->GetBrowserBridgeParent();
2800 return aBp
->SendCloneDocumentTreeIntoSelf(source
, data
)
2802 GetMainThreadSerialEventTarget(), __func__
,
2804 BrowserParent::CloneDocumentTreeIntoSelfPromise::
2805 ResolveOrRejectValue
&& aValue
) {
2806 // We're cloning a remote iframe, so we created a
2807 // BrowserBridge which makes us register an OOP load
2808 // (see Document::OOPChildLoadStarted), even though
2809 // this isn't a real load. We call
2810 // SendMaybeFireEmbedderLoadEvents here so that we do
2811 // register the end of the load (see
2812 // Document::OOPChildLoadDone).
2814 Unused
<< bridge
->SendMaybeFireEmbedderLoadEvents(
2815 EmbedderElementEventType::NoEvent
);
2817 if (aValue
.IsResolve() && aValue
.ResolveValue()) {
2818 return GenericNonExclusivePromise::CreateAndResolve(
2821 return GenericNonExclusivePromise::CreateAndReject(
2822 NS_ERROR_FAILURE
, __func__
);
2825 [](nsresult aRv
) -> RefPtr
<GenericNonExclusivePromise
> {
2827 nsPrintfCString("Remote clone failed: %x\n", unsigned(aRv
))
2829 return GenericNonExclusivePromise::CreateAndReject(
2830 NS_ERROR_FAILURE
, __func__
);
2833 mClonePromise
->Then(
2834 GetMainThreadSerialEventTarget(), __func__
,
2835 [self
= RefPtr
{this}]() { self
->mClonePromise
= nullptr; });
2838 bool CanonicalBrowsingContext::StartApzAutoscroll(float aAnchorX
,
2841 uint32_t aPresShellId
) {
2842 nsCOMPtr
<nsIWidget
> widget
;
2843 mozilla::layers::LayersId layersId
{0};
2845 if (IsInProcess()) {
2846 nsCOMPtr
<nsPIDOMWindowOuter
> outer
= GetDOMWindow();
2851 widget
= widget::WidgetUtils::DOMWindowToWidget(outer
);
2853 layersId
= widget
->GetRootLayerTreeId();
2856 RefPtr
<BrowserParent
> parent
= GetBrowserParent();
2861 widget
= parent
->GetWidget();
2862 layersId
= parent
->GetLayersId();
2865 if (!widget
|| !widget
->AsyncPanZoomEnabled()) {
2869 // The anchor coordinates that are passed in are relative to the origin of the
2870 // screen, but we are sending them to APZ which only knows about coordinates
2871 // relative to the widget, so convert them accordingly.
2872 const LayoutDeviceIntPoint anchor
=
2873 RoundedToInt(LayoutDevicePoint(aAnchorX
, aAnchorY
)) -
2874 widget
->WidgetToScreenOffset();
2876 mozilla::layers::ScrollableLayerGuid
guid(layersId
, aPresShellId
, aScrollId
);
2878 return widget
->StartAsyncAutoscroll(
2879 ViewAs
<ScreenPixel
>(
2880 anchor
, PixelCastJustification::LayoutDeviceIsScreenForBounds
),
2884 void CanonicalBrowsingContext::StopApzAutoscroll(nsViewID aScrollId
,
2885 uint32_t aPresShellId
) {
2886 nsCOMPtr
<nsIWidget
> widget
;
2887 mozilla::layers::LayersId layersId
{0};
2889 if (IsInProcess()) {
2890 nsCOMPtr
<nsPIDOMWindowOuter
> outer
= GetDOMWindow();
2895 widget
= widget::WidgetUtils::DOMWindowToWidget(outer
);
2897 layersId
= widget
->GetRootLayerTreeId();
2900 RefPtr
<BrowserParent
> parent
= GetBrowserParent();
2905 widget
= parent
->GetWidget();
2906 layersId
= parent
->GetLayersId();
2909 if (!widget
|| !widget
->AsyncPanZoomEnabled()) {
2913 mozilla::layers::ScrollableLayerGuid
guid(layersId
, aPresShellId
, aScrollId
);
2914 widget
->StopAsyncAutoscroll(guid
);
2917 NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext
)
2919 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext
,
2921 tmp
->mPermanentKey
.setNull();
2922 if (tmp
->mSessionHistory
) {
2923 tmp
->mSessionHistory
->SetBrowsingContext(nullptr);
2925 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionHistory
, mContainerFeaturePolicy
,
2926 mCurrentBrowserParent
, mWebProgress
,
2927 mSessionStoreSessionStorageUpdateTimer
)
2928 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2930 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CanonicalBrowsingContext
,
2932 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionHistory
, mContainerFeaturePolicy
,
2933 mCurrentBrowserParent
, mWebProgress
,
2934 mSessionStoreSessionStorageUpdateTimer
)
2935 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2937 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CanonicalBrowsingContext
,
2939 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPermanentKey
)
2940 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2942 NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext
, BrowsingContext
)
2943 NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext
, BrowsingContext
)
2945 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanonicalBrowsingContext
)
2946 NS_INTERFACE_MAP_END_INHERITING(BrowsingContext
)
2949 } // namespace mozilla