no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / docshell / base / CanonicalBrowsingContext.cpp
blob33ed5aab2882dfb45cd29a31707339c4b9f450f8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/CanonicalBrowsingContext.h"
9 #include "ErrorList.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"
32 #ifdef NS_PRINTING
33 # include "mozilla/layout/RemotePrintJobParent.h"
34 #endif
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) {
87 return;
90 static bool sHasSeenPrivateContext = false;
91 if (!sHasSeenPrivateContext) {
92 sHasSeenPrivateContext = true;
93 mozilla::Telemetry::ScalarSet(
94 mozilla::Telemetry::ScalarID::DOM_PARENTPROCESS_PRIVATE_WINDOW_USED,
95 true);
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",
114 nullptr);
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,
132 FieldValues&& aInit)
133 : BrowsingContext(aParentWindow, aGroup, aBrowsingContextId, aType,
134 std::move(aInit)),
135 mProcessId(aOwnerProcessId),
136 mEmbedderProcessId(aEmbedderProcessId),
137 mPermanentKey(JS::NullValue()) {
138 // You are only ever allowed to create CanonicalBrowsingContexts in the
139 // parent process.
140 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
142 // The initial URI in a BrowsingContext is always "about:blank".
143 MOZ_ALWAYS_SUCCEEDS(
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);
159 /* static */
160 already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Get(
161 uint64_t aId) {
162 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
163 return BrowsingContext::Get(aId).downcast<CanonicalBrowsingContext>();
166 /* static */
167 CanonicalBrowsingContext* CanonicalBrowsingContext::Cast(
168 BrowsingContext* aContext) {
169 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
170 return static_cast<CanonicalBrowsingContext*>(aContext);
173 /* static */
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) {
188 return nullptr;
191 ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
192 if (!cpm) {
193 return nullptr;
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;
203 return;
206 ContentParent* cp = GetContentParent();
207 if (!cp) {
208 aRv.Throw(NS_ERROR_UNEXPECTED);
209 return;
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() {
225 if (!IsTop()) {
226 return nullptr;
228 if (!mSecureBrowserUI) {
229 mSecureBrowserUI = new nsSecureBrowserUI(this);
231 return mSecureBrowserUI;
234 namespace {
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 {
243 public:
244 NS_DECL_ISUPPORTS
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) {}
253 private:
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();
260 } else {
261 context = CanonicalBrowsingContext::Get(mTopContextId);
263 return context && !context->IsDiscarded() ? context->GetWebProgress()
264 : nullptr;
267 uint64_t mTopContextId = 0;
270 NS_IMPL_ISUPPORTS(DocShellProgressBridge, nsIWebProgressListener)
271 } // namespace
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
277 // listener.
278 if (!IsTopContent()) {
279 return;
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());
299 mIsReplaced = true;
300 aNewContext->mIsReplaced = false;
302 if (mStatusFilter) {
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.
313 Transaction txn;
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
326 // 1781936).
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));
355 } else {
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
370 // true.
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);
438 if (!widget) {
439 widget = nsContentUtils::WidgetForDocument(topEmbedder->OwnerDoc());
443 if (widget) {
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());
458 return nullptr;
461 already_AddRefed<WindowGlobalParent>
462 CanonicalBrowsingContext::GetEmbedderWindowGlobal() const {
463 uint64_t windowId = GetEmbedderInnerWindowId();
464 if (windowId == 0) {
465 return nullptr;
468 return WindowGlobalParent::GetByInnerWindowId(windowId);
471 CanonicalBrowsingContext*
472 CanonicalBrowsingContext::GetParentCrossChromeBoundary() {
473 if (GetParent()) {
474 return Cast(GetParent());
476 if (auto* embedder = GetEmbedderElement()) {
477 return Cast(embedder->OwnerDoc()->GetBrowsingContext());
479 return nullptr;
482 CanonicalBrowsingContext* CanonicalBrowsingContext::TopCrossChromeBoundary() {
483 CanonicalBrowsingContext* bc = this;
484 while (auto* parent = bc->GetParentCrossChromeBoundary()) {
485 bc = parent;
487 return bc;
490 Nullable<WindowProxyHolder> CanonicalBrowsingContext::GetTopChromeWindow() {
491 RefPtr<CanonicalBrowsingContext> bc = TopCrossChromeBoundary();
492 if (bc->IsChrome()) {
493 return WindowProxyHolder(bc.forget());
495 return nullptr;
498 nsISHistory* CanonicalBrowsingContext::GetSessionHistory() {
499 if (!IsTop()) {
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() {
513 return mActiveEntry;
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()) {
545 return;
548 SessionHistoryEntry* parentSHE =
549 GetParent()->Canonical()->GetActiveSessionHistoryEntry();
550 if (parentSHE) {
551 int32_t index = -1;
552 for (BrowsingContext* sibling : GetParent()->Children()) {
553 ++index;
554 if (sibling == this) {
555 nsCOMPtr<nsISHEntry> shEntry;
556 parentSHE->GetChildSHEntryIfHasNoDynamicallyAddedChild(
557 index, getter_AddRefs(shEntry));
558 nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(shEntry);
559 if (she) {
560 aLoadingInfo.emplace(she);
561 mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{
562 aLoadingInfo.value().mLoadId, she.get()});
563 Unused << SetHistoryID(she->DocshellID());
565 break;
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) {
580 if (existingEntry) {
581 entry = existingEntry;
582 } else {
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()));
590 if (!loadingEntry) {
591 return nullptr;
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()) &&
605 mActiveEntry) {
606 entry = mActiveEntry;
607 } else {
608 entry = new SessionHistoryEntry(aLoadState, aChannel);
609 if (IsTop()) {
610 // Only top level pages care about Get/SetPersist.
611 entry->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);
625 } else {
626 loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(entry);
627 mLoadingEntries.AppendElement(
628 LoadingSessionHistoryEntry{loadingInfo->mLoadId, entry});
631 MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(loadingInfo->mLoadId)->mEntry ==
632 entry);
634 return loadingInfo;
637 UniquePtr<LoadingSessionHistoryInfo>
638 CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
639 LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel) {
640 MOZ_ASSERT(aInfo);
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);
652 if (IsTop()) {
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));
658 } else {
659 loadingEntry->SetIsSubFrame(aInfo->mInfo.IsSubFrame());
661 loadingEntry->SetDocshellID(GetHistoryID());
662 loadingEntry->SetIsDynamicallyAdded(CreatedDynamically());
663 return MakeUnique<LoadingSessionHistoryInfo>(loadingEntry, aInfo);
666 return nullptr;
669 using PrintPromise = CanonicalBrowsingContext::PrintPromise;
670 #ifdef NS_PRINTING
671 class PrintListenerAdapter final : public nsIWebProgressListener {
672 public:
673 explicit PrintListenerAdapter(PrintPromise::Private* aPromise)
674 : mPromise(aPromise) {}
676 NS_DECL_ISUPPORTS
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__);
684 mPromise = nullptr;
686 return NS_OK;
688 NS_IMETHOD OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
689 nsresult aStatus,
690 const char16_t* aMessage) override {
691 if (aStatus != NS_OK && mPromise) {
692 mPromise->Reject(aStatus, __func__);
693 mPromise = nullptr;
695 return NS_OK;
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 {
702 return NS_OK;
704 NS_IMETHOD OnLocationChange(nsIWebProgress* aWebProgress,
705 nsIRequest* aRequest, nsIURI* aLocation,
706 uint32_t aFlags) override {
707 return NS_OK;
709 NS_IMETHOD OnSecurityChange(nsIWebProgress* aWebProgress,
710 nsIRequest* aRequest, uint32_t aState) override {
711 return NS_OK;
713 NS_IMETHOD OnContentBlockingEvent(nsIWebProgress* aWebProgress,
714 nsIRequest* aRequest,
715 uint32_t aEvent) override {
716 return NS_OK;
719 private:
720 ~PrintListenerAdapter() = default;
722 RefPtr<PrintPromise::Private> mPromise;
725 NS_IMPL_ISUPPORTS(PrintListenerAdapter, nsIWebProgressListener)
726 #endif
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)
736 ->Then(
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) {
745 #ifndef NS_PRINTING
746 return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__);
747 #else
749 auto promise = MakeRefPtr<PrintPromise::Private>(__func__);
750 auto listener = MakeRefPtr<PrintListenerAdapter>(promise);
751 if (IsInProcess()) {
752 RefPtr<nsGlobalWindowOuter> outerWindow =
753 nsGlobalWindowOuter::Cast(GetDOMWindow());
754 if (NS_WARN_IF(!outerWindow)) {
755 promise->Reject(NS_ERROR_FAILURE, __func__);
756 return promise;
759 ErrorResult rv;
760 outerWindow->Print(aPrintSettings,
761 /* aRemotePrintJob = */ nullptr, listener,
762 /* aDocShellToCloneInto = */ nullptr,
763 nsGlobalWindowOuter::IsPreview::No,
764 nsGlobalWindowOuter::IsForWindowDotPrint::No,
765 /* aPrintPreviewCallback = */ nullptr, rv);
766 if (rv.Failed()) {
767 promise->Reject(rv.StealNSResult(), __func__);
769 return promise;
772 auto* browserParent = GetBrowserParent();
773 if (NS_WARN_IF(!browserParent)) {
774 promise->Reject(NS_ERROR_FAILURE, __func__);
775 return promise;
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__);
782 return promise;
785 nsresult rv;
786 nsCOMPtr<nsIPrintSettings> printSettings = aPrintSettings;
787 if (!printSettings) {
788 rv =
789 printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
790 if (NS_WARN_IF(NS_FAILED(rv))) {
791 promise->Reject(rv, __func__);
792 return promise;
796 embedding::PrintData printData;
797 rv = printSettingsSvc->SerializeToPrintData(printSettings, &printData);
798 if (NS_WARN_IF(NS_FAILED(rv))) {
799 promise->Reject(rv, __func__);
800 return promise;
803 layout::RemotePrintJobParent* remotePrintJob =
804 new layout::RemotePrintJobParent(printSettings);
805 printData.remotePrintJob() =
806 browserParent->Manager()->SendPRemotePrintJobConstructor(remotePrintJob);
808 if (listener) {
809 remotePrintJob->RegisterListener(listener);
812 if (NS_WARN_IF(!browserParent->SendPrint(this, printData))) {
813 promise->Reject(NS_ERROR_FAILURE, __func__);
815 return promise.forget();
816 #endif
819 void CanonicalBrowsingContext::CallOnAllTopDescendants(
820 const FunctionRef<CallState(CanonicalBrowsingContext*)>& aCallback,
821 bool aIncludeNestedBrowsers) {
822 MOZ_ASSERT(IsTop(), "Should only call on top BC");
823 MOZ_ASSERT(
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..
831 return;
834 AutoTArray<RefPtr<BrowsingContextGroup>, 32> groups;
835 BrowsingContextGroup::GetAllGroups(groups);
836 for (auto& browsingContextGroup : groups) {
837 for (auto& bc : browsingContextGroup->Toplevels()) {
838 if (bc == this) {
839 // Cannot be a descendent of myself so skip.
840 continue;
843 if (aIncludeNestedBrowsers) {
844 if (this != bc->Canonical()->TopCrossChromeBoundary()) {
845 continue;
847 } else {
848 auto* parent = bc->Canonical()->GetParentCrossChromeBoundary();
849 if (!parent || this != parent->Top()) {
850 continue;
854 if (aCallback(bc->Canonical()) == CallState::Stop) {
855 return;
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,
866 aLoadId));
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());
872 if (!shistory) {
873 SessionHistoryEntry::RemoveLoadId(aLoadId);
874 mLoadingEntries.RemoveElementAt(i);
875 return;
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.
898 return;
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
906 // Step 4.4.2.
907 nsAutoString nameOfNewEntry;
908 newActiveEntry->GetName(nameOfNewEntry);
909 if (!nameOfNewEntry.IsEmpty()) {
910 nsSHistory::WalkContiguousEntries(
911 newActiveEntry,
912 [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
915 bool addEntry = ShouldUpdateSessionHistory(aLoadType);
916 if (IsTop()) {
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;
935 if (!addEntry) {
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) {
942 addEntry = false;
943 mActiveEntry->ReplaceWith(*newActiveEntry);
944 } else {
945 mActiveEntry = newActiveEntry;
948 if (loadFromSessionHistory) {
949 // XXX Synchronize browsing context tree and session history tree?
950 shistory->InternalSetRequestedIndex(indexOfHistoryLoad);
951 shistory->UpdateIndex();
953 if (IsTop()) {
954 mActiveEntry->SetWireframe(Nothing());
956 } else if (addEntry) {
957 shistory->AddEntry(mActiveEntry, aPersist);
958 shistory->InternalSetRequestedIndex(-1);
960 } else {
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) {
965 if (mActiveEntry) {
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(),
970 this);
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) {
979 if (mActiveEntry) {
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);
988 } else {
989 // AddChildSHEntryHelper does update the index of the session
990 // history!
991 shistory->AddChildSHEntryHelper(mActiveEntry, newActiveEntry,
992 Top(), aCloneEntryChildren);
993 mActiveEntry = newActiveEntry;
995 } else {
996 SessionHistoryEntry* parentEntry = GetParent()->mActiveEntry;
997 // XXX What should happen if parent doesn't have mActiveEntry?
998 // Or can that even happen ever?
999 if (parentEntry) {
1000 mActiveEntry = newActiveEntry;
1001 // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite
1002 // right, but aUseRemoteSubframes should be going away.
1003 parentEntry->AddChild(
1004 mActiveEntry,
1005 CreatedDynamically() ? -1 : GetParent()->IndexOf(this),
1006 IsInProcess());
1009 shistory->InternalSetRequestedIndex(-1);
1013 ResetSHEntryHasUserInteractionCache();
1015 HistoryCommitIndexAndLength(aChangeID, caller);
1017 shistory->LogHistory();
1019 return;
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);
1047 aCanReload = true;
1048 nsISHistory* shistory = GetSessionHistory();
1049 NS_ENSURE_TRUE_VOID(shistory);
1051 shistory->NotifyOnHistoryReload(&aCanReload);
1052 if (!aCanReload) {
1053 return;
1056 if (mActiveEntry) {
1057 aLoadState.emplace(WrapMovingNotNull(RefPtr{CreateLoadInfo(mActiveEntry)}));
1058 aReloadActiveEntry.emplace(true);
1059 if (aForceReload) {
1060 shistory->RemoveFrameEntries(mActiveEntry);
1062 } else if (!mLoadingEntries.IsEmpty()) {
1063 const LoadingSessionHistoryEntry& loadingEntry =
1064 mLoadingEntries.LastElement();
1065 uint64_t loadId = loadingEntry.mLoadId;
1066 aLoadState.emplace(
1067 WrapMovingNotNull(RefPtr{CreateLoadInfo(loadingEntry.mEntry)}));
1068 aReloadActiveEntry.emplace(false);
1069 if (aForceReload) {
1070 SessionHistoryEntry::LoadingEntry* entry =
1071 SessionHistoryEntry::GetByLoadId(loadId);
1072 if (entry) {
1073 shistory->RemoveFrameEntries(entry->mEntry);
1078 if (aLoadState) {
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();
1091 if (!shistory) {
1092 return;
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;
1108 if (IsTop()) {
1109 Maybe<int32_t> previousEntryIndex, loadedEntryIndex;
1110 shistory->AddToRootSessionHistory(
1111 true, oldActiveEntry, this, mActiveEntry, aLoadType,
1112 nsDocShell::ShouldAddToSessionHistory(aInfo->GetURI(), nullptr),
1113 &previousEntryIndex, &loadedEntryIndex);
1114 } else {
1115 if (oldActiveEntry) {
1116 shistory->AddChildSHEntryHelper(oldActiveEntry, mActiveEntry, Top(),
1117 true);
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) {
1138 return;
1141 mActiveEntry->SetInfo(aInfo);
1142 // Notify children of the update
1143 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
1144 if (shistory) {
1145 shistory->NotifyOnHistoryReplaceEntry();
1146 shistory->UpdateRootBrowsingContextState();
1149 ResetSHEntryHasUserInteractionCache();
1151 if (IsTop()) {
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());
1169 if (shistory) {
1170 CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory);
1171 nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry);
1172 bool didRemove;
1173 AutoTArray<nsID, 16> ids({GetHistoryID()});
1174 shistory->RemoveEntries(ids, shistory->GetIndexOfEntry(root), &didRemove);
1175 if (didRemove) {
1176 RefPtr<BrowsingContext> rootBC = shistory->GetBrowsingContext();
1177 if (rootBC) {
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) {
1195 NS_ERROR(
1196 "aRequireUserInteraction may only be used with an offset of -1 or 1");
1197 return Nothing();
1200 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory());
1201 if (!shistory) {
1202 return Nothing();
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)));
1213 while (true) {
1214 index += aOffset;
1215 if (!index.isValid()) {
1216 MOZ_LOG(gSHLog, LogLevel::Debug, ("Invalid index"));
1217 return Nothing();
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) {
1225 break;
1227 if (shistory->HasUserInteractionAtIndex(index.value())) {
1228 break;
1232 // Implement aborting additional history navigations from within the same
1233 // event spin of the content process.
1235 uint64_t epoch;
1236 bool sameEpoch = false;
1237 Maybe<ContentParentId> id;
1238 shistory->GetEpoch(epoch, id);
1240 if (aContentId == id && epoch >= aHistoryEpoch) {
1241 sameEpoch = true;
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)"));
1253 return Nothing();
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();
1271 if (!element) {
1272 return;
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;
1287 if (mCurrentLoad) {
1288 mCurrentLoad->Cancel(NS_BINDING_ABORTED,
1289 "CanonicalBrowsingContext::CanonicalDiscard"_ns);
1292 if (mWebProgress) {
1293 RefPtr<BrowsingContextWebProgress> progress = mWebProgress;
1294 progress->ContextDiscarded();
1297 if (IsTop()) {
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);
1316 mPendingDiscards++;
1319 void CanonicalBrowsingContext::RemovePendingDiscard() {
1320 mPendingDiscards--;
1321 if (!mPendingDiscards) {
1322 mFullyDiscarded = true;
1323 auto listeners = std::move(mFullyDiscardedListeners);
1324 for (const auto& listener : listeners) {
1325 listener(Id());
1330 void CanonicalBrowsingContext::AddFinalDiscardListener(
1331 std::function<void(uint64_t)>&& aListener) {
1332 if (mFullyDiscarded) {
1333 aListener(Id());
1334 return;
1336 mFullyDiscardedListeners.AppendElement(std::move(aListener));
1339 void CanonicalBrowsingContext::SetForceAppWindowActive(bool aForceActive,
1340 ErrorResult& aRv) {
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) {
1349 return;
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) {
1372 return;
1375 SetIsActiveInternal(isNowActive, IgnoreErrors());
1376 if (widget) {
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()) {
1385 return;
1388 MOZ_DIAGNOSTIC_ASSERT(aPrivateBrowsing == UsePrivateBrowsing());
1389 if (aPrivateBrowsing) {
1390 IncreasePrivateCount();
1391 } else {
1392 DecreasePrivateCount();
1396 void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() {
1397 WindowContext* windowContext = GetCurrentWindowContext();
1398 if (!windowContext) {
1399 return;
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,
1407 Id());
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,
1418 ErrorResult& aRv) {
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();
1450 /* static */
1451 bool CanonicalBrowsingContext::IsPrivateBrowsingActive() {
1452 return gNumberOfPrivateContexts > 0;
1455 void CanonicalBrowsingContext::UpdateMediaControlAction(
1456 const MediaControlAction& aAction) {
1457 if (IsDiscarded()) {
1458 return;
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)) {
1475 aError.Throw(rv);
1476 return;
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);
1491 return;
1494 if (NS_FAILED(rv)) {
1495 aError.Throw(rv);
1496 return;
1499 LoadURI(loadState, true);
1502 void CanonicalBrowsingContext::GoBack(
1503 const Optional<int32_t>& aCancelContentJSEpoch,
1504 bool aRequireUserInteraction, bool aUserActivation) {
1505 if (IsDiscarded()) {
1506 return;
1509 // Stop any known network loads if necessary.
1510 if (mCurrentLoad) {
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()) {
1532 return;
1535 // Stop any known network loads if necessary.
1536 if (mCurrentLoad) {
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()) {
1558 return;
1561 // Stop any known network loads if necessary.
1562 if (mCurrentLoad) {
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,
1577 aUserActivation);
1580 void CanonicalBrowsingContext::Reload(uint32_t aReloadFlags) {
1581 if (IsDiscarded()) {
1582 return;
1585 // Stop any known network loads if necessary.
1586 if (mCurrentLoad) {
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()) {
1599 return;
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() {
1618 if (!mPromise) {
1619 return;
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(); });
1629 return;
1633 ProcessReady();
1636 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() {
1637 if (!mPromise) {
1638 return;
1641 MOZ_ASSERT(!mProcessReady);
1642 mProcessReady = true;
1643 MaybeFinish();
1646 void CanonicalBrowsingContext::PendingRemotenessChange::MaybeFinish() {
1647 if (!mPromise) {
1648 return;
1651 if (!mProcessReady || mWaitingForPrepareToChange) {
1652 return;
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!");
1660 Cancel(rv);
1661 } else {
1662 Clear();
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
1681 // be aborted.
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();
1693 if (!browser) {
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)) {
1720 return 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,
1727 /* notify */ true);
1729 // The process has been created, hand off to nsFrameLoaderOwner to finish
1730 // the process switch.
1731 ErrorResult error;
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))) {
1742 return rv;
1745 // We did it! The process switch is complete.
1746 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
1747 RefPtr<BrowserParent> newBrowser = frameLoader->GetBrowserParent();
1748 if (!newBrowser) {
1749 if (mContentParent) {
1750 // Failed to create the BrowserParent somehow! Abort the process switch
1751 // attempt.
1752 return NS_ERROR_UNEXPECTED;
1755 if (!loadResumed) {
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)) {
1764 return rv;
1767 } else if (!loadResumed) {
1768 newBrowser->ResumeLoad(mPendingSwitchId);
1771 mPromise->Resolve(newBrowser, __func__);
1772 return NS_OK;
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
1782 // be aborted.
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;
1816 if (wasRemote) {
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
1822 // been destroyed.
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(
1837 mPendingSwitchId,
1838 "We always have a PendingSwitchId, except for print-preview loads, "
1839 "which will never perform a process-switch to being in-process with "
1840 "their embedder");
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__);
1847 return NS_OK;
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.
1878 if (wasRemote) {
1879 target->ShowSubframeCrashedUI(oldBrowser->GetBrowserBridgeParent());
1881 return rv;
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;
1891 if (!wasRemote) {
1892 clearChildID = Some(embedderBrowser->Manager()->ChildID());
1893 target->StartUnloadingHost(*clearChildID);
1895 auto callback = [target, clearChildID](auto&&) {
1896 if (clearChildID) {
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,
1906 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__);
1916 return NS_OK;
1919 void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv) {
1920 if (!mPromise) {
1921 return;
1924 mPromise->Reject(aRv, __func__);
1925 Clear();
1928 void CanonicalBrowsingContext::PendingRemotenessChange::Clear() {
1929 // Make sure we don't die while we're doing cleanup.
1930 RefPtr<PendingRemotenessChange> kungFuDeathGrip(this);
1931 if (mTarget) {
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;
1949 mPromise = nullptr;
1950 mTarget = nullptr;
1953 CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange(
1954 CanonicalBrowsingContext* aTarget, RemotenessPromise::Private* aPromise,
1955 uint64_t aPendingSwitchId, const NavigationIsolationOptions& aOptions)
1956 : mTarget(aTarget),
1957 mPromise(aPromise),
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 "
2012 "frames");
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();
2068 if (!browser) {
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)) {
2076 change->Cancel(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&) {
2089 change->Cancel(
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(
2099 aPendingSwitchId,
2100 "We always have a PendingSwitchId, except for print-preview loads, "
2101 "which will never perform a process-switch to being in-process with "
2102 "their embedder");
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();
2146 bool preferUsed =
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); });
2168 } else {
2169 change->ProcessLaunched();
2171 return promise.forget();
2174 void CanonicalBrowsingContext::MaybeSetPermanentKey(Element* aEmbedder) {
2175 MOZ_DIAGNOSTIC_ASSERT(IsTop());
2177 if (aEmbedder) {
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.
2190 if (GetParent()) {
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()) {
2216 return false;
2219 // Session-history-in-parent implementation relies currently on getting a
2220 // round trip through a child process.
2221 if (aLoadState->LoadIsFromSessionHistory()) {
2222 return false;
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())) {
2230 return false;
2233 if (WindowGlobalParent* global = GetCurrentWindowGlobal()) {
2234 nsCOMPtr<nsIURI> currentURI = global->GetDocumentURI();
2235 if (currentURI) {
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.
2244 return false;
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()) {
2250 return false;
2253 *aOuterWindowId = global->OuterWindowId();
2255 return true;
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()) {
2266 return false;
2269 uint64_t outerWindowId = 0;
2270 if (!SupportsLoadingInParent(aLoadState, &outerWindowId)) {
2271 return false;
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,
2283 aSetNavigating);
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())) {
2294 return false;
2297 uint64_t outerWindowId = 0;
2298 if (!SupportsLoadingInParent(aLoadState, &outerWindowId)) {
2299 return false;
2302 // If we successfully open the DocumentChannel, then it'll register
2303 // itself using aLoadIdentifier and be kept alive until it completes
2304 // loading.
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() &&
2313 mCurrentLoad) {
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;
2326 return false;
2329 return true;
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)));
2347 } else {
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() {
2366 nsID changeID = {};
2367 CallerWillNotifyHistoryIndexAndLengthChanges caller(nullptr);
2368 HistoryCommitIndexAndLength(changeID, caller);
2370 void CanonicalBrowsingContext::HistoryCommitIndexAndLength(
2371 const nsID& aChangeID,
2372 const CallerWillNotifyHistoryIndexAndLengthChanges& aProofOfCaller) {
2373 if (!IsTop()) {
2374 Cast(Top())->HistoryCommitIndexAndLength(aChangeID, aProofOfCaller);
2375 return;
2378 nsISHistory* shistory = GetSessionHistory();
2379 if (!shistory) {
2380 return;
2382 int32_t index = 0;
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,
2392 aChangeID);
2396 void CanonicalBrowsingContext::SynchronizeLayoutHistoryState() {
2397 if (mActiveEntry) {
2398 if (IsInProcess()) {
2399 nsIDocShell* docShell = GetDocShell();
2400 if (docShell) {
2401 docShell->PersistLayoutHistoryState();
2403 nsCOMPtr<nsILayoutHistoryState> state;
2404 docShell->GetLayoutHistoryState(getter_AddRefs(state));
2405 if (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>>&
2414 aResult) {
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));
2422 []() {});
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
2430 // context.
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()) {
2443 return;
2446 if (NS_WARN_IF(NS_FAILED(SetHasRestoreData(true)))) {
2447 aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2448 return;
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);
2460 return nullptr;
2463 void CanonicalBrowsingContext::ClearRestoreState() {
2464 if (!mRestoreState) {
2465 MOZ_DIAGNOSTIC_ASSERT(!GetHasRestoreData());
2466 return;
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) {
2480 return;
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();
2499 return;
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
2506 // been sent out.
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) {
2514 state->mResolves++;
2515 if (!state->mData && state->mRequests == state->mResolves) {
2516 state->Resolve();
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);
2530 return;
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();
2544 mPromise = nullptr;
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);
2551 if (!funcs) {
2552 return NS_ERROR_FAILURE;
2555 nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
2556 AutoJSAPI jsapi;
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,
2568 storage);
2569 if (!ToJSValue(jsapi.cx(), storage, &update)) {
2570 return NS_ERROR_FAILURE;
2572 } else {
2573 update.setNull();
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()) {
2583 aDone();
2584 return;
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);
2598 aDone();
2602 /* static */
2603 void CanonicalBrowsingContext::UpdateSessionStoreForStorage(
2604 uint64_t aBrowsingContextId) {
2605 RefPtr<CanonicalBrowsingContext> browsingContext = Get(aBrowsingContextId);
2607 if (!browsingContext) {
2608 return;
2611 browsingContext->UpdateSessionStoreSessionStorage([]() {});
2614 void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() {
2615 if (!StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
2616 return;
2619 if (!IsTop()) {
2620 Top()->MaybeScheduleSessionStoreUpdate();
2621 return;
2624 if (IsInBFCache()) {
2625 return;
2628 if (mSessionStoreSessionStorageUpdateTimer) {
2629 return;
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()) {
2643 return;
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");
2678 return;
2680 if (mCrossGroupOpenerId != 0) {
2681 aRv.ThrowNotAllowedError("Can only set crossGroupOpener once");
2682 return;
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
2697 // that it has.
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) {
2703 callback();
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
2723 // content.
2724 if (aAbnormalShutdown) {
2725 ShowSubframeCrashedUI(aBrowserParent->GetBrowserBridgeParent());
2730 void CanonicalBrowsingContext::ShowSubframeCrashedUI(
2731 BrowserBridgeParent* aBridge) {
2732 if (!aBridge || IsDiscarded() || !aBridge->CanSend()) {
2733 return;
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
2741 // this.
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) {
2754 if (aIsSubDoc) {
2755 nsAutoCString uri("[no uri]");
2756 nsCOMPtr<nsIURI> currentURI =
2757 aBrowsingContext->Canonical()->GetCurrentURI();
2758 if (currentURI) {
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();
2808 if (currentURI) {
2809 uri = currentURI->GetSpecOrDefault();
2811 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, ("Checking %s", uri.get()));
2814 if (IsInProcess()) {
2815 return false;
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"));
2844 if (aNewURI) {
2845 bool equalUri = false;
2846 aNewURI->Equals(currentURI, &equalUri);
2847 if (equalUri) {
2848 // When loading the same uri, disable bfcache so that
2849 // nsDocShell::OnNewURI transforms the load to LOAD_NORMAL_REPLACE.
2850 return false;
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;
2861 if (wgp) {
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();
2883 if (currentURI) {
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,
2906 aRv);
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;
2929 mClonePromise =
2930 ChangeRemoteness(options, /* aPendingSwitchId = */ 0)
2931 ->Then(
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)
2939 ->Then(
2940 GetMainThreadSerialEventTarget(), __func__,
2941 [bridge](
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).
2951 if (bridge) {
2952 Unused << bridge->SendMaybeFireEmbedderLoadEvents(
2953 EmbedderElementEventType::NoEvent);
2955 if (aValue.IsResolve() && aValue.ResolveValue()) {
2956 return GenericNonExclusivePromise::CreateAndResolve(
2957 true, __func__);
2959 return GenericNonExclusivePromise::CreateAndReject(
2960 NS_ERROR_FAILURE, __func__);
2963 [](nsresult aRv) -> RefPtr<GenericNonExclusivePromise> {
2964 NS_WARNING(
2965 nsPrintfCString("Remote clone failed: %x\n", unsigned(aRv))
2966 .get());
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,
2977 float aAnchorY,
2978 nsViewID aScrollId,
2979 uint32_t aPresShellId) {
2980 nsCOMPtr<nsIWidget> widget;
2981 mozilla::layers::LayersId layersId{0};
2983 if (IsInProcess()) {
2984 nsCOMPtr<nsPIDOMWindowOuter> outer = GetDOMWindow();
2985 if (!outer) {
2986 return false;
2989 widget = widget::WidgetUtils::DOMWindowToWidget(outer);
2990 if (widget) {
2991 layersId = widget->GetRootLayerTreeId();
2993 } else {
2994 RefPtr<BrowserParent> parent = GetBrowserParent();
2995 if (!parent) {
2996 return false;
2999 widget = parent->GetWidget();
3000 layersId = parent->GetLayersId();
3003 if (!widget || !widget->AsyncPanZoomEnabled()) {
3004 return false;
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),
3019 guid);
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();
3029 if (!outer) {
3030 return;
3033 widget = widget::WidgetUtils::DOMWindowToWidget(outer);
3034 if (widget) {
3035 layersId = widget->GetRootLayerTreeId();
3037 } else {
3038 RefPtr<BrowserParent> parent = GetBrowserParent();
3039 if (!parent) {
3040 return;
3043 widget = parent->GetWidget();
3044 layersId = parent->GetLayersId();
3047 if (!widget || !widget->AsyncPanZoomEnabled()) {
3048 return;
3051 mozilla::layers::ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId);
3052 widget->StopAsyncAutoscroll(guid);
3055 already_AddRefed<nsISHEntry>
3056 CanonicalBrowsingContext::GetMostRecentLoadingSessionHistoryEntry() {
3057 if (mLoadingEntries.IsEmpty()) {
3058 return nullptr;
3061 RefPtr<SessionHistoryEntry> entry = mLoadingEntries.LastElement().mEntry;
3062 return entry.forget();
3065 already_AddRefed<BounceTrackingState>
3066 CanonicalBrowsingContext::GetBounceTrackingState() {
3067 if (!mWebProgress) {
3068 return nullptr;
3070 return mWebProgress->GetBounceTrackingState();
3073 NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext)
3075 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext,
3076 BrowsingContext)
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,
3087 BrowsingContext)
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,
3094 BrowsingContext)
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