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"
10 #include "mozilla/CheckedInt.h"
11 #include "mozilla/ErrorResult.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/AsyncEventDispatcher.h"
14 #include "mozilla/dom/BrowserParent.h"
15 #include "mozilla/dom/BrowsingContextBinding.h"
16 #include "mozilla/dom/BrowsingContextGroup.h"
17 #include "mozilla/dom/ContentParent.h"
18 #include "mozilla/dom/EventTarget.h"
19 #include "mozilla/dom/PBrowserParent.h"
20 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
21 #include "mozilla/dom/PWindowGlobalParent.h"
22 #include "mozilla/dom/Promise.h"
23 #include "mozilla/dom/Promise-inl.h"
24 #include "mozilla/dom/WindowGlobalParent.h"
25 #include "mozilla/dom/ContentProcessManager.h"
26 #include "mozilla/dom/MediaController.h"
27 #include "mozilla/dom/MediaControlService.h"
28 #include "mozilla/dom/ContentPlaybackController.h"
29 #include "mozilla/dom/SessionStorageManager.h"
30 #include "mozilla/ipc/ProtocolUtils.h"
31 #include "mozilla/layers/CompositorBridgeChild.h"
33 # include "mozilla/layout/RemotePrintJobParent.h"
35 #include "mozilla/net/DocumentLoadListener.h"
36 #include "mozilla/NullPrincipal.h"
37 #include "mozilla/StaticPrefs_browser.h"
38 #include "mozilla/StaticPrefs_docshell.h"
39 #include "mozilla/StaticPrefs_fission.h"
40 #include "mozilla/Telemetry.h"
41 #include "nsILayoutHistoryState.h"
42 #include "nsIPrintSettings.h"
43 #include "nsIPrintSettingsService.h"
44 #include "nsISupports.h"
45 #include "nsIWebNavigation.h"
46 #include "nsDocShell.h"
47 #include "nsFrameLoader.h"
48 #include "nsFrameLoaderOwner.h"
49 #include "nsGlobalWindowOuter.h"
50 #include "nsIWebBrowserChrome.h"
51 #include "nsIXULRuntime.h"
52 #include "nsNetUtil.h"
53 #include "nsSHistory.h"
54 #include "nsSecureBrowserUI.h"
55 #include "nsQueryObject.h"
56 #include "nsBrowserStatusFilter.h"
57 #include "nsIBrowser.h"
58 #include "nsTHashSet.h"
59 #include "SessionStoreFunctions.h"
60 #include "nsIXPConnect.h"
61 #include "nsImportModule.h"
62 #include "UnitTransforms.h"
64 using namespace mozilla::ipc
;
66 extern mozilla::LazyLogModule gAutoplayPermissionLog
;
67 extern mozilla::LazyLogModule gSHLog
;
68 extern mozilla::LazyLogModule gSHIPBFCacheLog
;
70 #define AUTOPLAY_LOG(msg, ...) \
71 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
73 static mozilla::LazyLogModule
sPBContext("PBContext");
75 // Global count of canonical browsing contexts with the private attribute set
76 static uint32_t gNumberOfPrivateContexts
= 0;
78 // Current parent process epoch for parent initiated navigations
79 static uint64_t gParentInitiatedNavigationEpoch
= 0;
81 static void IncreasePrivateCount() {
82 gNumberOfPrivateContexts
++;
83 MOZ_LOG(sPBContext
, mozilla::LogLevel::Debug
,
84 ("%s: Private browsing context count %d -> %d", __func__
,
85 gNumberOfPrivateContexts
- 1, gNumberOfPrivateContexts
));
86 if (gNumberOfPrivateContexts
> 1) {
90 static bool sHasSeenPrivateContext
= false;
91 if (!sHasSeenPrivateContext
) {
92 sHasSeenPrivateContext
= true;
93 mozilla::Telemetry::ScalarSet(
94 mozilla::Telemetry::ScalarID::DOM_PARENTPROCESS_PRIVATE_WINDOW_USED
,
99 static void DecreasePrivateCount() {
100 MOZ_ASSERT(gNumberOfPrivateContexts
> 0);
101 gNumberOfPrivateContexts
--;
103 MOZ_LOG(sPBContext
, mozilla::LogLevel::Debug
,
104 ("%s: Private browsing context count %d -> %d", __func__
,
105 gNumberOfPrivateContexts
+ 1, gNumberOfPrivateContexts
));
106 if (!gNumberOfPrivateContexts
&&
107 !mozilla::StaticPrefs::browser_privatebrowsing_autostart()) {
108 nsCOMPtr
<nsIObserverService
> observerService
=
109 mozilla::services::GetObserverService();
110 if (observerService
) {
111 MOZ_LOG(sPBContext
, mozilla::LogLevel::Debug
,
112 ("%s: last-pb-context-exited fired", __func__
));
113 observerService
->NotifyObservers(nullptr, "last-pb-context-exited",
119 namespace mozilla::dom
{
121 extern mozilla::LazyLogModule gUserInteractionPRLog
;
123 #define USER_ACTIVATION_LOG(msg, ...) \
124 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
126 CanonicalBrowsingContext::CanonicalBrowsingContext(WindowContext
* aParentWindow
,
127 BrowsingContextGroup
* aGroup
,
128 uint64_t aBrowsingContextId
,
129 uint64_t aOwnerProcessId
,
130 uint64_t aEmbedderProcessId
,
131 BrowsingContext::Type aType
,
133 : BrowsingContext(aParentWindow
, aGroup
, aBrowsingContextId
, aType
,
135 mProcessId(aOwnerProcessId
),
136 mEmbedderProcessId(aEmbedderProcessId
),
137 mPermanentKey(JS::NullValue()) {
138 // You are only ever allowed to create CanonicalBrowsingContexts in the
140 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
142 // The initial URI in a BrowsingContext is always "about:blank".
144 NS_NewURI(getter_AddRefs(mCurrentRemoteURI
), "about:blank"));
146 mozilla::HoldJSObjects(this);
149 CanonicalBrowsingContext::~CanonicalBrowsingContext() {
150 mPermanentKey
.setNull();
152 mozilla::DropJSObjects(this);
154 if (mSessionHistory
) {
155 mSessionHistory
->SetBrowsingContext(nullptr);
160 already_AddRefed
<CanonicalBrowsingContext
> CanonicalBrowsingContext::Get(
162 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
163 return BrowsingContext::Get(aId
).downcast
<CanonicalBrowsingContext
>();
167 CanonicalBrowsingContext
* CanonicalBrowsingContext::Cast(
168 BrowsingContext
* aContext
) {
169 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
170 return static_cast<CanonicalBrowsingContext
*>(aContext
);
174 const CanonicalBrowsingContext
* CanonicalBrowsingContext::Cast(
175 const BrowsingContext
* aContext
) {
176 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
177 return static_cast<const CanonicalBrowsingContext
*>(aContext
);
180 already_AddRefed
<CanonicalBrowsingContext
> CanonicalBrowsingContext::Cast(
181 already_AddRefed
<BrowsingContext
>&& aContext
) {
182 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
183 return aContext
.downcast
<CanonicalBrowsingContext
>();
186 ContentParent
* CanonicalBrowsingContext::GetContentParent() const {
187 if (mProcessId
== 0) {
191 ContentProcessManager
* cpm
= ContentProcessManager::GetSingleton();
195 return cpm
->GetContentProcessById(ContentParentId(mProcessId
));
198 void CanonicalBrowsingContext::GetCurrentRemoteType(nsACString
& aRemoteType
,
199 ErrorResult
& aRv
) const {
200 // If we're in the parent process, dump out the void string.
201 if (mProcessId
== 0) {
202 aRemoteType
= NOT_REMOTE_TYPE
;
206 ContentParent
* cp
= GetContentParent();
208 aRv
.Throw(NS_ERROR_UNEXPECTED
);
212 aRemoteType
= cp
->GetRemoteType();
215 void CanonicalBrowsingContext::SetOwnerProcessId(uint64_t aProcessId
) {
216 MOZ_LOG(GetLog(), LogLevel::Debug
,
217 ("SetOwnerProcessId for 0x%08" PRIx64
" (0x%08" PRIx64
218 " -> 0x%08" PRIx64
")",
219 Id(), mProcessId
, aProcessId
));
221 mProcessId
= aProcessId
;
224 nsISecureBrowserUI
* CanonicalBrowsingContext::GetSecureBrowserUI() {
228 if (!mSecureBrowserUI
) {
229 mSecureBrowserUI
= new nsSecureBrowserUI(this);
231 return mSecureBrowserUI
;
235 // The DocShellProgressBridge is attached to a root content docshell loaded in
236 // the parent process. Notifications are paired up with the docshell which they
237 // came from, so that they can be fired to the correct
238 // BrowsingContextWebProgress and bubble through this tree separately.
240 // Notifications are filtered by a nsBrowserStatusFilter before being received
241 // by the DocShellProgressBridge.
242 class DocShellProgressBridge
: public nsIWebProgressListener
{
245 // NOTE: This relies in the expansion of `NS_FORWARD_SAFE` and all listener
246 // methods accepting an `aWebProgress` argument. If this changes in the
247 // future, this may need to be written manually.
248 NS_FORWARD_SAFE_NSIWEBPROGRESSLISTENER(GetTargetContext(aWebProgress
))
250 explicit DocShellProgressBridge(uint64_t aTopContextId
)
251 : mTopContextId(aTopContextId
) {}
254 virtual ~DocShellProgressBridge() = default;
256 nsIWebProgressListener
* GetTargetContext(nsIWebProgress
* aWebProgress
) {
257 RefPtr
<CanonicalBrowsingContext
> context
;
258 if (nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(aWebProgress
)) {
259 context
= docShell
->GetBrowsingContext()->Canonical();
261 context
= CanonicalBrowsingContext::Get(mTopContextId
);
263 return context
&& !context
->IsDiscarded() ? context
->GetWebProgress()
267 uint64_t mTopContextId
= 0;
270 NS_IMPL_ISUPPORTS(DocShellProgressBridge
, nsIWebProgressListener
)
273 void CanonicalBrowsingContext::MaybeAddAsProgressListener(
274 nsIWebProgress
* aWebProgress
) {
275 // Only add as a listener if the created docshell is a toplevel content
276 // docshell. We'll get notifications for all of our subframes through a single
278 if (!IsTopContent()) {
282 if (!mDocShellProgressBridge
) {
283 mDocShellProgressBridge
= new DocShellProgressBridge(Id());
284 mStatusFilter
= new nsBrowserStatusFilter();
285 mStatusFilter
->AddProgressListener(mDocShellProgressBridge
,
286 nsIWebProgress::NOTIFY_ALL
);
289 aWebProgress
->AddProgressListener(mStatusFilter
, nsIWebProgress::NOTIFY_ALL
);
292 void CanonicalBrowsingContext::ReplacedBy(
293 CanonicalBrowsingContext
* aNewContext
,
294 const NavigationIsolationOptions
& aRemotenessOptions
) {
295 MOZ_ASSERT(!aNewContext
->mWebProgress
);
296 MOZ_ASSERT(!aNewContext
->mSessionHistory
);
297 MOZ_ASSERT(IsTop() && aNewContext
->IsTop());
300 aNewContext
->mIsReplaced
= false;
303 mStatusFilter
->RemoveProgressListener(mDocShellProgressBridge
);
304 mStatusFilter
= nullptr;
307 mWebProgress
->ContextReplaced(aNewContext
);
308 aNewContext
->mWebProgress
= std::move(mWebProgress
);
310 // Use the Transaction for the fields which need to be updated whether or not
311 // the new context has been attached before.
312 // SetWithoutSyncing can be used if context hasn't been attached.
314 txn
.SetBrowserId(GetBrowserId());
315 txn
.SetIsAppTab(GetIsAppTab());
316 txn
.SetHasSiblings(GetHasSiblings());
317 txn
.SetHistoryID(GetHistoryID());
318 txn
.SetExplicitActive(GetExplicitActive());
319 txn
.SetEmbedderColorSchemes(GetEmbedderColorSchemes());
320 txn
.SetHasRestoreData(GetHasRestoreData());
321 txn
.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart());
322 txn
.SetForceOffline(GetForceOffline());
324 // Propagate some settings on BrowsingContext replacement so they're not lost
325 // on bfcached navigations. These are important for GeckoView (see bug
327 txn
.SetAllowJavascript(GetAllowJavascript());
328 txn
.SetForceEnableTrackingProtection(GetForceEnableTrackingProtection());
329 txn
.SetUserAgentOverride(GetUserAgentOverride());
330 txn
.SetSuspendMediaWhenInactive(GetSuspendMediaWhenInactive());
331 txn
.SetDisplayMode(GetDisplayMode());
332 txn
.SetForceDesktopViewport(GetForceDesktopViewport());
333 txn
.SetIsUnderHiddenEmbedderElement(GetIsUnderHiddenEmbedderElement());
335 // When using site-specific zoom, we let the front-end manage it, otherwise it
336 // can cause weirdness like bug 1846141.
337 if (!StaticPrefs::browser_zoom_siteSpecific()) {
338 txn
.SetFullZoom(GetFullZoom());
339 txn
.SetTextZoom(GetTextZoom());
342 // Propagate the default load flags so that the TRR mode flags are forwarded
343 // to the new browsing context. See bug 1828643.
344 txn
.SetDefaultLoadFlags(GetDefaultLoadFlags());
346 // As this is a different BrowsingContext, set InitialSandboxFlags to the
347 // current flags in the new context so that they also apply to any initial
348 // about:blank documents created in it.
349 txn
.SetSandboxFlags(GetSandboxFlags());
350 txn
.SetInitialSandboxFlags(GetSandboxFlags());
351 txn
.SetTargetTopLevelLinkClicksToBlankInternal(
352 TargetTopLevelLinkClicksToBlank());
353 if (aNewContext
->EverAttached()) {
354 MOZ_ALWAYS_SUCCEEDS(txn
.Commit(aNewContext
));
356 txn
.CommitWithoutSyncing(aNewContext
);
359 aNewContext
->mRestoreState
= mRestoreState
.forget();
360 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
362 // XXXBFCache name handling is still a bit broken in Fission in general,
363 // at least in case name should be cleared.
364 if (aRemotenessOptions
.mTryUseBFCache
) {
365 MOZ_ASSERT(!aNewContext
->EverAttached());
366 aNewContext
->mFields
.SetWithoutSyncing
<IDX_Name
>(GetName());
367 // We don't copy over HasLoadedNonInitialDocument here, we'll actually end
368 // up loading a new initial document at this point, before the real load.
369 // The real load will then end up setting HasLoadedNonInitialDocument to
373 if (mSessionHistory
) {
374 mSessionHistory
->SetBrowsingContext(aNewContext
);
375 // At this point we will be creating a new ChildSHistory in the child.
376 // That means that the child's epoch will be reset, so it makes sense to
377 // reset the epoch in the parent too.
378 mSessionHistory
->SetEpoch(0, Nothing());
379 mSessionHistory
.swap(aNewContext
->mSessionHistory
);
380 RefPtr
<ChildSHistory
> childSHistory
= ForgetChildSHistory();
381 aNewContext
->SetChildSHistory(childSHistory
);
384 BackgroundSessionStorageManager::PropagateManager(Id(), aNewContext
->Id());
386 // Transfer the ownership of the priority active status from the old context
387 // to the new context.
388 aNewContext
->mPriorityActive
= mPriorityActive
;
389 mPriorityActive
= false;
391 MOZ_ASSERT(aNewContext
->mLoadingEntries
.IsEmpty());
392 mLoadingEntries
.SwapElements(aNewContext
->mLoadingEntries
);
393 MOZ_ASSERT(!aNewContext
->mActiveEntry
);
394 mActiveEntry
.swap(aNewContext
->mActiveEntry
);
396 aNewContext
->mPermanentKey
= mPermanentKey
;
397 mPermanentKey
.setNull();
400 void CanonicalBrowsingContext::UpdateSecurityState() {
401 if (mSecureBrowserUI
) {
402 mSecureBrowserUI
->RecomputeSecurityFlags();
406 void CanonicalBrowsingContext::GetWindowGlobals(
407 nsTArray
<RefPtr
<WindowGlobalParent
>>& aWindows
) {
408 aWindows
.SetCapacity(GetWindowContexts().Length());
409 for (auto& window
: GetWindowContexts()) {
410 aWindows
.AppendElement(static_cast<WindowGlobalParent
*>(window
.get()));
414 WindowGlobalParent
* CanonicalBrowsingContext::GetCurrentWindowGlobal() const {
415 return static_cast<WindowGlobalParent
*>(GetCurrentWindowContext());
418 WindowGlobalParent
* CanonicalBrowsingContext::GetParentWindowContext() {
419 return static_cast<WindowGlobalParent
*>(
420 BrowsingContext::GetParentWindowContext());
423 WindowGlobalParent
* CanonicalBrowsingContext::GetTopWindowContext() {
424 return static_cast<WindowGlobalParent
*>(
425 BrowsingContext::GetTopWindowContext());
428 already_AddRefed
<nsIWidget
>
429 CanonicalBrowsingContext::GetParentProcessWidgetContaining() {
430 // If our document is loaded in-process, such as chrome documents, get the
431 // widget directly from our outer window. Otherwise, try to get the widget
432 // from the toplevel content's browser's element.
433 nsCOMPtr
<nsIWidget
> widget
;
434 if (nsGlobalWindowOuter
* window
= nsGlobalWindowOuter::Cast(GetDOMWindow())) {
435 widget
= window
->GetNearestWidget();
436 } else if (Element
* topEmbedder
= Top()->GetEmbedderElement()) {
437 widget
= nsContentUtils::WidgetForContent(topEmbedder
);
439 widget
= nsContentUtils::WidgetForDocument(topEmbedder
->OwnerDoc());
444 widget
= widget
->GetTopLevelWidget();
447 return widget
.forget();
450 already_AddRefed
<nsIBrowserDOMWindow
>
451 CanonicalBrowsingContext::GetBrowserDOMWindow() {
452 RefPtr
<CanonicalBrowsingContext
> chromeTop
= TopCrossChromeBoundary();
453 nsGlobalWindowOuter
* topWin
;
454 if ((topWin
= nsGlobalWindowOuter::Cast(chromeTop
->GetDOMWindow())) &&
455 topWin
->IsChromeWindow()) {
456 return do_AddRef(topWin
->GetBrowserDOMWindow());
461 already_AddRefed
<WindowGlobalParent
>
462 CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
463 uint64_t windowId
= GetEmbedderInnerWindowId();
468 return WindowGlobalParent::GetByInnerWindowId(windowId
);
471 CanonicalBrowsingContext
*
472 CanonicalBrowsingContext::GetParentCrossChromeBoundary() {
474 return Cast(GetParent());
476 if (auto* embedder
= GetEmbedderElement()) {
477 return Cast(embedder
->OwnerDoc()->GetBrowsingContext());
482 CanonicalBrowsingContext
* CanonicalBrowsingContext::TopCrossChromeBoundary() {
483 CanonicalBrowsingContext
* bc
= this;
484 while (auto* parent
= bc
->GetParentCrossChromeBoundary()) {
490 Nullable
<WindowProxyHolder
> CanonicalBrowsingContext::GetTopChromeWindow() {
491 RefPtr
<CanonicalBrowsingContext
> bc
= TopCrossChromeBoundary();
492 if (bc
->IsChrome()) {
493 return WindowProxyHolder(bc
.forget());
498 nsISHistory
* CanonicalBrowsingContext::GetSessionHistory() {
500 return Cast(Top())->GetSessionHistory();
503 // Check GetChildSessionHistory() to make sure that this BrowsingContext has
504 // session history enabled.
505 if (!mSessionHistory
&& GetChildSessionHistory()) {
506 mSessionHistory
= new nsSHistory(this);
509 return mSessionHistory
;
512 SessionHistoryEntry
* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() {
516 void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
517 SessionHistoryEntry
* aEntry
) {
518 mActiveEntry
= aEntry
;
521 bool CanonicalBrowsingContext::HasHistoryEntry(nsISHEntry
* aEntry
) {
522 // XXX Should we check also loading entries?
523 return aEntry
&& mActiveEntry
== aEntry
;
526 void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry
* aOldEntry
,
527 nsISHEntry
* aNewEntry
) {
528 // XXX Should we check also loading entries?
529 if (mActiveEntry
== aOldEntry
) {
530 nsCOMPtr
<SessionHistoryEntry
> newEntry
= do_QueryInterface(aNewEntry
);
531 mActiveEntry
= newEntry
.forget();
535 void CanonicalBrowsingContext::AddLoadingSessionHistoryEntry(
536 uint64_t aLoadId
, SessionHistoryEntry
* aEntry
) {
537 Unused
<< SetHistoryID(aEntry
->DocshellID());
538 mLoadingEntries
.AppendElement(LoadingSessionHistoryEntry
{aLoadId
, aEntry
});
541 void CanonicalBrowsingContext::GetLoadingSessionHistoryInfoFromParent(
542 Maybe
<LoadingSessionHistoryInfo
>& aLoadingInfo
) {
543 nsISHistory
* shistory
= GetSessionHistory();
544 if (!shistory
|| !GetParent()) {
548 SessionHistoryEntry
* parentSHE
=
549 GetParent()->Canonical()->GetActiveSessionHistoryEntry();
552 for (BrowsingContext
* sibling
: GetParent()->Children()) {
554 if (sibling
== this) {
555 nsCOMPtr
<nsISHEntry
> shEntry
;
556 parentSHE
->GetChildSHEntryIfHasNoDynamicallyAddedChild(
557 index
, getter_AddRefs(shEntry
));
558 nsCOMPtr
<SessionHistoryEntry
> she
= do_QueryInterface(shEntry
);
560 aLoadingInfo
.emplace(she
);
561 mLoadingEntries
.AppendElement(LoadingSessionHistoryEntry
{
562 aLoadingInfo
.value().mLoadId
, she
.get()});
563 Unused
<< SetHistoryID(she
->DocshellID());
571 UniquePtr
<LoadingSessionHistoryInfo
>
572 CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad(
573 nsDocShellLoadState
* aLoadState
, SessionHistoryEntry
* existingEntry
,
574 nsIChannel
* aChannel
) {
575 RefPtr
<SessionHistoryEntry
> entry
;
576 const LoadingSessionHistoryInfo
* existingLoadingInfo
=
577 aLoadState
->GetLoadingSessionHistoryInfo();
578 MOZ_ASSERT_IF(!existingLoadingInfo
, !existingEntry
);
579 if (existingLoadingInfo
) {
581 entry
= existingEntry
;
583 MOZ_ASSERT(!existingLoadingInfo
->mLoadIsFromSessionHistory
);
585 SessionHistoryEntry::LoadingEntry
* loadingEntry
=
586 SessionHistoryEntry::GetByLoadId(existingLoadingInfo
->mLoadId
);
587 MOZ_LOG(gSHLog
, LogLevel::Verbose
,
588 ("SHEntry::GetByLoadId(%" PRIu64
") -> %p",
589 existingLoadingInfo
->mLoadId
, entry
.get()));
593 entry
= loadingEntry
->mEntry
;
596 // If the entry was updated, update also the LoadingSessionHistoryInfo.
597 UniquePtr
<LoadingSessionHistoryInfo
> lshi
=
598 MakeUnique
<LoadingSessionHistoryInfo
>(entry
, existingLoadingInfo
);
599 aLoadState
->SetLoadingSessionHistoryInfo(std::move(lshi
));
600 existingLoadingInfo
= aLoadState
->GetLoadingSessionHistoryInfo();
601 Unused
<< SetHistoryEntryCount(entry
->BCHistoryLength());
602 } else if (aLoadState
->LoadType() == LOAD_REFRESH
&&
603 !ShouldAddEntryForRefresh(aLoadState
->URI(),
604 aLoadState
->PostDataStream()) &&
606 entry
= mActiveEntry
;
608 entry
= new SessionHistoryEntry(aLoadState
, aChannel
);
610 // Only top level pages care about Get/SetPersist.
612 nsDocShell::ShouldAddToSessionHistory(aLoadState
->URI(), aChannel
));
613 } else if (mActiveEntry
|| !mLoadingEntries
.IsEmpty()) {
614 entry
->SetIsSubFrame(true);
616 entry
->SetDocshellID(GetHistoryID());
617 entry
->SetIsDynamicallyAdded(CreatedDynamically());
618 entry
->SetForInitialLoad(true);
620 MOZ_DIAGNOSTIC_ASSERT(entry
);
622 UniquePtr
<LoadingSessionHistoryInfo
> loadingInfo
;
623 if (existingLoadingInfo
) {
624 loadingInfo
= MakeUnique
<LoadingSessionHistoryInfo
>(*existingLoadingInfo
);
626 loadingInfo
= MakeUnique
<LoadingSessionHistoryInfo
>(entry
);
627 mLoadingEntries
.AppendElement(
628 LoadingSessionHistoryEntry
{loadingInfo
->mLoadId
, entry
});
631 MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(loadingInfo
->mLoadId
)->mEntry
==
637 UniquePtr
<LoadingSessionHistoryInfo
>
638 CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
639 LoadingSessionHistoryInfo
* aInfo
, nsIChannel
* aNewChannel
) {
641 MOZ_ASSERT(aNewChannel
);
643 SessionHistoryInfo newInfo
= SessionHistoryInfo(
644 aNewChannel
, aInfo
->mInfo
.LoadType(),
645 aInfo
->mInfo
.GetPartitionedPrincipalToInherit(), aInfo
->mInfo
.GetCsp());
647 for (size_t i
= 0; i
< mLoadingEntries
.Length(); ++i
) {
648 if (mLoadingEntries
[i
].mLoadId
== aInfo
->mLoadId
) {
649 RefPtr
<SessionHistoryEntry
> loadingEntry
= mLoadingEntries
[i
].mEntry
;
650 loadingEntry
->SetInfo(&newInfo
);
653 // Only top level pages care about Get/SetPersist.
654 nsCOMPtr
<nsIURI
> uri
;
655 aNewChannel
->GetURI(getter_AddRefs(uri
));
656 loadingEntry
->SetPersist(
657 nsDocShell::ShouldAddToSessionHistory(uri
, aNewChannel
));
659 loadingEntry
->SetIsSubFrame(aInfo
->mInfo
.IsSubFrame());
661 loadingEntry
->SetDocshellID(GetHistoryID());
662 loadingEntry
->SetIsDynamicallyAdded(CreatedDynamically());
663 return MakeUnique
<LoadingSessionHistoryInfo
>(loadingEntry
, aInfo
);
669 using PrintPromise
= CanonicalBrowsingContext::PrintPromise
;
671 class PrintListenerAdapter final
: public nsIWebProgressListener
{
673 explicit PrintListenerAdapter(PrintPromise::Private
* aPromise
)
674 : mPromise(aPromise
) {}
678 // NS_DECL_NSIWEBPROGRESSLISTENER
679 NS_IMETHOD
OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
680 uint32_t aStateFlags
, nsresult aStatus
) override
{
681 if (aStateFlags
& nsIWebProgressListener::STATE_STOP
&&
682 aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
&& mPromise
) {
683 mPromise
->Resolve(true, __func__
);
688 NS_IMETHOD
OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
690 const char16_t
* aMessage
) override
{
691 if (aStatus
!= NS_OK
&& mPromise
) {
692 mPromise
->Reject(aStatus
, __func__
);
697 NS_IMETHOD
OnProgressChange(nsIWebProgress
* aWebProgress
,
698 nsIRequest
* aRequest
, int32_t aCurSelfProgress
,
699 int32_t aMaxSelfProgress
,
700 int32_t aCurTotalProgress
,
701 int32_t aMaxTotalProgress
) override
{
704 NS_IMETHOD
OnLocationChange(nsIWebProgress
* aWebProgress
,
705 nsIRequest
* aRequest
, nsIURI
* aLocation
,
706 uint32_t aFlags
) override
{
709 NS_IMETHOD
OnSecurityChange(nsIWebProgress
* aWebProgress
,
710 nsIRequest
* aRequest
, uint32_t aState
) override
{
713 NS_IMETHOD
OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
714 nsIRequest
* aRequest
,
715 uint32_t aEvent
) override
{
720 ~PrintListenerAdapter() = default;
722 RefPtr
<PrintPromise::Private
> mPromise
;
725 NS_IMPL_ISUPPORTS(PrintListenerAdapter
, nsIWebProgressListener
)
728 already_AddRefed
<Promise
> CanonicalBrowsingContext::PrintJS(
729 nsIPrintSettings
* aPrintSettings
, ErrorResult
& aRv
) {
730 RefPtr
<Promise
> promise
= Promise::Create(GetIncumbentGlobal(), aRv
);
731 if (NS_WARN_IF(aRv
.Failed())) {
732 return promise
.forget();
735 Print(aPrintSettings
)
737 GetCurrentSerialEventTarget(), __func__
,
738 [promise
](bool) { promise
->MaybeResolveWithUndefined(); },
739 [promise
](nsresult aResult
) { promise
->MaybeReject(aResult
); });
740 return promise
.forget();
743 RefPtr
<PrintPromise
> CanonicalBrowsingContext::Print(
744 nsIPrintSettings
* aPrintSettings
) {
746 return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
, __func__
);
749 auto promise
= MakeRefPtr
<PrintPromise::Private
>(__func__
);
750 auto listener
= MakeRefPtr
<PrintListenerAdapter
>(promise
);
752 RefPtr
<nsGlobalWindowOuter
> outerWindow
=
753 nsGlobalWindowOuter::Cast(GetDOMWindow());
754 if (NS_WARN_IF(!outerWindow
)) {
755 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
760 outerWindow
->Print(aPrintSettings
,
761 /* aRemotePrintJob = */ nullptr, listener
,
762 /* aDocShellToCloneInto = */ nullptr,
763 nsGlobalWindowOuter::IsPreview::No
,
764 nsGlobalWindowOuter::IsForWindowDotPrint::No
,
765 /* aPrintPreviewCallback = */ nullptr, rv
);
767 promise
->Reject(rv
.StealNSResult(), __func__
);
772 auto* browserParent
= GetBrowserParent();
773 if (NS_WARN_IF(!browserParent
)) {
774 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
778 nsCOMPtr
<nsIPrintSettingsService
> printSettingsSvc
=
779 do_GetService("@mozilla.org/gfx/printsettings-service;1");
780 if (NS_WARN_IF(!printSettingsSvc
)) {
781 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
786 nsCOMPtr
<nsIPrintSettings
> printSettings
= aPrintSettings
;
787 if (!printSettings
) {
789 printSettingsSvc
->CreateNewPrintSettings(getter_AddRefs(printSettings
));
790 if (NS_WARN_IF(NS_FAILED(rv
))) {
791 promise
->Reject(rv
, __func__
);
796 embedding::PrintData printData
;
797 rv
= printSettingsSvc
->SerializeToPrintData(printSettings
, &printData
);
798 if (NS_WARN_IF(NS_FAILED(rv
))) {
799 promise
->Reject(rv
, __func__
);
803 layout::RemotePrintJobParent
* remotePrintJob
=
804 new layout::RemotePrintJobParent(printSettings
);
805 printData
.remotePrintJob() =
806 browserParent
->Manager()->SendPRemotePrintJobConstructor(remotePrintJob
);
809 remotePrintJob
->RegisterListener(listener
);
812 if (NS_WARN_IF(!browserParent
->SendPrint(this, printData
))) {
813 promise
->Reject(NS_ERROR_FAILURE
, __func__
);
815 return promise
.forget();
819 void CanonicalBrowsingContext::CallOnAllTopDescendants(
820 const FunctionRef
<CallState(CanonicalBrowsingContext
*)>& aCallback
,
821 bool aIncludeNestedBrowsers
) {
822 MOZ_ASSERT(IsTop(), "Should only call on top BC");
824 !aIncludeNestedBrowsers
||
825 (IsChrome() && !GetParentCrossChromeBoundary()),
826 "If aIncludeNestedBrowsers is set, should only call on top chrome BC");
828 if (!IsInProcess()) {
829 // We rely on top levels having to be embedded in the parent process, so
830 // we can only have top level descendants if embedded here..
834 AutoTArray
<RefPtr
<BrowsingContextGroup
>, 32> groups
;
835 BrowsingContextGroup::GetAllGroups(groups
);
836 for (auto& browsingContextGroup
: groups
) {
837 for (auto& bc
: browsingContextGroup
->Toplevels()) {
839 // Cannot be a descendent of myself so skip.
843 if (aIncludeNestedBrowsers
) {
844 if (this != bc
->Canonical()->TopCrossChromeBoundary()) {
848 auto* parent
= bc
->Canonical()->GetParentCrossChromeBoundary();
849 if (!parent
|| this != parent
->Top()) {
854 if (aCallback(bc
->Canonical()) == CallState::Stop
) {
861 void CanonicalBrowsingContext::SessionHistoryCommit(
862 uint64_t aLoadId
, const nsID
& aChangeID
, uint32_t aLoadType
, bool aPersist
,
863 bool aCloneEntryChildren
, bool aChannelExpired
, uint32_t aCacheKey
) {
864 MOZ_LOG(gSHLog
, LogLevel::Verbose
,
865 ("CanonicalBrowsingContext::SessionHistoryCommit %p %" PRIu64
, this,
867 MOZ_ASSERT(aLoadId
!= UINT64_MAX
,
868 "Must not send special about:blank loadinfo to parent.");
869 for (size_t i
= 0; i
< mLoadingEntries
.Length(); ++i
) {
870 if (mLoadingEntries
[i
].mLoadId
== aLoadId
) {
871 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
873 SessionHistoryEntry::RemoveLoadId(aLoadId
);
874 mLoadingEntries
.RemoveElementAt(i
);
878 RefPtr
<SessionHistoryEntry
> newActiveEntry
= mLoadingEntries
[i
].mEntry
;
879 if (aCacheKey
!= 0) {
880 newActiveEntry
->SetCacheKey(aCacheKey
);
883 if (aChannelExpired
) {
884 newActiveEntry
->SharedInfo()->mExpired
= true;
887 bool loadFromSessionHistory
= !newActiveEntry
->ForInitialLoad();
888 newActiveEntry
->SetForInitialLoad(false);
889 SessionHistoryEntry::RemoveLoadId(aLoadId
);
890 mLoadingEntries
.RemoveElementAt(i
);
892 int32_t indexOfHistoryLoad
= -1;
893 if (loadFromSessionHistory
) {
894 nsCOMPtr
<nsISHEntry
> root
= nsSHistory::GetRootSHEntry(newActiveEntry
);
895 indexOfHistoryLoad
= shistory
->GetIndexOfEntry(root
);
896 if (indexOfHistoryLoad
< 0) {
897 // Entry has been removed from the session history.
902 CallerWillNotifyHistoryIndexAndLengthChanges
caller(shistory
);
904 // If there is a name in the new entry, clear the name of all contiguous
905 // entries. This is for https://html.spec.whatwg.org/#history-traversal
907 nsAutoString nameOfNewEntry
;
908 newActiveEntry
->GetName(nameOfNewEntry
);
909 if (!nameOfNewEntry
.IsEmpty()) {
910 nsSHistory::WalkContiguousEntries(
912 [](nsISHEntry
* aEntry
) { aEntry
->SetName(EmptyString()); });
915 bool addEntry
= ShouldUpdateSessionHistory(aLoadType
);
917 if (mActiveEntry
&& !mActiveEntry
->GetFrameLoader()) {
918 bool sharesDocument
= true;
919 mActiveEntry
->SharesDocumentWith(newActiveEntry
, &sharesDocument
);
920 if (!sharesDocument
) {
921 // If the old page won't be in the bfcache,
922 // clear the dynamic entries.
923 RemoveDynEntriesFromActiveSessionHistoryEntry();
927 if (LOAD_TYPE_HAS_FLAGS(aLoadType
,
928 nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY
)) {
929 // Replace the current entry with the new entry.
930 int32_t index
= shistory
->GetIndexForReplace();
932 // If we're trying to replace an inexistant shistory entry then we
933 // should append instead.
934 addEntry
= index
< 0;
936 shistory
->ReplaceEntry(index
, newActiveEntry
);
938 mActiveEntry
= newActiveEntry
;
939 } else if (LOAD_TYPE_HAS_FLAGS(
940 aLoadType
, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH
) &&
941 !ShouldAddEntryForRefresh(newActiveEntry
) && mActiveEntry
) {
943 mActiveEntry
->ReplaceWith(*newActiveEntry
);
945 mActiveEntry
= newActiveEntry
;
948 if (loadFromSessionHistory
) {
949 // XXX Synchronize browsing context tree and session history tree?
950 shistory
->InternalSetRequestedIndex(indexOfHistoryLoad
);
951 shistory
->UpdateIndex();
954 mActiveEntry
->SetWireframe(Nothing());
956 } else if (addEntry
) {
957 shistory
->AddEntry(mActiveEntry
, aPersist
);
958 shistory
->InternalSetRequestedIndex(-1);
961 // FIXME The old implementations adds it to the parent's mLSHE if there
962 // is one, need to figure out if that makes sense here (peterv
963 // doesn't think it would).
964 if (loadFromSessionHistory
) {
966 // mActiveEntry is null if we're loading iframes from session
967 // history while also parent page is loading from session history.
968 // In that case there isn't anything to sync.
969 mActiveEntry
->SyncTreesForSubframeNavigation(newActiveEntry
, Top(),
972 mActiveEntry
= newActiveEntry
;
974 shistory
->InternalSetRequestedIndex(indexOfHistoryLoad
);
975 // FIXME UpdateIndex() here may update index too early (but even the
976 // old implementation seems to have similar issues).
977 shistory
->UpdateIndex();
978 } else if (addEntry
) {
980 if (LOAD_TYPE_HAS_FLAGS(
981 aLoadType
, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY
) ||
982 (LOAD_TYPE_HAS_FLAGS(aLoadType
,
983 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH
) &&
984 !ShouldAddEntryForRefresh(newActiveEntry
))) {
985 // FIXME We need to make sure that when we create the info we
986 // make a copy of the shared state.
987 mActiveEntry
->ReplaceWith(*newActiveEntry
);
989 // AddChildSHEntryHelper does update the index of the session
991 shistory
->AddChildSHEntryHelper(mActiveEntry
, newActiveEntry
,
992 Top(), aCloneEntryChildren
);
993 mActiveEntry
= newActiveEntry
;
996 SessionHistoryEntry
* parentEntry
= GetParent()->mActiveEntry
;
997 // XXX What should happen if parent doesn't have mActiveEntry?
998 // Or can that even happen ever?
1000 mActiveEntry
= newActiveEntry
;
1001 // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite
1002 // right, but aUseRemoteSubframes should be going away.
1003 parentEntry
->AddChild(
1005 CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
1009 shistory
->InternalSetRequestedIndex(-1);
1013 ResetSHEntryHasUserInteractionCache();
1015 HistoryCommitIndexAndLength(aChangeID
, caller
);
1017 shistory
->LogHistory();
1021 // XXX Should the loading entries before [i] be removed?
1023 // FIXME Should we throw an error if we don't find an entry for
1024 // aSessionHistoryEntryId?
1027 already_AddRefed
<nsDocShellLoadState
> CanonicalBrowsingContext::CreateLoadInfo(
1028 SessionHistoryEntry
* aEntry
) {
1029 const SessionHistoryInfo
& info
= aEntry
->Info();
1030 RefPtr
<nsDocShellLoadState
> loadState(new nsDocShellLoadState(info
.GetURI()));
1031 info
.FillLoadInfo(*loadState
);
1032 UniquePtr
<LoadingSessionHistoryInfo
> loadingInfo
;
1033 loadingInfo
= MakeUnique
<LoadingSessionHistoryInfo
>(aEntry
);
1034 mLoadingEntries
.AppendElement(
1035 LoadingSessionHistoryEntry
{loadingInfo
->mLoadId
, aEntry
});
1036 loadState
->SetLoadingSessionHistoryInfo(std::move(loadingInfo
));
1038 return loadState
.forget();
1041 void CanonicalBrowsingContext::NotifyOnHistoryReload(
1042 bool aForceReload
, bool& aCanReload
,
1043 Maybe
<NotNull
<RefPtr
<nsDocShellLoadState
>>>& aLoadState
,
1044 Maybe
<bool>& aReloadActiveEntry
) {
1045 MOZ_DIAGNOSTIC_ASSERT(!aLoadState
);
1048 nsISHistory
* shistory
= GetSessionHistory();
1049 NS_ENSURE_TRUE_VOID(shistory
);
1051 shistory
->NotifyOnHistoryReload(&aCanReload
);
1057 aLoadState
.emplace(WrapMovingNotNull(RefPtr
{CreateLoadInfo(mActiveEntry
)}));
1058 aReloadActiveEntry
.emplace(true);
1060 shistory
->RemoveFrameEntries(mActiveEntry
);
1062 } else if (!mLoadingEntries
.IsEmpty()) {
1063 const LoadingSessionHistoryEntry
& loadingEntry
=
1064 mLoadingEntries
.LastElement();
1065 uint64_t loadId
= loadingEntry
.mLoadId
;
1067 WrapMovingNotNull(RefPtr
{CreateLoadInfo(loadingEntry
.mEntry
)}));
1068 aReloadActiveEntry
.emplace(false);
1070 SessionHistoryEntry::LoadingEntry
* entry
=
1071 SessionHistoryEntry::GetByLoadId(loadId
);
1073 shistory
->RemoveFrameEntries(entry
->mEntry
);
1079 // Use 0 as the offset, since aLoadState will be be used for reload.
1080 aLoadState
.ref()->SetLoadIsFromSessionHistory(0,
1081 aReloadActiveEntry
.value());
1083 // If we don't have an active entry and we don't have a loading entry then
1084 // the nsDocShell will create a load state based on its document.
1087 void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
1088 const Maybe
<nsPoint
>& aPreviousScrollPos
, SessionHistoryInfo
* aInfo
,
1089 uint32_t aLoadType
, uint32_t aUpdatedCacheKey
, const nsID
& aChangeID
) {
1090 nsISHistory
* shistory
= GetSessionHistory();
1094 CallerWillNotifyHistoryIndexAndLengthChanges
caller(shistory
);
1096 RefPtr
<SessionHistoryEntry
> oldActiveEntry
= mActiveEntry
;
1097 if (aPreviousScrollPos
.isSome() && oldActiveEntry
) {
1098 oldActiveEntry
->SetScrollPosition(aPreviousScrollPos
.ref().x
,
1099 aPreviousScrollPos
.ref().y
);
1101 mActiveEntry
= new SessionHistoryEntry(aInfo
);
1102 mActiveEntry
->SetDocshellID(GetHistoryID());
1103 mActiveEntry
->AdoptBFCacheEntry(oldActiveEntry
);
1104 if (aUpdatedCacheKey
!= 0) {
1105 mActiveEntry
->SharedInfo()->mCacheKey
= aUpdatedCacheKey
;
1109 Maybe
<int32_t> previousEntryIndex
, loadedEntryIndex
;
1110 shistory
->AddToRootSessionHistory(
1111 true, oldActiveEntry
, this, mActiveEntry
, aLoadType
,
1112 nsDocShell::ShouldAddToSessionHistory(aInfo
->GetURI(), nullptr),
1113 &previousEntryIndex
, &loadedEntryIndex
);
1115 if (oldActiveEntry
) {
1116 shistory
->AddChildSHEntryHelper(oldActiveEntry
, mActiveEntry
, Top(),
1118 } else if (GetParent() && GetParent()->mActiveEntry
) {
1119 GetParent()->mActiveEntry
->AddChild(
1120 mActiveEntry
, CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
1121 UseRemoteSubframes());
1125 ResetSHEntryHasUserInteractionCache();
1127 shistory
->InternalSetRequestedIndex(-1);
1129 // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry.
1130 HistoryCommitIndexAndLength(aChangeID
, caller
);
1132 static_cast<nsSHistory
*>(shistory
)->LogHistory();
1135 void CanonicalBrowsingContext::ReplaceActiveSessionHistoryEntry(
1136 SessionHistoryInfo
* aInfo
) {
1137 if (!mActiveEntry
) {
1141 mActiveEntry
->SetInfo(aInfo
);
1142 // Notify children of the update
1143 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
1145 shistory
->NotifyOnHistoryReplaceEntry();
1146 shistory
->UpdateRootBrowsingContextState();
1149 ResetSHEntryHasUserInteractionCache();
1152 mActiveEntry
->SetWireframe(Nothing());
1155 // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry.
1158 void CanonicalBrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() {
1159 nsISHistory
* shistory
= GetSessionHistory();
1160 // In theory shistory can be null here if the method is called right after
1161 // CanonicalBrowsingContext::ReplacedBy call.
1162 NS_ENSURE_TRUE_VOID(shistory
);
1163 nsCOMPtr
<nsISHEntry
> root
= nsSHistory::GetRootSHEntry(mActiveEntry
);
1164 shistory
->RemoveDynEntries(shistory
->GetIndexOfEntry(root
), mActiveEntry
);
1167 void CanonicalBrowsingContext::RemoveFromSessionHistory(const nsID
& aChangeID
) {
1168 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
1170 CallerWillNotifyHistoryIndexAndLengthChanges
caller(shistory
);
1171 nsCOMPtr
<nsISHEntry
> root
= nsSHistory::GetRootSHEntry(mActiveEntry
);
1173 AutoTArray
<nsID
, 16> ids({GetHistoryID()});
1174 shistory
->RemoveEntries(ids
, shistory
->GetIndexOfEntry(root
), &didRemove
);
1176 RefPtr
<BrowsingContext
> rootBC
= shistory
->GetBrowsingContext();
1178 if (!rootBC
->IsInProcess()) {
1179 if (ContentParent
* cp
= rootBC
->Canonical()->GetContentParent()) {
1180 Unused
<< cp
->SendDispatchLocationChangeEvent(rootBC
);
1182 } else if (rootBC
->GetDocShell()) {
1183 rootBC
->GetDocShell()->DispatchLocationChangeEvent();
1187 HistoryCommitIndexAndLength(aChangeID
, caller
);
1191 Maybe
<int32_t> CanonicalBrowsingContext::HistoryGo(
1192 int32_t aOffset
, uint64_t aHistoryEpoch
, bool aRequireUserInteraction
,
1193 bool aUserActivation
, Maybe
<ContentParentId
> aContentId
) {
1194 if (aRequireUserInteraction
&& aOffset
!= -1 && aOffset
!= 1) {
1196 "aRequireUserInteraction may only be used with an offset of -1 or 1");
1200 nsSHistory
* shistory
= static_cast<nsSHistory
*>(GetSessionHistory());
1205 CheckedInt
<int32_t> index
= shistory
->GetRequestedIndex() >= 0
1206 ? shistory
->GetRequestedIndex()
1207 : shistory
->Index();
1208 MOZ_LOG(gSHLog
, LogLevel::Debug
,
1209 ("HistoryGo(%d->%d) epoch %" PRIu64
"/id %" PRIu64
, aOffset
,
1210 (index
+ aOffset
).value(), aHistoryEpoch
,
1211 (uint64_t)(aContentId
.isSome() ? aContentId
.value() : 0)));
1215 if (!index
.isValid()) {
1216 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Invalid index"));
1220 // Check for user interaction if desired, except for the first and last
1221 // history entries. We compare with >= to account for the case where
1222 // aOffset >= length.
1223 if (!aRequireUserInteraction
|| index
.value() >= shistory
->Length() - 1 ||
1224 index
.value() <= 0) {
1227 if (shistory
->HasUserInteractionAtIndex(index
.value())) {
1232 // Implement aborting additional history navigations from within the same
1233 // event spin of the content process.
1236 bool sameEpoch
= false;
1237 Maybe
<ContentParentId
> id
;
1238 shistory
->GetEpoch(epoch
, id
);
1240 if (aContentId
== id
&& epoch
>= aHistoryEpoch
) {
1242 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Same epoch/id"));
1244 // Don't update the epoch until we know if the target index is valid
1246 // GoToIndex checks that index is >= 0 and < length.
1247 nsTArray
<nsSHistory::LoadEntryResult
> loadResults
;
1248 nsresult rv
= shistory
->GotoIndex(index
.value(), loadResults
, sameEpoch
,
1249 aOffset
== 0, aUserActivation
);
1250 if (NS_FAILED(rv
)) {
1251 MOZ_LOG(gSHLog
, LogLevel::Debug
,
1252 ("Dropping HistoryGo - bad index or same epoch (not in same doc)"));
1255 if (epoch
< aHistoryEpoch
|| aContentId
!= id
) {
1256 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Set epoch"));
1257 shistory
->SetEpoch(aHistoryEpoch
, aContentId
);
1259 int32_t requestedIndex
= shistory
->GetRequestedIndex();
1260 nsSHistory::LoadURIs(loadResults
);
1261 return Some(requestedIndex
);
1264 JSObject
* CanonicalBrowsingContext::WrapObject(
1265 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
1266 return CanonicalBrowsingContext_Binding::Wrap(aCx
, this, aGivenProto
);
1269 void CanonicalBrowsingContext::DispatchWheelZoomChange(bool aIncrease
) {
1270 Element
* element
= Top()->GetEmbedderElement();
1275 auto event
= aIncrease
? u
"DoZoomEnlargeBy10"_ns
: u
"DoZoomReduceBy10"_ns
;
1276 auto dispatcher
= MakeRefPtr
<AsyncEventDispatcher
>(
1277 element
, event
, CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
1278 dispatcher
->PostDOMEvent();
1281 void CanonicalBrowsingContext::CanonicalDiscard() {
1282 if (mTabMediaController
) {
1283 mTabMediaController
->Shutdown();
1284 mTabMediaController
= nullptr;
1288 mCurrentLoad
->Cancel(NS_BINDING_ABORTED
,
1289 "CanonicalBrowsingContext::CanonicalDiscard"_ns
);
1293 RefPtr
<BrowsingContextWebProgress
> progress
= mWebProgress
;
1294 progress
->ContextDiscarded();
1298 BackgroundSessionStorageManager::RemoveManager(Id());
1301 CancelSessionStoreUpdate();
1303 if (UsePrivateBrowsing() && EverAttached() && IsContent()) {
1304 DecreasePrivateCount();
1308 void CanonicalBrowsingContext::CanonicalAttach() {
1309 if (UsePrivateBrowsing() && IsContent()) {
1310 IncreasePrivateCount();
1314 void CanonicalBrowsingContext::AddPendingDiscard() {
1315 MOZ_ASSERT(!mFullyDiscarded
);
1319 void CanonicalBrowsingContext::RemovePendingDiscard() {
1321 if (!mPendingDiscards
) {
1322 mFullyDiscarded
= true;
1323 auto listeners
= std::move(mFullyDiscardedListeners
);
1324 for (const auto& listener
: listeners
) {
1330 void CanonicalBrowsingContext::AddFinalDiscardListener(
1331 std::function
<void(uint64_t)>&& aListener
) {
1332 if (mFullyDiscarded
) {
1336 mFullyDiscardedListeners
.AppendElement(std::move(aListener
));
1339 void CanonicalBrowsingContext::SetForceAppWindowActive(bool aForceActive
,
1341 MOZ_DIAGNOSTIC_ASSERT(IsChrome());
1342 MOZ_DIAGNOSTIC_ASSERT(IsTop());
1343 if (!IsChrome() || !IsTop()) {
1344 return aRv
.ThrowNotAllowedError(
1345 "You shouldn't need to force this BrowsingContext to be active, use "
1346 ".isActive instead");
1348 if (mForceAppWindowActive
== aForceActive
) {
1351 mForceAppWindowActive
= aForceActive
;
1352 RecomputeAppWindowVisibility();
1355 void CanonicalBrowsingContext::RecomputeAppWindowVisibility() {
1356 MOZ_RELEASE_ASSERT(IsChrome());
1357 MOZ_RELEASE_ASSERT(IsTop());
1359 const bool wasAlreadyActive
= IsActive();
1361 nsCOMPtr
<nsIWidget
> widget
;
1362 if (auto* docShell
= GetDocShell()) {
1363 nsDocShell::Cast(docShell
)->GetMainWidget(getter_AddRefs(widget
));
1366 Unused
<< NS_WARN_IF(!widget
);
1367 const bool isNowActive
=
1368 ForceAppWindowActive() || (widget
&& !widget
->IsFullyOccluded() &&
1369 widget
->SizeMode() != nsSizeMode_Minimized
);
1371 if (isNowActive
== wasAlreadyActive
) {
1375 SetIsActiveInternal(isNowActive
, IgnoreErrors());
1377 // Pause if we are not active, resume if we are active.
1378 widget
->PauseOrResumeCompositor(!isNowActive
);
1382 void CanonicalBrowsingContext::AdjustPrivateBrowsingCount(
1383 bool aPrivateBrowsing
) {
1384 if (IsDiscarded() || !EverAttached() || IsChrome()) {
1388 MOZ_DIAGNOSTIC_ASSERT(aPrivateBrowsing
== UsePrivateBrowsing());
1389 if (aPrivateBrowsing
) {
1390 IncreasePrivateCount();
1392 DecreasePrivateCount();
1396 void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
1397 WindowContext
* windowContext
= GetCurrentWindowContext();
1398 if (!windowContext
) {
1402 // As this function would only be called when user click the play icon on the
1403 // tab bar. That's clear user intent to play, so gesture activate the window
1404 // context so that the block-autoplay logic allows the media to autoplay.
1405 windowContext
->NotifyUserGestureActivation();
1406 AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64
,
1408 StartDelayedAutoplayMediaComponents();
1409 // Notfiy all content browsing contexts which are related with the canonical
1410 // browsing content tree to start delayed autoplay media.
1412 Group()->EachParent([&](ContentParent
* aParent
) {
1413 Unused
<< aParent
->SendStartDelayedAutoplayMediaComponents(this);
1417 void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted
,
1419 MOZ_ASSERT(!GetParent(),
1420 "Notify media mute change on non top-level context!");
1421 SetMuted(aMuted
, aRv
);
1424 uint32_t CanonicalBrowsingContext::CountSiteOrigins(
1425 GlobalObject
& aGlobal
,
1426 const Sequence
<OwningNonNull
<BrowsingContext
>>& aRoots
) {
1427 nsTHashSet
<nsCString
> uniqueSiteOrigins
;
1429 for (const auto& root
: aRoots
) {
1430 root
->PreOrderWalk([&](BrowsingContext
* aContext
) {
1431 WindowGlobalParent
* windowGlobalParent
=
1432 aContext
->Canonical()->GetCurrentWindowGlobal();
1433 if (windowGlobalParent
) {
1434 nsIPrincipal
* documentPrincipal
=
1435 windowGlobalParent
->DocumentPrincipal();
1437 bool isContentPrincipal
= documentPrincipal
->GetIsContentPrincipal();
1438 if (isContentPrincipal
) {
1439 nsCString siteOrigin
;
1440 documentPrincipal
->GetSiteOrigin(siteOrigin
);
1441 uniqueSiteOrigins
.Insert(siteOrigin
);
1447 return uniqueSiteOrigins
.Count();
1451 bool CanonicalBrowsingContext::IsPrivateBrowsingActive() {
1452 return gNumberOfPrivateContexts
> 0;
1455 void CanonicalBrowsingContext::UpdateMediaControlAction(
1456 const MediaControlAction
& aAction
) {
1457 if (IsDiscarded()) {
1460 ContentMediaControlKeyHandler::HandleMediaControlAction(this, aAction
);
1461 Group()->EachParent([&](ContentParent
* aParent
) {
1462 Unused
<< aParent
->SendUpdateMediaControlAction(this, aAction
);
1466 void CanonicalBrowsingContext::LoadURI(nsIURI
* aURI
,
1467 const LoadURIOptions
& aOptions
,
1468 ErrorResult
& aError
) {
1469 RefPtr
<nsDocShellLoadState
> loadState
;
1470 nsresult rv
= nsDocShellLoadState::CreateFromLoadURIOptions(
1471 this, aURI
, aOptions
, getter_AddRefs(loadState
));
1472 MOZ_ASSERT(rv
!= NS_ERROR_MALFORMED_URI
);
1474 if (NS_FAILED(rv
)) {
1479 LoadURI(loadState
, true);
1482 void CanonicalBrowsingContext::FixupAndLoadURIString(
1483 const nsAString
& aURI
, const LoadURIOptions
& aOptions
,
1484 ErrorResult
& aError
) {
1485 RefPtr
<nsDocShellLoadState
> loadState
;
1486 nsresult rv
= nsDocShellLoadState::CreateFromLoadURIOptions(
1487 this, aURI
, aOptions
, getter_AddRefs(loadState
));
1489 if (rv
== NS_ERROR_MALFORMED_URI
) {
1490 DisplayLoadError(aURI
);
1494 if (NS_FAILED(rv
)) {
1499 LoadURI(loadState
, true);
1502 void CanonicalBrowsingContext::GoBack(
1503 const Optional
<int32_t>& aCancelContentJSEpoch
,
1504 bool aRequireUserInteraction
, bool aUserActivation
) {
1505 if (IsDiscarded()) {
1509 // Stop any known network loads if necessary.
1511 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
, ""_ns
);
1514 if (RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(GetDocShell())) {
1515 if (aCancelContentJSEpoch
.WasPassed()) {
1516 docShell
->SetCancelContentJSEpoch(aCancelContentJSEpoch
.Value());
1518 docShell
->GoBack(aRequireUserInteraction
, aUserActivation
);
1519 } else if (ContentParent
* cp
= GetContentParent()) {
1520 Maybe
<int32_t> cancelContentJSEpoch
;
1521 if (aCancelContentJSEpoch
.WasPassed()) {
1522 cancelContentJSEpoch
= Some(aCancelContentJSEpoch
.Value());
1524 Unused
<< cp
->SendGoBack(this, cancelContentJSEpoch
,
1525 aRequireUserInteraction
, aUserActivation
);
1528 void CanonicalBrowsingContext::GoForward(
1529 const Optional
<int32_t>& aCancelContentJSEpoch
,
1530 bool aRequireUserInteraction
, bool aUserActivation
) {
1531 if (IsDiscarded()) {
1535 // Stop any known network loads if necessary.
1537 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
, ""_ns
);
1540 if (RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(GetDocShell())) {
1541 if (aCancelContentJSEpoch
.WasPassed()) {
1542 docShell
->SetCancelContentJSEpoch(aCancelContentJSEpoch
.Value());
1544 docShell
->GoForward(aRequireUserInteraction
, aUserActivation
);
1545 } else if (ContentParent
* cp
= GetContentParent()) {
1546 Maybe
<int32_t> cancelContentJSEpoch
;
1547 if (aCancelContentJSEpoch
.WasPassed()) {
1548 cancelContentJSEpoch
.emplace(aCancelContentJSEpoch
.Value());
1550 Unused
<< cp
->SendGoForward(this, cancelContentJSEpoch
,
1551 aRequireUserInteraction
, aUserActivation
);
1554 void CanonicalBrowsingContext::GoToIndex(
1555 int32_t aIndex
, const Optional
<int32_t>& aCancelContentJSEpoch
,
1556 bool aUserActivation
) {
1557 if (IsDiscarded()) {
1561 // Stop any known network loads if necessary.
1563 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
, ""_ns
);
1566 if (RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(GetDocShell())) {
1567 if (aCancelContentJSEpoch
.WasPassed()) {
1568 docShell
->SetCancelContentJSEpoch(aCancelContentJSEpoch
.Value());
1570 docShell
->GotoIndex(aIndex
, aUserActivation
);
1571 } else if (ContentParent
* cp
= GetContentParent()) {
1572 Maybe
<int32_t> cancelContentJSEpoch
;
1573 if (aCancelContentJSEpoch
.WasPassed()) {
1574 cancelContentJSEpoch
.emplace(aCancelContentJSEpoch
.Value());
1576 Unused
<< cp
->SendGoToIndex(this, aIndex
, cancelContentJSEpoch
,
1580 void CanonicalBrowsingContext::Reload(uint32_t aReloadFlags
) {
1581 if (IsDiscarded()) {
1585 // Stop any known network loads if necessary.
1587 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
, ""_ns
);
1590 if (RefPtr
<nsDocShell
> docShell
= nsDocShell::Cast(GetDocShell())) {
1591 docShell
->Reload(aReloadFlags
);
1592 } else if (ContentParent
* cp
= GetContentParent()) {
1593 Unused
<< cp
->SendReload(this, aReloadFlags
);
1597 void CanonicalBrowsingContext::Stop(uint32_t aStopFlags
) {
1598 if (IsDiscarded()) {
1602 // Stop any known network loads if necessary.
1603 if (mCurrentLoad
&& (aStopFlags
& nsIWebNavigation::STOP_NETWORK
)) {
1604 mCurrentLoad
->Cancel(NS_BINDING_ABORTED
,
1605 "CanonicalBrowsingContext::Stop"_ns
);
1608 // Ask the docshell to stop to handle loads that haven't
1609 // yet reached here, as well as non-network activity.
1610 if (auto* docShell
= nsDocShell::Cast(GetDocShell())) {
1611 docShell
->Stop(aStopFlags
);
1612 } else if (ContentParent
* cp
= GetContentParent()) {
1613 Unused
<< cp
->SendStopLoad(this, aStopFlags
);
1617 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() {
1622 if (mContentParent
) {
1623 // If our new content process is still unloading from a previous process
1624 // switch, wait for that unload to complete before continuing.
1625 auto found
= mTarget
->FindUnloadingHost(mContentParent
->ChildID());
1626 if (found
!= mTarget
->mUnloadingHosts
.end()) {
1627 found
->mCallbacks
.AppendElement(
1628 [self
= RefPtr
{this}]() { self
->ProcessReady(); });
1636 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() {
1641 MOZ_ASSERT(!mProcessReady
);
1642 mProcessReady
= true;
1646 void CanonicalBrowsingContext::PendingRemotenessChange::MaybeFinish() {
1651 if (!mProcessReady
|| mWaitingForPrepareToChange
) {
1655 // If this BrowsingContext is embedded within the parent process, perform the
1656 // process switch directly.
1657 nsresult rv
= mTarget
->IsTopContent() ? FinishTopContent() : FinishSubframe();
1658 if (NS_FAILED(rv
)) {
1659 NS_WARNING("Error finishing PendingRemotenessChange!");
1666 // Logic for finishing a toplevel process change embedded within the parent
1667 // process. Due to frontend integration the logic differs substantially from
1668 // subframe process switches, and is handled separately.
1669 nsresult
CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() {
1670 MOZ_DIAGNOSTIC_ASSERT(mTarget
->IsTop(),
1671 "We shouldn't be trying to change the remoteness of "
1672 "non-remote iframes");
1674 // Abort if our ContentParent died while process switching.
1675 if (mContentParent
&& NS_WARN_IF(mContentParent
->IsShuttingDown())) {
1676 return NS_ERROR_FAILURE
;
1679 // While process switching, we need to check if any of our ancestors are
1680 // discarded or no longer current, in which case the process switch needs to
1682 RefPtr
<CanonicalBrowsingContext
> target(mTarget
);
1683 if (target
->IsDiscarded() || !target
->AncestorsAreCurrent()) {
1684 return NS_ERROR_FAILURE
;
1687 Element
* browserElement
= target
->GetEmbedderElement();
1688 if (!browserElement
) {
1689 return NS_ERROR_FAILURE
;
1692 nsCOMPtr
<nsIBrowser
> browser
= browserElement
->AsBrowser();
1694 return NS_ERROR_FAILURE
;
1697 RefPtr
<nsFrameLoaderOwner
> frameLoaderOwner
= do_QueryObject(browserElement
);
1698 MOZ_RELEASE_ASSERT(frameLoaderOwner
,
1699 "embedder browser must be nsFrameLoaderOwner");
1701 // If we're process switching a browsing context in private browsing
1702 // mode we might decrease the private browsing count to '0', which
1703 // would make us fire "last-pb-context-exited" and drop the private
1704 // session. To prevent that we artificially increment the number of
1705 // private browsing contexts with '1' until the process switch is done.
1706 bool usePrivateBrowsing
= mTarget
->UsePrivateBrowsing();
1707 if (usePrivateBrowsing
) {
1708 IncreasePrivateCount();
1711 auto restorePrivateCount
= MakeScopeExit([usePrivateBrowsing
]() {
1712 if (usePrivateBrowsing
) {
1713 DecreasePrivateCount();
1717 // Tell frontend code that this browser element is about to change process.
1718 nsresult rv
= browser
->BeforeChangeRemoteness();
1719 if (NS_FAILED(rv
)) {
1723 // Some frontend code checks the value of the `remote` attribute on the
1724 // browser to determine if it is remote, so update the value.
1725 browserElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::remote
,
1726 mContentParent
? u
"true"_ns
: u
"false"_ns
,
1729 // The process has been created, hand off to nsFrameLoaderOwner to finish
1730 // the process switch.
1732 frameLoaderOwner
->ChangeRemotenessToProcess(mContentParent
, mOptions
,
1733 mSpecificGroup
, error
);
1734 if (error
.Failed()) {
1735 return error
.StealNSResult();
1738 // Tell frontend the load is done.
1739 bool loadResumed
= false;
1740 rv
= browser
->FinishChangeRemoteness(mPendingSwitchId
, &loadResumed
);
1741 if (NS_WARN_IF(NS_FAILED(rv
))) {
1745 // We did it! The process switch is complete.
1746 RefPtr
<nsFrameLoader
> frameLoader
= frameLoaderOwner
->GetFrameLoader();
1747 RefPtr
<BrowserParent
> newBrowser
= frameLoader
->GetBrowserParent();
1749 if (mContentParent
) {
1750 // Failed to create the BrowserParent somehow! Abort the process switch
1752 return NS_ERROR_UNEXPECTED
;
1756 RefPtr
<nsDocShell
> newDocShell
= frameLoader
->GetDocShell(error
);
1757 if (error
.Failed()) {
1758 return error
.StealNSResult();
1761 rv
= newDocShell
->ResumeRedirectedLoad(mPendingSwitchId
,
1762 /* aHistoryIndex */ -1);
1763 if (NS_FAILED(rv
)) {
1767 } else if (!loadResumed
) {
1768 newBrowser
->ResumeLoad(mPendingSwitchId
);
1771 mPromise
->Resolve(newBrowser
, __func__
);
1775 nsresult
CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() {
1776 MOZ_DIAGNOSTIC_ASSERT(!mOptions
.mReplaceBrowsingContext
,
1777 "Cannot replace BC for subframe");
1778 MOZ_DIAGNOSTIC_ASSERT(!mTarget
->IsTop());
1780 // While process switching, we need to check if any of our ancestors are
1781 // discarded or no longer current, in which case the process switch needs to
1783 RefPtr
<CanonicalBrowsingContext
> target(mTarget
);
1784 if (target
->IsDiscarded() || !target
->AncestorsAreCurrent()) {
1785 return NS_ERROR_FAILURE
;
1788 if (NS_WARN_IF(!mContentParent
)) {
1789 return NS_ERROR_FAILURE
;
1792 RefPtr
<WindowGlobalParent
> embedderWindow
= target
->GetParentWindowContext();
1793 if (NS_WARN_IF(!embedderWindow
) || NS_WARN_IF(!embedderWindow
->CanSend())) {
1794 return NS_ERROR_FAILURE
;
1797 RefPtr
<BrowserParent
> embedderBrowser
= embedderWindow
->GetBrowserParent();
1798 if (NS_WARN_IF(!embedderBrowser
)) {
1799 return NS_ERROR_FAILURE
;
1802 // If we're creating a new remote browser, and the host process is already
1803 // dead, abort the process switch.
1804 if (mContentParent
!= embedderBrowser
->Manager() &&
1805 NS_WARN_IF(mContentParent
->IsShuttingDown())) {
1806 return NS_ERROR_FAILURE
;
1809 RefPtr
<BrowserParent
> oldBrowser
= target
->GetBrowserParent();
1810 target
->SetCurrentBrowserParent(nullptr);
1812 // If we were in a remote frame, trigger unloading of the remote window. The
1813 // previous BrowserParent is registered in `mUnloadingHosts` and will only be
1814 // cleared when the BrowserParent is fully destroyed.
1815 bool wasRemote
= oldBrowser
&& oldBrowser
->GetBrowsingContext() == target
;
1817 MOZ_DIAGNOSTIC_ASSERT(oldBrowser
!= embedderBrowser
);
1818 MOZ_DIAGNOSTIC_ASSERT(oldBrowser
->IsDestroyed() ||
1819 oldBrowser
->GetBrowserBridgeParent());
1821 // `oldBrowser` will clear the `UnloadingHost` status once the actor has
1823 if (oldBrowser
->CanSend()) {
1824 target
->StartUnloadingHost(oldBrowser
->Manager()->ChildID());
1825 Unused
<< oldBrowser
->SendWillChangeProcess();
1826 oldBrowser
->Destroy();
1830 // Update which process is considered the current owner
1831 target
->SetOwnerProcessId(mContentParent
->ChildID());
1833 // If we're switching from remote to local, we don't need to create a
1834 // BrowserBridge, and can instead perform the switch directly.
1835 if (mContentParent
== embedderBrowser
->Manager()) {
1836 MOZ_DIAGNOSTIC_ASSERT(
1838 "We always have a PendingSwitchId, except for print-preview loads, "
1839 "which will never perform a process-switch to being in-process with "
1841 MOZ_DIAGNOSTIC_ASSERT(wasRemote
,
1842 "Attempt to process-switch from local to local?");
1844 target
->SetCurrentBrowserParent(embedderBrowser
);
1845 Unused
<< embedderWindow
->SendMakeFrameLocal(target
, mPendingSwitchId
);
1846 mPromise
->Resolve(embedderBrowser
, __func__
);
1850 // The BrowsingContext will be remote, either as an already-remote frame
1851 // changing processes, or as a local frame becoming remote. Construct a new
1852 // BrowserBridgeParent to host the remote content.
1853 target
->SetCurrentBrowserParent(nullptr);
1855 MOZ_DIAGNOSTIC_ASSERT(target
->UseRemoteTabs() && target
->UseRemoteSubframes(),
1856 "Not supported without fission");
1857 uint32_t chromeFlags
= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW
|
1858 nsIWebBrowserChrome::CHROME_FISSION_WINDOW
;
1859 if (target
->UsePrivateBrowsing()) {
1860 chromeFlags
|= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW
;
1863 nsCOMPtr
<nsIPrincipal
> initialPrincipal
=
1864 NullPrincipal::Create(target
->OriginAttributesRef());
1865 WindowGlobalInit windowInit
=
1866 WindowGlobalActor::AboutBlankInitializer(target
, initialPrincipal
);
1868 // Create and initialize our new BrowserBridgeParent.
1869 TabId
tabId(nsContentUtils::GenerateTabId());
1870 RefPtr
<BrowserBridgeParent
> bridge
= new BrowserBridgeParent();
1871 nsresult rv
= bridge
->InitWithProcess(embedderBrowser
, mContentParent
,
1872 windowInit
, chromeFlags
, tabId
);
1873 if (NS_WARN_IF(NS_FAILED(rv
))) {
1874 // If we've already destroyed our previous document, make a best-effort
1875 // attempt to recover from this failure and show the crashed tab UI. We only
1876 // do this in the previously-remote case, as previously in-process frames
1877 // will have their navigation cancelled, and will remain visible.
1879 target
->ShowSubframeCrashedUI(oldBrowser
->GetBrowserBridgeParent());
1884 // Tell the embedder process a remoteness change is in-process. When this is
1885 // acknowledged, reset the in-flight ID if it used to be an in-process load.
1886 RefPtr
<BrowserParent
> newBrowser
= bridge
->GetBrowserParent();
1888 // If we weren't remote, mark our embedder window browser as unloading until
1889 // our embedder process has acked our MakeFrameRemote message.
1890 Maybe
<uint64_t> clearChildID
;
1892 clearChildID
= Some(embedderBrowser
->Manager()->ChildID());
1893 target
->StartUnloadingHost(*clearChildID
);
1895 auto callback
= [target
, clearChildID
](auto&&) {
1897 target
->ClearUnloadingHost(*clearChildID
);
1901 ManagedEndpoint
<PBrowserBridgeChild
> endpoint
=
1902 embedderBrowser
->OpenPBrowserBridgeEndpoint(bridge
);
1903 MOZ_DIAGNOSTIC_ASSERT(endpoint
.IsValid());
1904 embedderWindow
->SendMakeFrameRemote(target
, std::move(endpoint
), tabId
,
1905 newBrowser
->GetLayersId(), callback
,
1909 // Resume the pending load in our new process.
1910 if (mPendingSwitchId
) {
1911 newBrowser
->ResumeLoad(mPendingSwitchId
);
1914 // We did it! The process switch is complete.
1915 mPromise
->Resolve(newBrowser
, __func__
);
1919 void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv
) {
1924 mPromise
->Reject(aRv
, __func__
);
1928 void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
1929 // Make sure we don't die while we're doing cleanup.
1930 RefPtr
<PendingRemotenessChange
> kungFuDeathGrip(this);
1932 MOZ_DIAGNOSTIC_ASSERT(mTarget
->mPendingRemotenessChange
== this);
1933 mTarget
->mPendingRemotenessChange
= nullptr;
1936 // When this PendingRemotenessChange was created, it was given a
1937 // `mContentParent`.
1938 if (mContentParent
) {
1939 mContentParent
->RemoveKeepAlive();
1940 mContentParent
= nullptr;
1943 // If we were given a specific group, stop keeping that group alive manually.
1944 if (mSpecificGroup
) {
1945 mSpecificGroup
->RemoveKeepAlive();
1946 mSpecificGroup
= nullptr;
1953 CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange(
1954 CanonicalBrowsingContext
* aTarget
, RemotenessPromise::Private
* aPromise
,
1955 uint64_t aPendingSwitchId
, const NavigationIsolationOptions
& aOptions
)
1958 mPendingSwitchId(aPendingSwitchId
),
1959 mOptions(aOptions
) {}
1961 CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() {
1962 MOZ_ASSERT(!mPromise
&& !mTarget
&& !mContentParent
&& !mSpecificGroup
,
1963 "should've already been Cancel() or Complete()-ed");
1966 BrowserParent
* CanonicalBrowsingContext::GetBrowserParent() const {
1967 return mCurrentBrowserParent
;
1970 void CanonicalBrowsingContext::SetCurrentBrowserParent(
1971 BrowserParent
* aBrowserParent
) {
1972 MOZ_DIAGNOSTIC_ASSERT(!mCurrentBrowserParent
|| !aBrowserParent
,
1973 "BrowsingContext already has a current BrowserParent!");
1974 MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent
, aBrowserParent
->CanSend());
1975 MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent
,
1976 aBrowserParent
->Manager()->ChildID() == mProcessId
);
1978 // BrowserParent must either be directly for this BrowsingContext, or the
1979 // manager out our embedder WindowGlobal.
1980 MOZ_DIAGNOSTIC_ASSERT_IF(
1981 aBrowserParent
&& aBrowserParent
->GetBrowsingContext() != this,
1982 GetParentWindowContext() &&
1983 GetParentWindowContext()->Manager() == aBrowserParent
);
1985 if (aBrowserParent
&& IsTopContent() && !ManuallyManagesActiveness()) {
1986 aBrowserParent
->SetRenderLayers(IsActive());
1989 mCurrentBrowserParent
= aBrowserParent
;
1992 bool CanonicalBrowsingContext::ManuallyManagesActiveness() const {
1993 auto* el
= GetEmbedderElement();
1994 return el
&& el
->IsXULElement() && el
->HasAttr(nsGkAtoms::manualactiveness
);
1997 RefPtr
<CanonicalBrowsingContext::RemotenessPromise
>
1998 CanonicalBrowsingContext::ChangeRemoteness(
1999 const NavigationIsolationOptions
& aOptions
, uint64_t aPendingSwitchId
) {
2000 MOZ_DIAGNOSTIC_ASSERT(IsContent(),
2001 "cannot change the process of chrome contexts");
2002 MOZ_DIAGNOSTIC_ASSERT(
2003 IsTop() == IsEmbeddedInProcess(0),
2004 "toplevel content must be embedded in the parent process");
2005 MOZ_DIAGNOSTIC_ASSERT(!aOptions
.mReplaceBrowsingContext
|| IsTop(),
2006 "Cannot replace BrowsingContext for subframes");
2007 MOZ_DIAGNOSTIC_ASSERT(
2008 aOptions
.mSpecificGroupId
== 0 || aOptions
.mReplaceBrowsingContext
,
2009 "Cannot specify group ID unless replacing BC");
2010 MOZ_DIAGNOSTIC_ASSERT(aPendingSwitchId
|| !IsTop(),
2011 "Should always have aPendingSwitchId for top-level "
2014 if (!AncestorsAreCurrent()) {
2015 NS_WARNING("An ancestor context is no longer current");
2016 return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
2019 // Ensure our embedder hasn't been destroyed or asked to shutdown already.
2020 RefPtr
<WindowGlobalParent
> embedderWindowGlobal
= GetEmbedderWindowGlobal();
2021 if (!embedderWindowGlobal
) {
2022 NS_WARNING("Non-embedded BrowsingContext");
2023 return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED
, __func__
);
2026 if (!embedderWindowGlobal
->CanSend()) {
2027 NS_WARNING("Embedder already been destroyed.");
2028 return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
, __func__
);
2031 RefPtr
<BrowserParent
> embedderBrowser
=
2032 embedderWindowGlobal
->GetBrowserParent();
2033 if (embedderBrowser
&& embedderBrowser
->Manager()->IsShuttingDown()) {
2034 NS_WARNING("Embedder already asked to shutdown.");
2035 return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE
, __func__
);
2038 if (aOptions
.mRemoteType
.IsEmpty() && (!IsTop() || !GetEmbedderElement())) {
2039 NS_WARNING("Cannot load non-remote subframes");
2040 return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
2043 // Cancel ongoing remoteness changes.
2044 if (mPendingRemotenessChange
) {
2045 mPendingRemotenessChange
->Cancel(NS_ERROR_ABORT
);
2046 MOZ_DIAGNOSTIC_ASSERT(!mPendingRemotenessChange
, "Should have cleared");
2049 auto promise
= MakeRefPtr
<RemotenessPromise::Private
>(__func__
);
2050 promise
->UseDirectTaskDispatch(__func__
);
2052 RefPtr
<PendingRemotenessChange
> change
=
2053 new PendingRemotenessChange(this, promise
, aPendingSwitchId
, aOptions
);
2054 mPendingRemotenessChange
= change
;
2056 // If a specific BrowsingContextGroup ID was specified for this load, make
2057 // sure to keep it alive until the process switch is completed.
2058 if (aOptions
.mSpecificGroupId
) {
2059 change
->mSpecificGroup
=
2060 BrowsingContextGroup::GetOrCreate(aOptions
.mSpecificGroupId
);
2061 change
->mSpecificGroup
->AddKeepAlive();
2064 // Call `prepareToChangeRemoteness` in parallel with starting a new process
2065 // for <browser> loads.
2066 if (IsTop() && GetEmbedderElement()) {
2067 nsCOMPtr
<nsIBrowser
> browser
= GetEmbedderElement()->AsBrowser();
2069 change
->Cancel(NS_ERROR_FAILURE
);
2070 return promise
.forget();
2073 RefPtr
<Promise
> blocker
;
2074 nsresult rv
= browser
->PrepareToChangeRemoteness(getter_AddRefs(blocker
));
2075 if (NS_FAILED(rv
)) {
2077 return promise
.forget();
2080 // Mark prepareToChange as unresolved, and wait for it to become resolved.
2081 if (blocker
&& blocker
->State() != Promise::PromiseState::Resolved
) {
2082 change
->mWaitingForPrepareToChange
= true;
2083 blocker
->AddCallbacksWithCycleCollectedArgs(
2084 [change
](JSContext
*, JS::Handle
<JS::Value
>, ErrorResult
&) {
2085 change
->mWaitingForPrepareToChange
= false;
2086 change
->MaybeFinish();
2088 [change
](JSContext
*, JS::Handle
<JS::Value
> aValue
, ErrorResult
&) {
2090 Promise::TryExtractNSResultFromRejectionValue(aValue
));
2095 // Switching a subframe to be local within it's embedding process.
2096 if (embedderBrowser
&&
2097 aOptions
.mRemoteType
== embedderBrowser
->Manager()->GetRemoteType()) {
2098 MOZ_DIAGNOSTIC_ASSERT(
2100 "We always have a PendingSwitchId, except for print-preview loads, "
2101 "which will never perform a process-switch to being in-process with "
2103 MOZ_DIAGNOSTIC_ASSERT(!aOptions
.mReplaceBrowsingContext
);
2104 MOZ_DIAGNOSTIC_ASSERT(!aOptions
.mRemoteType
.IsEmpty());
2105 MOZ_DIAGNOSTIC_ASSERT(!change
->mWaitingForPrepareToChange
);
2106 MOZ_DIAGNOSTIC_ASSERT(!change
->mSpecificGroup
);
2108 // Switching to local, so we don't need to create a new process, and will
2109 // instead use our embedder process.
2110 change
->mContentParent
= embedderBrowser
->Manager();
2111 change
->mContentParent
->AddKeepAlive();
2112 change
->ProcessLaunched();
2113 return promise
.forget();
2116 // Switching to the parent process.
2117 if (aOptions
.mRemoteType
.IsEmpty()) {
2118 change
->ProcessLaunched();
2119 return promise
.forget();
2122 // If we're aiming to end up in a new process of the same type as our old
2123 // process, and then putting our previous document in the BFCache, try to stay
2124 // in the same process to avoid creating new processes unnecessarily.
2125 RefPtr
<ContentParent
> existingProcess
= GetContentParent();
2126 if (existingProcess
&& !existingProcess
->IsShuttingDown() &&
2127 aOptions
.mReplaceBrowsingContext
&&
2128 aOptions
.mRemoteType
== existingProcess
->GetRemoteType()) {
2129 change
->mContentParent
= existingProcess
;
2130 change
->mContentParent
->AddKeepAlive();
2131 change
->ProcessLaunched();
2132 return promise
.forget();
2135 // Try to predict which BrowsingContextGroup will be used for the final load
2136 // in this BrowsingContext. This has to be accurate if switching into an
2137 // existing group, as it will control what pool of processes will be used
2138 // for process selection.
2140 // It's _technically_ OK to provide a group here if we're actually going to
2141 // switch into a brand new group, though it's sub-optimal, as it can
2142 // restrict the set of processes we're using.
2143 BrowsingContextGroup
* finalGroup
=
2144 aOptions
.mReplaceBrowsingContext
? change
->mSpecificGroup
.get() : Group();
2147 StaticPrefs::browser_tabs_remote_subframesPreferUsed() && !IsTop();
2149 change
->mContentParent
= ContentParent::GetNewOrUsedLaunchingBrowserProcess(
2150 /* aRemoteType = */ aOptions
.mRemoteType
,
2151 /* aGroup = */ finalGroup
,
2152 /* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND
,
2153 /* aPreferUsed = */ preferUsed
);
2154 if (!change
->mContentParent
) {
2155 change
->Cancel(NS_ERROR_FAILURE
);
2156 return promise
.forget();
2159 // Add a KeepAlive used by this ContentParent, which will be cleared when
2160 // the change is complete. This should prevent the process dying before
2161 // we're ready to use it.
2162 change
->mContentParent
->AddKeepAlive();
2163 if (change
->mContentParent
->IsLaunching()) {
2164 change
->mContentParent
->WaitForLaunchAsync()->Then(
2165 GetMainThreadSerialEventTarget(), __func__
,
2166 [change
](ContentParent
*) { change
->ProcessLaunched(); },
2167 [change
]() { change
->Cancel(NS_ERROR_FAILURE
); });
2169 change
->ProcessLaunched();
2171 return promise
.forget();
2174 void CanonicalBrowsingContext::MaybeSetPermanentKey(Element
* aEmbedder
) {
2175 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2178 if (nsCOMPtr
<nsIBrowser
> browser
= aEmbedder
->AsBrowser()) {
2179 JS::Rooted
<JS::Value
> key(RootingCx());
2180 if (NS_SUCCEEDED(browser
->GetPermanentKey(&key
)) && key
.isObject()) {
2181 mPermanentKey
= key
;
2187 MediaController
* CanonicalBrowsingContext::GetMediaController() {
2188 // We would only create one media controller per tab, so accessing the
2189 // controller via the top-level browsing context.
2191 return Cast(Top())->GetMediaController();
2194 MOZ_ASSERT(!GetParent(),
2195 "Must access the controller from the top-level browsing context!");
2196 // Only content browsing context can create media controller, we won't create
2197 // controller for chrome document, such as the browser UI.
2198 if (!mTabMediaController
&& !IsDiscarded() && IsContent()) {
2199 mTabMediaController
= new MediaController(Id());
2201 return mTabMediaController
;
2204 bool CanonicalBrowsingContext::HasCreatedMediaController() const {
2205 return !!mTabMediaController
;
2208 bool CanonicalBrowsingContext::SupportsLoadingInParent(
2209 nsDocShellLoadState
* aLoadState
, uint64_t* aOuterWindowId
) {
2210 // We currently don't support initiating loads in the parent when they are
2211 // watched by devtools. This is because devtools tracks loads using content
2212 // process notifications, which happens after the load is initiated in this
2213 // case. Devtools clears all prior requests when it detects a new navigation,
2214 // so it drops the main document load that happened here.
2215 if (WatchedByDevTools()) {
2219 // Session-history-in-parent implementation relies currently on getting a
2220 // round trip through a child process.
2221 if (aLoadState
->LoadIsFromSessionHistory()) {
2225 // DocumentChannel currently only supports connecting channels into the
2226 // content process, so we can only support schemes that will always be loaded
2227 // there for now. Restrict to just http(s) for simplicity.
2228 if (!net::SchemeIsHTTP(aLoadState
->URI()) &&
2229 !net::SchemeIsHTTPS(aLoadState
->URI())) {
2233 if (WindowGlobalParent
* global
= GetCurrentWindowGlobal()) {
2234 nsCOMPtr
<nsIURI
> currentURI
= global
->GetDocumentURI();
2236 bool newURIHasRef
= false;
2237 aLoadState
->URI()->GetHasRef(&newURIHasRef
);
2238 bool equalsExceptRef
= false;
2239 aLoadState
->URI()->EqualsExceptRef(currentURI
, &equalsExceptRef
);
2241 if (equalsExceptRef
&& newURIHasRef
) {
2242 // This navigation is same-doc WRT the current one, we should pass it
2243 // down to the docshell to be handled.
2247 // If the current document has a beforeunload listener, then we need to
2248 // start the load in that process after we fire the event.
2249 if (global
->HasBeforeUnload()) {
2253 *aOuterWindowId
= global
->OuterWindowId();
2258 bool CanonicalBrowsingContext::LoadInParent(nsDocShellLoadState
* aLoadState
,
2259 bool aSetNavigating
) {
2260 // We currently only support starting loads directly from the
2261 // CanonicalBrowsingContext for top-level BCs.
2262 // We currently only support starting loads directly from the
2263 // CanonicalBrowsingContext for top-level BCs.
2264 if (!IsTopContent() || !GetContentParent() ||
2265 !StaticPrefs::browser_tabs_documentchannel_parent_controlled()) {
2269 uint64_t outerWindowId
= 0;
2270 if (!SupportsLoadingInParent(aLoadState
, &outerWindowId
)) {
2274 MOZ_ASSERT(!net::SchemeIsJavascript(aLoadState
->URI()));
2276 MOZ_ALWAYS_SUCCEEDS(
2277 SetParentInitiatedNavigationEpoch(++gParentInitiatedNavigationEpoch
));
2278 // Note: If successful, this will recurse into StartDocumentLoad and
2279 // set mCurrentLoad to the DocumentLoadListener instance created.
2280 // Ideally in the future we will only start loads from here, and we can
2281 // just set this directly instead.
2282 return net::DocumentLoadListener::LoadInParent(this, aLoadState
,
2286 bool CanonicalBrowsingContext::AttemptSpeculativeLoadInParent(
2287 nsDocShellLoadState
* aLoadState
) {
2288 // We currently only support starting loads directly from the
2289 // CanonicalBrowsingContext for top-level BCs.
2290 // We currently only support starting loads directly from the
2291 // CanonicalBrowsingContext for top-level BCs.
2292 if (!IsTopContent() || !GetContentParent() ||
2293 (StaticPrefs::browser_tabs_documentchannel_parent_controlled())) {
2297 uint64_t outerWindowId
= 0;
2298 if (!SupportsLoadingInParent(aLoadState
, &outerWindowId
)) {
2302 // If we successfully open the DocumentChannel, then it'll register
2303 // itself using aLoadIdentifier and be kept alive until it completes
2305 return net::DocumentLoadListener::SpeculativeLoadInParent(this, aLoadState
);
2308 bool CanonicalBrowsingContext::StartDocumentLoad(
2309 net::DocumentLoadListener
* aLoad
) {
2310 // If we're controlling loads from the parent, then starting a new load means
2311 // that we need to cancel any existing ones.
2312 if (StaticPrefs::browser_tabs_documentchannel_parent_controlled() &&
2314 // Make sure we are not loading a javascript URI.
2315 MOZ_ASSERT(!aLoad
->IsLoadingJSURI());
2317 // If we want to do a download, don't cancel the current navigation.
2318 if (!aLoad
->IsDownload()) {
2319 mCurrentLoad
->Cancel(NS_BINDING_CANCELLED_OLD_LOAD
, ""_ns
);
2322 mCurrentLoad
= aLoad
;
2324 if (NS_FAILED(SetCurrentLoadIdentifier(Some(aLoad
->GetLoadIdentifier())))) {
2325 mCurrentLoad
= nullptr;
2332 void CanonicalBrowsingContext::EndDocumentLoad(bool aContinueNavigating
) {
2333 mCurrentLoad
= nullptr;
2335 if (!aContinueNavigating
) {
2336 // Resetting the current load identifier on a discarded context
2337 // has no effect when a document load has finished.
2338 Unused
<< SetCurrentLoadIdentifier(Nothing());
2342 already_AddRefed
<nsIURI
> CanonicalBrowsingContext::GetCurrentURI() const {
2343 nsCOMPtr
<nsIURI
> currentURI
;
2344 if (nsIDocShell
* docShell
= GetDocShell()) {
2345 MOZ_ALWAYS_SUCCEEDS(
2346 nsDocShell::Cast(docShell
)->GetCurrentURI(getter_AddRefs(currentURI
)));
2348 currentURI
= mCurrentRemoteURI
;
2350 return currentURI
.forget();
2353 void CanonicalBrowsingContext::SetCurrentRemoteURI(nsIURI
* aCurrentRemoteURI
) {
2354 MOZ_ASSERT(!GetDocShell());
2355 mCurrentRemoteURI
= aCurrentRemoteURI
;
2358 void CanonicalBrowsingContext::ResetSHEntryHasUserInteractionCache() {
2359 WindowContext
* topWc
= GetTopWindowContext();
2360 if (topWc
&& !topWc
->IsDiscarded()) {
2361 MOZ_ALWAYS_SUCCEEDS(topWc
->SetSHEntryHasUserInteraction(false));
2365 void CanonicalBrowsingContext::HistoryCommitIndexAndLength() {
2367 CallerWillNotifyHistoryIndexAndLengthChanges
caller(nullptr);
2368 HistoryCommitIndexAndLength(changeID
, caller
);
2370 void CanonicalBrowsingContext::HistoryCommitIndexAndLength(
2371 const nsID
& aChangeID
,
2372 const CallerWillNotifyHistoryIndexAndLengthChanges
& aProofOfCaller
) {
2374 Cast(Top())->HistoryCommitIndexAndLength(aChangeID
, aProofOfCaller
);
2378 nsISHistory
* shistory
= GetSessionHistory();
2383 shistory
->GetIndex(&index
);
2384 int32_t length
= shistory
->GetCount();
2386 GetChildSessionHistory()->SetIndexAndLength(index
, length
, aChangeID
);
2388 shistory
->EvictOutOfRangeDocumentViewers(index
);
2390 Group()->EachParent([&](ContentParent
* aParent
) {
2391 Unused
<< aParent
->SendHistoryCommitIndexAndLength(this, index
, length
,
2396 void CanonicalBrowsingContext::SynchronizeLayoutHistoryState() {
2398 if (IsInProcess()) {
2399 nsIDocShell
* docShell
= GetDocShell();
2401 docShell
->PersistLayoutHistoryState();
2403 nsCOMPtr
<nsILayoutHistoryState
> state
;
2404 docShell
->GetLayoutHistoryState(getter_AddRefs(state
));
2406 mActiveEntry
->SetLayoutHistoryState(state
);
2409 } else if (ContentParent
* cp
= GetContentParent()) {
2410 cp
->SendGetLayoutHistoryState(this)->Then(
2411 GetCurrentSerialEventTarget(), __func__
,
2412 [activeEntry
= mActiveEntry
](
2413 const std::tuple
<RefPtr
<nsILayoutHistoryState
>, Maybe
<Wireframe
>>&
2415 if (std::get
<0>(aResult
)) {
2416 activeEntry
->SetLayoutHistoryState(std::get
<0>(aResult
));
2418 if (std::get
<1>(aResult
)) {
2419 activeEntry
->SetWireframe(std::get
<1>(aResult
));
2427 void CanonicalBrowsingContext::ResetScalingZoom() {
2428 // This currently only ever gets called in the parent process, and we
2429 // pass the message on to the WindowGlobalChild for the rootmost browsing
2431 if (WindowGlobalParent
* topWindow
= GetTopWindowContext()) {
2432 Unused
<< topWindow
->SendResetScalingZoom();
2436 void CanonicalBrowsingContext::SetRestoreData(SessionStoreRestoreData
* aData
,
2437 ErrorResult
& aError
) {
2438 MOZ_DIAGNOSTIC_ASSERT(aData
);
2440 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(GetParentObject());
2441 RefPtr
<Promise
> promise
= Promise::Create(global
, aError
);
2442 if (aError
.Failed()) {
2446 if (NS_WARN_IF(NS_FAILED(SetHasRestoreData(true)))) {
2447 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2451 mRestoreState
= new RestoreState();
2452 mRestoreState
->mData
= aData
;
2453 mRestoreState
->mPromise
= promise
;
2456 already_AddRefed
<Promise
> CanonicalBrowsingContext::GetRestorePromise() {
2457 if (mRestoreState
) {
2458 return do_AddRef(mRestoreState
->mPromise
);
2463 void CanonicalBrowsingContext::ClearRestoreState() {
2464 if (!mRestoreState
) {
2465 MOZ_DIAGNOSTIC_ASSERT(!GetHasRestoreData());
2468 if (mRestoreState
->mPromise
) {
2469 mRestoreState
->mPromise
->MaybeRejectWithUndefined();
2471 mRestoreState
= nullptr;
2472 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
2475 void CanonicalBrowsingContext::RequestRestoreTabContent(
2476 WindowGlobalParent
* aWindow
) {
2477 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2479 if (IsDiscarded() || !mRestoreState
|| !mRestoreState
->mData
) {
2483 CanonicalBrowsingContext
* context
= aWindow
->GetBrowsingContext();
2484 MOZ_DIAGNOSTIC_ASSERT(!context
->IsDiscarded());
2486 RefPtr
<SessionStoreRestoreData
> data
=
2487 mRestoreState
->mData
->FindDataForChild(context
);
2489 if (context
->IsTop()) {
2490 MOZ_DIAGNOSTIC_ASSERT(context
== this);
2492 // We need to wait until the appropriate load event has fired before we
2493 // can "complete" the restore process, so if we're holding an empty data
2494 // object, just resolve the promise immediately.
2495 if (mRestoreState
->mData
->IsEmpty()) {
2496 MOZ_DIAGNOSTIC_ASSERT(!data
|| data
->IsEmpty());
2497 mRestoreState
->Resolve();
2498 ClearRestoreState();
2502 // Since we're following load event order, we'll only arrive here for a
2503 // toplevel context after we've already sent down data for all child frames,
2504 // so it's safe to clear this reference now. The completion callback below
2505 // relies on the mData field being null to determine if all requests have
2507 mRestoreState
->ClearData();
2508 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false));
2511 if (data
&& !data
->IsEmpty()) {
2512 auto onTabRestoreComplete
= [self
= RefPtr
{this},
2513 state
= RefPtr
{mRestoreState
}](auto) {
2515 if (!state
->mData
&& state
->mRequests
== state
->mResolves
) {
2517 if (state
== self
->mRestoreState
) {
2518 self
->ClearRestoreState();
2523 mRestoreState
->mRequests
++;
2525 if (data
->CanRestoreInto(aWindow
->GetDocumentURI())) {
2526 if (!aWindow
->IsInProcess()) {
2527 aWindow
->SendRestoreTabContent(WrapNotNull(data
.get()),
2528 onTabRestoreComplete
,
2529 onTabRestoreComplete
);
2532 data
->RestoreInto(context
);
2535 // This must be called both when we're doing an in-process restore, and when
2536 // we didn't do a restore at all due to a URL mismatch.
2537 onTabRestoreComplete(true);
2541 void CanonicalBrowsingContext::RestoreState::Resolve() {
2542 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
2543 mPromise
->MaybeResolveWithUndefined();
2547 nsresult
CanonicalBrowsingContext::WriteSessionStorageToSessionStore(
2548 const nsTArray
<SSCacheCopy
>& aSesssionStorage
, uint32_t aEpoch
) {
2549 nsCOMPtr
<nsISessionStoreFunctions
> funcs
= do_ImportESModule(
2550 "resource://gre/modules/SessionStoreFunctions.sys.mjs", fallible
);
2552 return NS_ERROR_FAILURE
;
2555 nsCOMPtr
<nsIXPConnectWrappedJS
> wrapped
= do_QueryInterface(funcs
);
2557 if (!jsapi
.Init(wrapped
->GetJSObjectGlobal())) {
2558 return NS_ERROR_FAILURE
;
2561 JS::Rooted
<JS::Value
> key(jsapi
.cx(), Top()->PermanentKey());
2563 Record
<nsCString
, Record
<nsString
, nsString
>> storage
;
2564 JS::Rooted
<JS::Value
> update(jsapi
.cx());
2566 if (!aSesssionStorage
.IsEmpty()) {
2567 SessionStoreUtils::ConstructSessionStorageValues(this, aSesssionStorage
,
2569 if (!ToJSValue(jsapi
.cx(), storage
, &update
)) {
2570 return NS_ERROR_FAILURE
;
2576 return funcs
->UpdateSessionStoreForStorage(Top()->GetEmbedderElement(), this,
2577 key
, aEpoch
, update
);
2580 void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage(
2581 const std::function
<void()>& aDone
) {
2582 if (!StaticPrefs::browser_sessionstore_collect_session_storage_AtStartup()) {
2587 using DataPromise
= BackgroundSessionStorageManager::DataPromise
;
2588 BackgroundSessionStorageManager::GetData(
2589 this, StaticPrefs::browser_sessionstore_dom_storage_limit(),
2590 /* aClearSessionStoreTimer = */ true)
2591 ->Then(GetCurrentSerialEventTarget(), __func__
,
2592 [self
= RefPtr
{this}, aDone
, epoch
= GetSessionStoreEpoch()](
2593 const DataPromise::ResolveOrRejectValue
& valueList
) {
2594 if (valueList
.IsResolve()) {
2595 self
->WriteSessionStorageToSessionStore(
2596 valueList
.ResolveValue(), epoch
);
2603 void CanonicalBrowsingContext::UpdateSessionStoreForStorage(
2604 uint64_t aBrowsingContextId
) {
2605 RefPtr
<CanonicalBrowsingContext
> browsingContext
= Get(aBrowsingContextId
);
2607 if (!browsingContext
) {
2611 browsingContext
->UpdateSessionStoreSessionStorage([]() {});
2614 void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() {
2615 if (!StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
2620 Top()->MaybeScheduleSessionStoreUpdate();
2624 if (IsInBFCache()) {
2628 if (mSessionStoreSessionStorageUpdateTimer
) {
2632 if (!StaticPrefs::browser_sessionstore_debug_no_auto_updates()) {
2633 auto result
= NS_NewTimerWithFuncCallback(
2634 [](nsITimer
*, void* aClosure
) {
2635 auto* context
= static_cast<CanonicalBrowsingContext
*>(aClosure
);
2636 context
->UpdateSessionStoreSessionStorage([]() {});
2638 this, StaticPrefs::browser_sessionstore_interval(),
2639 nsITimer::TYPE_ONE_SHOT
,
2640 "CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate");
2642 if (result
.isErr()) {
2646 mSessionStoreSessionStorageUpdateTimer
= result
.unwrap();
2650 void CanonicalBrowsingContext::CancelSessionStoreUpdate() {
2651 if (mSessionStoreSessionStorageUpdateTimer
) {
2652 mSessionStoreSessionStorageUpdateTimer
->Cancel();
2653 mSessionStoreSessionStorageUpdateTimer
= nullptr;
2657 void CanonicalBrowsingContext::SetContainerFeaturePolicy(
2658 FeaturePolicy
* aContainerFeaturePolicy
) {
2659 mContainerFeaturePolicy
= aContainerFeaturePolicy
;
2661 if (WindowGlobalParent
* current
= GetCurrentWindowGlobal()) {
2662 Unused
<< current
->SendSetContainerFeaturePolicy(mContainerFeaturePolicy
);
2666 void CanonicalBrowsingContext::SetCrossGroupOpenerId(uint64_t aOpenerId
) {
2667 MOZ_DIAGNOSTIC_ASSERT(IsTopContent());
2668 MOZ_DIAGNOSTIC_ASSERT(mCrossGroupOpenerId
== 0,
2669 "Can only set CrossGroupOpenerId once");
2670 mCrossGroupOpenerId
= aOpenerId
;
2673 void CanonicalBrowsingContext::SetCrossGroupOpener(
2674 CanonicalBrowsingContext
& aCrossGroupOpener
, ErrorResult
& aRv
) {
2675 if (!IsTopContent()) {
2676 aRv
.ThrowNotAllowedError(
2677 "Can only set crossGroupOpener on toplevel content");
2680 if (mCrossGroupOpenerId
!= 0) {
2681 aRv
.ThrowNotAllowedError("Can only set crossGroupOpener once");
2685 SetCrossGroupOpenerId(aCrossGroupOpener
.Id());
2688 auto CanonicalBrowsingContext::FindUnloadingHost(uint64_t aChildID
)
2689 -> nsTArray
<UnloadingHost
>::iterator
{
2690 return std::find_if(
2691 mUnloadingHosts
.begin(), mUnloadingHosts
.end(),
2692 [&](const auto& host
) { return host
.mChildID
== aChildID
; });
2695 void CanonicalBrowsingContext::ClearUnloadingHost(uint64_t aChildID
) {
2696 // Notify any callbacks which were waiting for the host to finish unloading
2698 auto found
= FindUnloadingHost(aChildID
);
2699 if (found
!= mUnloadingHosts
.end()) {
2700 auto callbacks
= std::move(found
->mCallbacks
);
2701 mUnloadingHosts
.RemoveElementAt(found
);
2702 for (const auto& callback
: callbacks
) {
2708 void CanonicalBrowsingContext::StartUnloadingHost(uint64_t aChildID
) {
2709 MOZ_DIAGNOSTIC_ASSERT(FindUnloadingHost(aChildID
) == mUnloadingHosts
.end());
2710 mUnloadingHosts
.AppendElement(UnloadingHost
{aChildID
, {}});
2713 void CanonicalBrowsingContext::BrowserParentDestroyed(
2714 BrowserParent
* aBrowserParent
, bool aAbnormalShutdown
) {
2715 ClearUnloadingHost(aBrowserParent
->Manager()->ChildID());
2717 // Handling specific to when the current BrowserParent has been destroyed.
2718 if (mCurrentBrowserParent
== aBrowserParent
) {
2719 mCurrentBrowserParent
= nullptr;
2721 // If this BrowserParent is for a subframe, attempt to recover from a
2722 // subframe crash by rendering the subframe crashed page in the embedding
2724 if (aAbnormalShutdown
) {
2725 ShowSubframeCrashedUI(aBrowserParent
->GetBrowserBridgeParent());
2730 void CanonicalBrowsingContext::ShowSubframeCrashedUI(
2731 BrowserBridgeParent
* aBridge
) {
2732 if (!aBridge
|| IsDiscarded() || !aBridge
->CanSend()) {
2736 MOZ_DIAGNOSTIC_ASSERT(!aBridge
->GetBrowsingContext() ||
2737 aBridge
->GetBrowsingContext() == this);
2739 // There is no longer a current inner window within this
2740 // BrowsingContext, update the `CurrentInnerWindowId` field to reflect
2742 MOZ_ALWAYS_SUCCEEDS(SetCurrentInnerWindowId(0));
2744 // The owning process will now be the embedder to render the subframe
2745 // crashed page, switch ownership back over.
2746 SetOwnerProcessId(aBridge
->Manager()->Manager()->ChildID());
2747 SetCurrentBrowserParent(aBridge
->Manager());
2749 Unused
<< aBridge
->SendSubFrameCrashed();
2752 static void LogBFCacheBlockingForDoc(BrowsingContext
* aBrowsingContext
,
2753 uint32_t aBFCacheCombo
, bool aIsSubDoc
) {
2755 nsAutoCString
uri("[no uri]");
2756 nsCOMPtr
<nsIURI
> currentURI
=
2757 aBrowsingContext
->Canonical()->GetCurrentURI();
2759 uri
= currentURI
->GetSpecOrDefault();
2761 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2762 (" ** Blocked for document %s", uri
.get()));
2764 if (aBFCacheCombo
& BFCacheStatus::EVENT_HANDLING_SUPPRESSED
) {
2765 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2766 (" * event handling suppression"));
2768 if (aBFCacheCombo
& BFCacheStatus::SUSPENDED
) {
2769 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * suspended Window"));
2771 if (aBFCacheCombo
& BFCacheStatus::UNLOAD_LISTENER
) {
2772 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * unload listener"));
2774 if (aBFCacheCombo
& BFCacheStatus::REQUEST
) {
2775 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * requests in the loadgroup"));
2777 if (aBFCacheCombo
& BFCacheStatus::ACTIVE_GET_USER_MEDIA
) {
2778 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * GetUserMedia"));
2780 if (aBFCacheCombo
& BFCacheStatus::ACTIVE_PEER_CONNECTION
) {
2781 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * PeerConnection"));
2783 if (aBFCacheCombo
& BFCacheStatus::CONTAINS_EME_CONTENT
) {
2784 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * EME content"));
2786 if (aBFCacheCombo
& BFCacheStatus::CONTAINS_MSE_CONTENT
) {
2787 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * MSE use"));
2789 if (aBFCacheCombo
& BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS
) {
2790 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * Speech use"));
2792 if (aBFCacheCombo
& BFCacheStatus::HAS_USED_VR
) {
2793 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * used VR"));
2795 if (aBFCacheCombo
& BFCacheStatus::BEFOREUNLOAD_LISTENER
) {
2796 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * beforeunload listener"));
2798 if (aBFCacheCombo
& BFCacheStatus::ACTIVE_LOCK
) {
2799 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * has active Web Locks"));
2803 bool CanonicalBrowsingContext::AllowedInBFCache(
2804 const Maybe
<uint64_t>& aChannelId
, nsIURI
* aNewURI
) {
2805 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
2806 nsAutoCString
uri("[no uri]");
2807 nsCOMPtr
<nsIURI
> currentURI
= GetCurrentURI();
2809 uri
= currentURI
->GetSpecOrDefault();
2811 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, ("Checking %s", uri
.get()));
2814 if (IsInProcess()) {
2818 uint32_t bfcacheCombo
= 0;
2819 if (mRestoreState
) {
2820 bfcacheCombo
|= BFCacheStatus::RESTORING
;
2821 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * during session restore"));
2824 if (Group()->Toplevels().Length() > 1) {
2825 bfcacheCombo
|= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG
;
2826 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2827 (" * auxiliary BrowsingContexts"));
2830 // There are not a lot of about:* pages that are allowed to load in
2831 // subframes, so it's OK to allow those few about:* pages enter BFCache.
2832 MOZ_ASSERT(IsTop(), "Trying to put a non top level BC into BFCache");
2834 WindowGlobalParent
* wgp
= GetCurrentWindowGlobal();
2835 if (wgp
&& wgp
->GetDocumentURI()) {
2836 nsCOMPtr
<nsIURI
> currentURI
= wgp
->GetDocumentURI();
2837 // Exempt about:* pages from bfcache, with the exception of about:blank
2838 if (currentURI
->SchemeIs("about") &&
2839 !currentURI
->GetSpecOrDefault().EqualsLiteral("about:blank")) {
2840 bfcacheCombo
|= BFCacheStatus::ABOUT_PAGE
;
2841 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
, (" * about:* page"));
2845 bool equalUri
= false;
2846 aNewURI
->Equals(currentURI
, &equalUri
);
2848 // When loading the same uri, disable bfcache so that
2849 // nsDocShell::OnNewURI transforms the load to LOAD_NORMAL_REPLACE.
2855 // For telemetry we're collecting all the flags for all the BCs hanging
2856 // from this top-level BC.
2857 PreOrderWalk([&](BrowsingContext
* aBrowsingContext
) {
2858 WindowGlobalParent
* wgp
=
2859 aBrowsingContext
->Canonical()->GetCurrentWindowGlobal();
2860 uint32_t subDocBFCacheCombo
= wgp
? wgp
->GetBFCacheStatus() : 0;
2862 const Maybe
<uint64_t>& singleChannelId
= wgp
->GetSingleChannelId();
2863 if (singleChannelId
.isSome()) {
2864 if (singleChannelId
.value() == 0 || aChannelId
.isNothing() ||
2865 singleChannelId
.value() != aChannelId
.value()) {
2866 subDocBFCacheCombo
|= BFCacheStatus::REQUEST
;
2871 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
2872 LogBFCacheBlockingForDoc(aBrowsingContext
, subDocBFCacheCombo
,
2873 aBrowsingContext
!= this);
2876 bfcacheCombo
|= subDocBFCacheCombo
;
2879 nsDocShell::ReportBFCacheComboTelemetry(bfcacheCombo
);
2880 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
2881 nsAutoCString
uri("[no uri]");
2882 nsCOMPtr
<nsIURI
> currentURI
= GetCurrentURI();
2884 uri
= currentURI
->GetSpecOrDefault();
2886 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
2887 (" +> %s %s be blocked from going into the BFCache", uri
.get(),
2888 bfcacheCombo
== 0 ? "shouldn't" : "should"));
2891 if (StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners()) {
2892 bfcacheCombo
&= ~BFCacheStatus::UNLOAD_LISTENER
;
2895 return bfcacheCombo
== 0;
2898 void CanonicalBrowsingContext::SetTouchEventsOverride(
2899 dom::TouchEventsOverride aOverride
, ErrorResult
& aRv
) {
2900 SetTouchEventsOverrideInternal(aOverride
, aRv
);
2903 void CanonicalBrowsingContext::SetTargetTopLevelLinkClicksToBlank(
2904 bool aTargetTopLevelLinkClicksToBlank
, ErrorResult
& aRv
) {
2905 SetTargetTopLevelLinkClicksToBlankInternal(aTargetTopLevelLinkClicksToBlank
,
2909 void CanonicalBrowsingContext::AddPageAwakeRequest() {
2910 MOZ_ASSERT(IsTop());
2911 auto count
= GetPageAwakeRequestCount();
2912 MOZ_ASSERT(count
< UINT32_MAX
);
2913 Unused
<< SetPageAwakeRequestCount(++count
);
2916 void CanonicalBrowsingContext::RemovePageAwakeRequest() {
2917 MOZ_ASSERT(IsTop());
2918 auto count
= GetPageAwakeRequestCount();
2919 MOZ_ASSERT(count
> 0);
2920 Unused
<< SetPageAwakeRequestCount(--count
);
2923 void CanonicalBrowsingContext::CloneDocumentTreeInto(
2924 CanonicalBrowsingContext
* aSource
, const nsACString
& aRemoteType
,
2925 embedding::PrintData
&& aPrintData
) {
2926 NavigationIsolationOptions options
;
2927 options
.mRemoteType
= aRemoteType
;
2930 ChangeRemoteness(options
, /* aPendingSwitchId = */ 0)
2932 GetMainThreadSerialEventTarget(), __func__
,
2933 [source
= MaybeDiscardedBrowsingContext
{aSource
},
2934 data
= std::move(aPrintData
)](
2935 BrowserParent
* aBp
) -> RefPtr
<GenericNonExclusivePromise
> {
2936 RefPtr
<BrowserBridgeParent
> bridge
=
2937 aBp
->GetBrowserBridgeParent();
2938 return aBp
->SendCloneDocumentTreeIntoSelf(source
, data
)
2940 GetMainThreadSerialEventTarget(), __func__
,
2942 BrowserParent::CloneDocumentTreeIntoSelfPromise::
2943 ResolveOrRejectValue
&& aValue
) {
2944 // We're cloning a remote iframe, so we created a
2945 // BrowserBridge which makes us register an OOP load
2946 // (see Document::OOPChildLoadStarted), even though
2947 // this isn't a real load. We call
2948 // SendMaybeFireEmbedderLoadEvents here so that we do
2949 // register the end of the load (see
2950 // Document::OOPChildLoadDone).
2952 Unused
<< bridge
->SendMaybeFireEmbedderLoadEvents(
2953 EmbedderElementEventType::NoEvent
);
2955 if (aValue
.IsResolve() && aValue
.ResolveValue()) {
2956 return GenericNonExclusivePromise::CreateAndResolve(
2959 return GenericNonExclusivePromise::CreateAndReject(
2960 NS_ERROR_FAILURE
, __func__
);
2963 [](nsresult aRv
) -> RefPtr
<GenericNonExclusivePromise
> {
2965 nsPrintfCString("Remote clone failed: %x\n", unsigned(aRv
))
2967 return GenericNonExclusivePromise::CreateAndReject(
2968 NS_ERROR_FAILURE
, __func__
);
2971 mClonePromise
->Then(
2972 GetMainThreadSerialEventTarget(), __func__
,
2973 [self
= RefPtr
{this}]() { self
->mClonePromise
= nullptr; });
2976 bool CanonicalBrowsingContext::StartApzAutoscroll(float aAnchorX
,
2979 uint32_t aPresShellId
) {
2980 nsCOMPtr
<nsIWidget
> widget
;
2981 mozilla::layers::LayersId layersId
{0};
2983 if (IsInProcess()) {
2984 nsCOMPtr
<nsPIDOMWindowOuter
> outer
= GetDOMWindow();
2989 widget
= widget::WidgetUtils::DOMWindowToWidget(outer
);
2991 layersId
= widget
->GetRootLayerTreeId();
2994 RefPtr
<BrowserParent
> parent
= GetBrowserParent();
2999 widget
= parent
->GetWidget();
3000 layersId
= parent
->GetLayersId();
3003 if (!widget
|| !widget
->AsyncPanZoomEnabled()) {
3007 // The anchor coordinates that are passed in are relative to the origin of the
3008 // screen, but we are sending them to APZ which only knows about coordinates
3009 // relative to the widget, so convert them accordingly.
3010 const LayoutDeviceIntPoint anchor
=
3011 RoundedToInt(LayoutDevicePoint(aAnchorX
, aAnchorY
)) -
3012 widget
->WidgetToScreenOffset();
3014 mozilla::layers::ScrollableLayerGuid
guid(layersId
, aPresShellId
, aScrollId
);
3016 return widget
->StartAsyncAutoscroll(
3017 ViewAs
<ScreenPixel
>(
3018 anchor
, PixelCastJustification::LayoutDeviceIsScreenForBounds
),
3022 void CanonicalBrowsingContext::StopApzAutoscroll(nsViewID aScrollId
,
3023 uint32_t aPresShellId
) {
3024 nsCOMPtr
<nsIWidget
> widget
;
3025 mozilla::layers::LayersId layersId
{0};
3027 if (IsInProcess()) {
3028 nsCOMPtr
<nsPIDOMWindowOuter
> outer
= GetDOMWindow();
3033 widget
= widget::WidgetUtils::DOMWindowToWidget(outer
);
3035 layersId
= widget
->GetRootLayerTreeId();
3038 RefPtr
<BrowserParent
> parent
= GetBrowserParent();
3043 widget
= parent
->GetWidget();
3044 layersId
= parent
->GetLayersId();
3047 if (!widget
|| !widget
->AsyncPanZoomEnabled()) {
3051 mozilla::layers::ScrollableLayerGuid
guid(layersId
, aPresShellId
, aScrollId
);
3052 widget
->StopAsyncAutoscroll(guid
);
3055 already_AddRefed
<nsISHEntry
>
3056 CanonicalBrowsingContext::GetMostRecentLoadingSessionHistoryEntry() {
3057 if (mLoadingEntries
.IsEmpty()) {
3061 RefPtr
<SessionHistoryEntry
> entry
= mLoadingEntries
.LastElement().mEntry
;
3062 return entry
.forget();
3065 already_AddRefed
<BounceTrackingState
>
3066 CanonicalBrowsingContext::GetBounceTrackingState() {
3067 if (!mWebProgress
) {
3070 return mWebProgress
->GetBounceTrackingState();
3073 NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext
)
3075 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext
,
3077 tmp
->mPermanentKey
.setNull();
3078 if (tmp
->mSessionHistory
) {
3079 tmp
->mSessionHistory
->SetBrowsingContext(nullptr);
3081 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionHistory
, mContainerFeaturePolicy
,
3082 mCurrentBrowserParent
, mWebProgress
,
3083 mSessionStoreSessionStorageUpdateTimer
)
3084 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3086 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CanonicalBrowsingContext
,
3088 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionHistory
, mContainerFeaturePolicy
,
3089 mCurrentBrowserParent
, mWebProgress
,
3090 mSessionStoreSessionStorageUpdateTimer
)
3091 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3093 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CanonicalBrowsingContext
,
3095 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPermanentKey
)
3096 NS_IMPL_CYCLE_COLLECTION_TRACE_END
3098 NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext
, BrowsingContext
)
3099 NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext
, BrowsingContext
)
3101 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanonicalBrowsingContext
)
3102 NS_INTERFACE_MAP_END_INHERITING(BrowsingContext
)
3104 } // namespace mozilla::dom