CanonicalBrowsingContext.cpp (140608B)
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/. */ 6 7 #include "mozilla/dom/CanonicalBrowsingContext.h" 8 9 #include "ContentAnalysis.h" 10 #include "ErrorList.h" 11 #include "mozilla/CheckedInt.h" 12 #include "mozilla/Components.h" 13 #include "mozilla/ErrorResult.h" 14 #include "mozilla/EventForwards.h" 15 #include "mozilla/AsyncEventDispatcher.h" 16 #include "mozilla/dom/BrowserParent.h" 17 #include "mozilla/dom/BrowsingContextBinding.h" 18 #include "mozilla/dom/BrowsingContextGroup.h" 19 #include "mozilla/dom/ContentParent.h" 20 #include "mozilla/dom/EventTarget.h" 21 #include "mozilla/dom/Navigation.h" 22 #include "mozilla/dom/NavigationUtils.h" 23 #include "mozilla/dom/PBrowserParent.h" 24 #include "mozilla/dom/PBackgroundSessionStorageCache.h" 25 #include "mozilla/dom/PWindowGlobalParent.h" 26 #include "mozilla/dom/Promise.h" 27 #include "mozilla/dom/Promise-inl.h" 28 #include "mozilla/dom/WindowGlobalParent.h" 29 #include "mozilla/dom/ContentProcessManager.h" 30 #include "mozilla/dom/MediaController.h" 31 #include "mozilla/dom/MediaControlService.h" 32 #include "mozilla/dom/ContentPlaybackController.h" 33 #include "mozilla/dom/SessionStorageManager.h" 34 #include "mozilla/ipc/ProtocolUtils.h" 35 #include "mozilla/layers/CompositorBridgeChild.h" 36 #ifdef NS_PRINTING 37 # include "mozilla/layout/RemotePrintJobParent.h" 38 #endif 39 #include "mozilla/net/DocumentLoadListener.h" 40 #include "mozilla/NullPrincipal.h" 41 #include "mozilla/StaticPrefs_browser.h" 42 #include "mozilla/StaticPrefs_docshell.h" 43 #include "mozilla/StaticPrefs_fission.h" 44 #include "mozilla/StaticPrefs_security.h" 45 #include "mozilla/glean/DomMetrics.h" 46 #include "nsILayoutHistoryState.h" 47 #include "nsIParentalControlsService.h" 48 #include "nsIPrintSettings.h" 49 #include "nsIPrintSettingsService.h" 50 #include "nsISupports.h" 51 #include "nsIWebNavigation.h" 52 #include "nsDocShell.h" 53 #include "nsFrameLoader.h" 54 #include "nsFrameLoaderOwner.h" 55 #include "nsGlobalWindowOuter.h" 56 #include "nsIContentAnalysis.h" 57 #include "nsIWebBrowserChrome.h" 58 #include "nsIXULRuntime.h" 59 #include "nsNetUtil.h" 60 #include "nsSHistory.h" 61 #include "nsSecureBrowserUI.h" 62 #include "nsQueryObject.h" 63 #include "nsBrowserStatusFilter.h" 64 #include "nsIBrowser.h" 65 #include "nsTHashSet.h" 66 #include "nsISessionStoreFunctions.h" 67 #include "nsIXPConnect.h" 68 #include "nsImportModule.h" 69 #include "UnitTransforms.h" 70 #include "nsIOpenWindowInfo.h" 71 #include "nsOpenWindowInfo.h" 72 73 using namespace mozilla::ipc; 74 75 extern mozilla::LazyLogModule gAutoplayPermissionLog; 76 extern mozilla::LazyLogModule gNavigationAPILog; 77 extern mozilla::LazyLogModule gSHLog; 78 extern mozilla::LazyLogModule gSHIPBFCacheLog; 79 80 #define AUTOPLAY_LOG(msg, ...) \ 81 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) 82 83 static mozilla::LazyLogModule sPBContext("PBContext"); 84 85 // Global count of canonical browsing contexts with the private attribute set 86 static uint32_t gNumberOfPrivateContexts = 0; 87 88 static void IncreasePrivateCount() { 89 gNumberOfPrivateContexts++; 90 MOZ_LOG(sPBContext, mozilla::LogLevel::Debug, 91 ("%s: Private browsing context count %d -> %d", __func__, 92 gNumberOfPrivateContexts - 1, gNumberOfPrivateContexts)); 93 if (gNumberOfPrivateContexts > 1) { 94 return; 95 } 96 97 static bool sHasSeenPrivateContext = false; 98 if (!sHasSeenPrivateContext) { 99 sHasSeenPrivateContext = true; 100 mozilla::glean::dom_parentprocess::private_window_used.Set(true); 101 } 102 } 103 104 static void DecreasePrivateCount() { 105 MOZ_ASSERT(gNumberOfPrivateContexts > 0); 106 gNumberOfPrivateContexts--; 107 108 MOZ_LOG(sPBContext, mozilla::LogLevel::Debug, 109 ("%s: Private browsing context count %d -> %d", __func__, 110 gNumberOfPrivateContexts + 1, gNumberOfPrivateContexts)); 111 if (!gNumberOfPrivateContexts && 112 !mozilla::StaticPrefs::browser_privatebrowsing_autostart()) { 113 nsCOMPtr<nsIObserverService> observerService = 114 mozilla::services::GetObserverService(); 115 if (observerService) { 116 MOZ_LOG(sPBContext, mozilla::LogLevel::Debug, 117 ("%s: last-pb-context-exited fired", __func__)); 118 observerService->NotifyObservers(nullptr, "last-pb-context-exited", 119 nullptr); 120 } 121 } 122 } 123 124 namespace mozilla::dom { 125 126 extern mozilla::LazyLogModule gUserInteractionPRLog; 127 128 #define USER_ACTIVATION_LOG(msg, ...) \ 129 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) 130 131 CanonicalBrowsingContext::CanonicalBrowsingContext(WindowContext* aParentWindow, 132 BrowsingContextGroup* aGroup, 133 uint64_t aBrowsingContextId, 134 uint64_t aOwnerProcessId, 135 uint64_t aEmbedderProcessId, 136 BrowsingContext::Type aType, 137 FieldValues&& aInit) 138 : BrowsingContext(aParentWindow, aGroup, aBrowsingContextId, aType, 139 std::move(aInit)), 140 mProcessId(aOwnerProcessId), 141 mEmbedderProcessId(aEmbedderProcessId), 142 mPermanentKey(JS::NullValue()) { 143 // You are only ever allowed to create CanonicalBrowsingContexts in the 144 // parent process. 145 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 146 147 // The initial URI in a BrowsingContext is always "about:blank". 148 MOZ_ALWAYS_SUCCEEDS( 149 NS_NewURI(getter_AddRefs(mCurrentRemoteURI), "about:blank")); 150 151 mozilla::HoldJSObjects(this); 152 } 153 154 CanonicalBrowsingContext::~CanonicalBrowsingContext() { 155 mPermanentKey.setNull(); 156 157 mozilla::DropJSObjects(this); 158 159 if (mSessionHistory) { 160 mSessionHistory->SetBrowsingContext(nullptr); 161 } 162 163 mActiveEntryList = nullptr; 164 } 165 166 /* static */ 167 already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Get( 168 uint64_t aId) { 169 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 170 return BrowsingContext::Get(aId).downcast<CanonicalBrowsingContext>(); 171 } 172 173 /* static */ 174 CanonicalBrowsingContext* CanonicalBrowsingContext::Cast( 175 BrowsingContext* aContext) { 176 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 177 return static_cast<CanonicalBrowsingContext*>(aContext); 178 } 179 180 /* static */ 181 const CanonicalBrowsingContext* CanonicalBrowsingContext::Cast( 182 const BrowsingContext* aContext) { 183 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 184 return static_cast<const CanonicalBrowsingContext*>(aContext); 185 } 186 187 already_AddRefed<CanonicalBrowsingContext> CanonicalBrowsingContext::Cast( 188 already_AddRefed<BrowsingContext>&& aContext) { 189 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 190 return aContext.downcast<CanonicalBrowsingContext>(); 191 } 192 193 ContentParent* CanonicalBrowsingContext::GetContentParent() const { 194 if (mProcessId == 0) { 195 return nullptr; 196 } 197 198 ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); 199 if (!cpm) { 200 return nullptr; 201 } 202 return cpm->GetContentProcessById(ContentParentId(mProcessId)); 203 } 204 205 void CanonicalBrowsingContext::GetCurrentRemoteType(nsACString& aRemoteType, 206 ErrorResult& aRv) const { 207 // If we're in the parent process, dump out the void string. 208 if (mProcessId == 0) { 209 aRemoteType = NOT_REMOTE_TYPE; 210 return; 211 } 212 213 ContentParent* cp = GetContentParent(); 214 if (!cp) { 215 aRv.Throw(NS_ERROR_UNEXPECTED); 216 return; 217 } 218 219 aRemoteType = cp->GetRemoteType(); 220 } 221 222 void CanonicalBrowsingContext::SetOwnerProcessId(uint64_t aProcessId) { 223 MOZ_LOG(GetLog(), LogLevel::Debug, 224 ("SetOwnerProcessId for 0x%08" PRIx64 " (0x%08" PRIx64 225 " -> 0x%08" PRIx64 ")", 226 Id(), mProcessId, aProcessId)); 227 228 mProcessId = aProcessId; 229 } 230 231 nsISecureBrowserUI* CanonicalBrowsingContext::GetSecureBrowserUI() { 232 if (!IsTop()) { 233 return nullptr; 234 } 235 if (!mSecureBrowserUI) { 236 mSecureBrowserUI = new nsSecureBrowserUI(this); 237 } 238 return mSecureBrowserUI; 239 } 240 241 void CanonicalBrowsingContext::ReplacedBy( 242 CanonicalBrowsingContext* aNewContext, 243 const NavigationIsolationOptions& aRemotenessOptions) { 244 MOZ_ASSERT(!aNewContext->mWebProgress); 245 MOZ_ASSERT(!aNewContext->mSessionHistory); 246 MOZ_ASSERT(IsTop() && aNewContext->IsTop()); 247 248 mIsReplaced = true; 249 aNewContext->mIsReplaced = false; 250 251 if (mStatusFilter) { 252 mStatusFilter->RemoveProgressListener(mDocShellProgressBridge); 253 mStatusFilter = nullptr; 254 } 255 256 mWebProgress->ContextReplaced(aNewContext); 257 aNewContext->mWebProgress = std::move(mWebProgress); 258 259 // Use the Transaction for the fields which need to be updated whether or not 260 // the new context has been attached before. 261 // SetWithoutSyncing can be used if context hasn't been attached. 262 Transaction txn; 263 txn.SetBrowserId(GetBrowserId()); 264 txn.SetIsAppTab(GetIsAppTab()); 265 txn.SetIsCaptivePortalTab(GetIsCaptivePortalTab()); 266 txn.SetHasSiblings(GetHasSiblings()); 267 txn.SetTopLevelCreatedByWebContent(GetTopLevelCreatedByWebContent()); 268 txn.SetHistoryID(GetHistoryID()); 269 txn.SetExplicitActive(GetExplicitActive()); 270 txn.SetEmbedderColorSchemes(GetEmbedderColorSchemes()); 271 txn.SetHasRestoreData(GetHasRestoreData()); 272 txn.SetShouldDelayMediaFromStart(GetShouldDelayMediaFromStart()); 273 txn.SetForceOffline(GetForceOffline()); 274 txn.SetTopInnerSizeForRFP(GetTopInnerSizeForRFP()); 275 txn.SetIPAddressSpace(GetIPAddressSpace()); 276 txn.SetParentalControlsEnabled(GetParentalControlsEnabled()); 277 278 if (!GetLanguageOverride().IsEmpty()) { 279 // Reapply language override to update the corresponding realm. 280 txn.SetLanguageOverride(GetLanguageOverride()); 281 } 282 if (!GetTimezoneOverride().IsEmpty()) { 283 // Reapply timezone override to update the corresponding realm. 284 txn.SetTimezoneOverride(GetTimezoneOverride()); 285 } 286 287 // Propagate some settings on BrowsingContext replacement so they're not lost 288 // on bfcached navigations. These are important for GeckoView (see bug 289 // 1781936). 290 txn.SetAllowJavascript(GetAllowJavascript()); 291 txn.SetForceEnableTrackingProtection(GetForceEnableTrackingProtection()); 292 txn.SetUserAgentOverride(GetUserAgentOverride()); 293 txn.SetSuspendMediaWhenInactive(GetSuspendMediaWhenInactive()); 294 txn.SetDisplayMode(GetDisplayMode()); 295 txn.SetForceDesktopViewport(GetForceDesktopViewport()); 296 txn.SetIsUnderHiddenEmbedderElement(GetIsUnderHiddenEmbedderElement()); 297 298 // When using site-specific zoom, we let the frontend manage the zoom level 299 // of BFCache'd contexts. Overriding those zoom levels can cause weirdness 300 // like bug 1846141. We always copy to new contexts to avoid bug 1914149. 301 if (!aNewContext->EverAttached() || 302 !StaticPrefs::browser_zoom_siteSpecific()) { 303 txn.SetFullZoom(GetFullZoom()); 304 txn.SetTextZoom(GetTextZoom()); 305 } 306 307 // Propagate the default load flags so that the TRR mode flags are forwarded 308 // to the new browsing context. See bug 1828643. 309 txn.SetDefaultLoadFlags(GetDefaultLoadFlags()); 310 311 // As this is a different BrowsingContext, set InitialSandboxFlags to the 312 // current flags in the new context so that they also apply to any initial 313 // about:blank documents created in it. 314 txn.SetSandboxFlags(GetSandboxFlags()); 315 txn.SetInitialSandboxFlags(GetSandboxFlags()); 316 txn.SetTargetTopLevelLinkClicksToBlankInternal( 317 TargetTopLevelLinkClicksToBlank()); 318 if (aNewContext->EverAttached()) { 319 MOZ_ALWAYS_SUCCEEDS(txn.Commit(aNewContext)); 320 } else { 321 txn.CommitWithoutSyncing(aNewContext); 322 } 323 324 aNewContext->mRestoreState = mRestoreState.forget(); 325 Transaction selfTxn; 326 selfTxn.SetHasRestoreData(false); 327 selfTxn.SetExplicitActive(ExplicitActiveStatus::Inactive); 328 MOZ_ALWAYS_SUCCEEDS(selfTxn.Commit(this)); 329 330 // XXXBFCache name handling is still a bit broken in Fission in general, 331 // at least in case name should be cleared. 332 if (aRemotenessOptions.mTryUseBFCache) { 333 MOZ_ASSERT(!aNewContext->EverAttached()); 334 aNewContext->mFields.SetWithoutSyncing<IDX_Name>(GetName()); 335 // We don't copy over HasLoadedNonInitialDocument here, we'll actually end 336 // up loading a new initial document at this point, before the real load. 337 // The real load will then end up setting HasLoadedNonInitialDocument to 338 // true. 339 } 340 341 if (mSessionHistory) { 342 mSessionHistory->SetBrowsingContext(aNewContext); 343 // At this point we will be creating a new ChildSHistory in the child. 344 // That means that the child's epoch will be reset, so it makes sense to 345 // reset the epoch in the parent too. 346 mSessionHistory->SetEpoch(0, Nothing()); 347 mSessionHistory.swap(aNewContext->mSessionHistory); 348 RefPtr<ChildSHistory> childSHistory = ForgetChildSHistory(); 349 aNewContext->SetChildSHistory(childSHistory); 350 } 351 352 BackgroundSessionStorageManager::PropagateManager(Id(), aNewContext->Id()); 353 354 // Transfer the ownership of the priority active status from the old context 355 // to the new context. 356 aNewContext->mPriorityActive = mPriorityActive; 357 mPriorityActive = false; 358 359 MOZ_ASSERT(aNewContext->mLoadingEntries.IsEmpty()); 360 mLoadingEntries.SwapElements(aNewContext->mLoadingEntries); 361 MOZ_ASSERT(!aNewContext->mActiveEntry); 362 mActiveEntry.swap(aNewContext->mActiveEntry); 363 if (Navigation::IsAPIEnabled()) { 364 MOZ_ASSERT(!aNewContext->mActiveEntryList); 365 aNewContext->mActiveEntryList = std::move(mActiveEntryList); 366 } 367 368 aNewContext->mPermanentKey = mPermanentKey; 369 mPermanentKey.setNull(); 370 } 371 372 void CanonicalBrowsingContext::UpdateSecurityState() { 373 if (mSecureBrowserUI) { 374 mSecureBrowserUI->RecomputeSecurityFlags(); 375 } 376 } 377 378 void CanonicalBrowsingContext::GetWindowGlobals( 379 nsTArray<RefPtr<WindowGlobalParent>>& aWindows) { 380 aWindows.SetCapacity(GetWindowContexts().Length()); 381 for (auto& window : GetWindowContexts()) { 382 aWindows.AppendElement(static_cast<WindowGlobalParent*>(window.get())); 383 } 384 } 385 386 WindowGlobalParent* CanonicalBrowsingContext::GetCurrentWindowGlobal() const { 387 return static_cast<WindowGlobalParent*>(GetCurrentWindowContext()); 388 } 389 390 WindowGlobalParent* CanonicalBrowsingContext::GetParentWindowContext() { 391 return static_cast<WindowGlobalParent*>( 392 BrowsingContext::GetParentWindowContext()); 393 } 394 395 WindowGlobalParent* CanonicalBrowsingContext::GetTopWindowContext() { 396 return static_cast<WindowGlobalParent*>( 397 BrowsingContext::GetTopWindowContext()); 398 } 399 400 already_AddRefed<nsIWidget> 401 CanonicalBrowsingContext::GetParentProcessWidgetContaining() { 402 // If our document is loaded in-process, such as chrome documents, get the 403 // widget directly from our outer window. Otherwise, try to get the widget 404 // from the toplevel content's browser's element. 405 nsCOMPtr<nsIWidget> widget; 406 if (nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetDOMWindow())) { 407 widget = window->GetNearestWidget(); 408 } else if (Element* topEmbedder = Top()->GetEmbedderElement()) { 409 widget = nsContentUtils::WidgetForContent(topEmbedder); 410 if (!widget) { 411 widget = nsContentUtils::WidgetForDocument(topEmbedder->OwnerDoc()); 412 } 413 } 414 415 if (widget) { 416 widget = widget->GetTopLevelWidget(); 417 } 418 419 return widget.forget(); 420 } 421 422 already_AddRefed<nsIBrowserDOMWindow> 423 CanonicalBrowsingContext::GetBrowserDOMWindow() { 424 RefPtr<CanonicalBrowsingContext> chromeTop = TopCrossChromeBoundary(); 425 nsGlobalWindowOuter* topWin; 426 if ((topWin = nsGlobalWindowOuter::Cast(chromeTop->GetDOMWindow())) && 427 topWin->IsChromeWindow()) { 428 return do_AddRef(topWin->GetBrowserDOMWindow()); 429 } 430 return nullptr; 431 } 432 433 already_AddRefed<WindowGlobalParent> 434 CanonicalBrowsingContext::GetEmbedderWindowGlobal() const { 435 uint64_t windowId = GetEmbedderInnerWindowId(); 436 if (windowId == 0) { 437 return nullptr; 438 } 439 440 return WindowGlobalParent::GetByInnerWindowId(windowId); 441 } 442 443 CanonicalBrowsingContext* 444 CanonicalBrowsingContext::GetParentCrossChromeBoundary() { 445 if (GetParent()) { 446 return Cast(GetParent()); 447 } 448 if (auto* embedder = GetEmbedderElement()) { 449 return Cast(embedder->OwnerDoc()->GetBrowsingContext()); 450 } 451 return nullptr; 452 } 453 454 CanonicalBrowsingContext* CanonicalBrowsingContext::TopCrossChromeBoundary() { 455 CanonicalBrowsingContext* bc = this; 456 while (auto* parent = bc->GetParentCrossChromeBoundary()) { 457 bc = parent; 458 } 459 return bc; 460 } 461 462 Nullable<WindowProxyHolder> CanonicalBrowsingContext::GetTopChromeWindow() { 463 RefPtr<CanonicalBrowsingContext> bc = TopCrossChromeBoundary(); 464 if (bc->IsChrome()) { 465 return WindowProxyHolder(bc.forget()); 466 } 467 return nullptr; 468 } 469 470 nsISHistory* CanonicalBrowsingContext::GetSessionHistory() { 471 if (!IsTop()) { 472 return Cast(Top())->GetSessionHistory(); 473 } 474 475 // Check GetChildSessionHistory() to make sure that this BrowsingContext has 476 // session history enabled. 477 if (!mSessionHistory && GetChildSessionHistory()) { 478 mSessionHistory = new nsSHistory(this); 479 } 480 481 return mSessionHistory; 482 } 483 484 SessionHistoryEntry* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() { 485 return mActiveEntry; 486 } 487 488 void CanonicalBrowsingContext::SetActiveSessionHistoryEntryFromBFCache( 489 SessionHistoryEntry* aEntry) { 490 mActiveEntry = aEntry; 491 auto* activeEntries = GetActiveEntries(); 492 if (Navigation::IsAPIEnabled() && activeEntries) { 493 if (StaticPrefs::dom_navigation_api_strict_enabled()) { 494 MOZ_DIAGNOSTIC_ASSERT(!aEntry || activeEntries->contains(aEntry)); 495 MOZ_DIAGNOSTIC_ASSERT(aEntry || activeEntries->isEmpty()); 496 } else { 497 MOZ_ASSERT(!aEntry || activeEntries->contains(aEntry)); 498 MOZ_ASSERT(aEntry || activeEntries->isEmpty()); 499 } 500 } 501 } 502 503 bool CanonicalBrowsingContext::HasHistoryEntry(nsISHEntry* aEntry) { 504 // XXX Should we check also loading entries? 505 return aEntry && mActiveEntry == aEntry; 506 } 507 508 void CanonicalBrowsingContext::SwapHistoryEntries(nsISHEntry* aOldEntry, 509 nsISHEntry* aNewEntry) { 510 // XXX Should we check also loading entries? 511 if (mActiveEntry != aOldEntry) { 512 return; 513 } 514 515 nsCOMPtr<SessionHistoryEntry> newEntry = do_QueryInterface(aNewEntry); 516 auto* activeEntries = GetActiveEntries(); 517 MOZ_LOG(gSHLog, LogLevel::Verbose, 518 ("Swapping History Entries: mActiveEntry=%p, aNewEntry=%p. " 519 "Is in list? mActiveEntry %s, aNewEntry %s. " 520 "Is aNewEntry in current mActiveEntryList? %s.", 521 mActiveEntry.get(), aNewEntry, 522 mActiveEntry && mActiveEntry->isInList() ? "yes" : "no", 523 newEntry && newEntry->isInList() ? "yes" : "no", 524 activeEntries->contains(newEntry) ? "yes" : "no")); 525 if (!newEntry) { 526 activeEntries->clear(); 527 mActiveEntry = nullptr; 528 return; 529 } 530 if (Navigation::IsAPIEnabled() && mActiveEntry->isInList()) { 531 RefPtr beforeOldEntry = mActiveEntry->removeAndGetPrevious(); 532 if (beforeOldEntry != newEntry) { 533 if (newEntry->isInList()) { 534 newEntry->setNext(mActiveEntry); 535 newEntry->remove(); 536 } 537 538 if (beforeOldEntry) { 539 beforeOldEntry->setNext(newEntry); 540 } else { 541 activeEntries->insertFront(newEntry); 542 } 543 } else { 544 newEntry->setPrevious(mActiveEntry); 545 } 546 } 547 mActiveEntry = newEntry.forget(); 548 } 549 550 void CanonicalBrowsingContext::AddLoadingSessionHistoryEntry( 551 uint64_t aLoadId, SessionHistoryEntry* aEntry) { 552 (void)SetHistoryID(aEntry->DocshellID()); 553 mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{aLoadId, aEntry}); 554 } 555 556 void CanonicalBrowsingContext::GetLoadingSessionHistoryInfoFromParent( 557 Maybe<LoadingSessionHistoryInfo>& aLoadingInfo) { 558 nsISHistory* shistory = GetSessionHistory(); 559 if (!shistory || !GetParent()) { 560 return; 561 } 562 563 SessionHistoryEntry* parentSHE = 564 GetParent()->Canonical()->GetActiveSessionHistoryEntry(); 565 if (parentSHE) { 566 int32_t index = -1; 567 for (BrowsingContext* sibling : GetParent()->Children()) { 568 ++index; 569 if (sibling == this) { 570 nsCOMPtr<nsISHEntry> shEntry; 571 parentSHE->GetChildSHEntryIfHasNoDynamicallyAddedChild( 572 index, getter_AddRefs(shEntry)); 573 nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(shEntry); 574 if (she) { 575 aLoadingInfo.emplace(she); 576 mLoadingEntries.AppendElement(LoadingSessionHistoryEntry{ 577 aLoadingInfo.value().mLoadId, she.get()}); 578 (void)SetHistoryID(she->DocshellID()); 579 } 580 break; 581 } 582 } 583 } 584 } 585 586 UniquePtr<LoadingSessionHistoryInfo> 587 CanonicalBrowsingContext::CreateLoadingSessionHistoryEntryForLoad( 588 nsDocShellLoadState* aLoadState, SessionHistoryEntry* existingEntry, 589 nsIChannel* aChannel) { 590 RefPtr<SessionHistoryEntry> entry; 591 const LoadingSessionHistoryInfo* existingLoadingInfo = 592 aLoadState->GetLoadingSessionHistoryInfo(); 593 MOZ_ASSERT_IF(!existingLoadingInfo, !existingEntry); 594 if (existingLoadingInfo) { 595 if (existingEntry) { 596 entry = existingEntry; 597 } else { 598 MOZ_ASSERT(!existingLoadingInfo->mLoadIsFromSessionHistory); 599 600 SessionHistoryEntry::LoadingEntry* loadingEntry = 601 SessionHistoryEntry::GetByLoadId(existingLoadingInfo->mLoadId); 602 MOZ_LOG(gSHLog, LogLevel::Verbose, 603 ("SHEntry::GetByLoadId(%" PRIu64 ") -> %p", 604 existingLoadingInfo->mLoadId, entry.get())); 605 if (!loadingEntry) { 606 return nullptr; 607 } 608 entry = loadingEntry->mEntry; 609 } 610 611 // If the entry was updated, update also the LoadingSessionHistoryInfo. 612 UniquePtr<LoadingSessionHistoryInfo> lshi = 613 MakeUnique<LoadingSessionHistoryInfo>(entry, existingLoadingInfo); 614 aLoadState->SetLoadingSessionHistoryInfo(std::move(lshi)); 615 existingLoadingInfo = aLoadState->GetLoadingSessionHistoryInfo(); 616 (void)SetHistoryEntryCount(entry->BCHistoryLength()); 617 } else if (aLoadState->LoadType() == LOAD_REFRESH && 618 !ShouldAddEntryForRefresh(aLoadState->URI(), 619 aLoadState->PostDataStream()) && 620 mActiveEntry) { 621 entry = mActiveEntry; 622 } else { 623 entry = new SessionHistoryEntry(aLoadState, aChannel); 624 if (IsTop() && 625 !nsDocShell::ShouldAddToSessionHistory(aLoadState->URI(), aChannel)) { 626 entry->SetTransient(); 627 } 628 if (!IsTop() && (mActiveEntry || !mLoadingEntries.IsEmpty())) { 629 entry->SetIsSubFrame(true); 630 } 631 entry->SetDocshellID(GetHistoryID()); 632 entry->SetIsDynamicallyAdded(CreatedDynamically()); 633 entry->SetForInitialLoad(true); 634 } 635 MOZ_DIAGNOSTIC_ASSERT(entry); 636 637 if (aLoadState->GetNavigationType() == NavigationType::Replace) { 638 MaybeReuseNavigationKeyFromActiveEntry(entry); 639 } 640 641 UniquePtr<LoadingSessionHistoryInfo> loadingInfo; 642 if (existingLoadingInfo) { 643 loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(*existingLoadingInfo); 644 } else { 645 loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(entry); 646 mLoadingEntries.AppendElement( 647 LoadingSessionHistoryEntry{loadingInfo->mLoadId, entry}); 648 } 649 650 // When adding a new entry we need to make sure that the navigation object 651 // gets it's entries list initialized to the contiguous entries ending in the 652 // new entry. 653 if (Navigation::IsAPIEnabled()) { 654 bool sessionHistoryLoad = 655 existingLoadingInfo && existingLoadingInfo->mLoadIsFromSessionHistory; 656 657 if (sessionHistoryLoad && !mActiveEntry) { 658 auto* activeEntries = GetActiveEntries(); 659 if (activeEntries && activeEntries->isEmpty()) { 660 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 661 shistory->ReconstructContiguousEntryListFrom(entry); 662 } 663 } 664 665 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, 666 "Determining navigation type from loadType={}", 667 aLoadState->LoadType()); 668 Maybe<NavigationType> navigationType = 669 NavigationUtils::NavigationTypeFromLoadType(aLoadState->LoadType()); 670 if (!navigationType) { 671 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, 672 "Failed to determine navigation type"); 673 return loadingInfo; 674 } 675 676 loadingInfo->mTriggeringEntry = 677 mActiveEntry ? Some(mActiveEntry->Info()) : Nothing(); 678 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Verbose, 679 "Triggering entry was {}.", 680 fmt::ptr(loadingInfo->mTriggeringEntry 681 .map([](auto& entry) { return &entry; }) 682 .valueOr(nullptr))); 683 684 if (!existingLoadingInfo || 685 !existingLoadingInfo->mTriggeringNavigationType) { 686 loadingInfo->mTriggeringNavigationType = navigationType; 687 } 688 689 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Verbose, 690 "Triggering navigation type was {}.", *navigationType); 691 692 GetContiguousEntriesForLoad(*loadingInfo, entry); 693 694 if (MOZ_LOG_TEST(gNavigationAPILog, LogLevel::Debug)) { 695 int32_t index = 0; 696 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, 697 "Preparing contiguous for {} ({}load))", 698 entry->Info().GetURI()->GetSpecOrDefault(), 699 sessionHistoryLoad ? "history " : ""); 700 for (const auto& entry : loadingInfo->mContiguousEntries) { 701 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, 702 "{}+- {} SHI {} {}\n URL = {}", 703 (mActiveEntry && entry == mActiveEntry->Info()) ? ">" : " ", 704 index++, entry.NavigationKey().ToString().get(), 705 entry.NavigationId().ToString().get(), 706 entry.GetURI()->GetSpecOrDefault()); 707 } 708 } 709 710 [[maybe_unused]] auto pred = [&](auto& entry) { 711 return entry.NavigationKey() == loadingInfo->mInfo.NavigationKey(); 712 }; 713 if (StaticPrefs::dom_navigation_api_strict_enabled()) { 714 // https://bugzil.la/1989045 715 MOZ_DIAGNOSTIC_ASSERT( 716 mozilla::AnyOf(loadingInfo->mContiguousEntries.begin(), 717 loadingInfo->mContiguousEntries.end(), pred), 718 "The target entry now needs to be a part of the contiguous list of " 719 "entries."); 720 } else { 721 MOZ_ASSERT( 722 mozilla::AnyOf(loadingInfo->mContiguousEntries.begin(), 723 loadingInfo->mContiguousEntries.end(), pred), 724 "The target entry now needs to be a part of the contiguous list of " 725 "entries."); 726 } 727 } 728 729 MOZ_ASSERT(SessionHistoryEntry::GetByLoadId(loadingInfo->mLoadId)->mEntry == 730 entry); 731 732 return loadingInfo; 733 } 734 735 UniquePtr<LoadingSessionHistoryInfo> 736 CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad( 737 LoadingSessionHistoryInfo* aInfo, nsIChannel* aNewChannel) { 738 MOZ_ASSERT(aInfo); 739 MOZ_ASSERT(aNewChannel); 740 741 SessionHistoryInfo newInfo = 742 SessionHistoryInfo(aNewChannel, aInfo->mInfo.LoadType(), 743 aInfo->mInfo.GetPartitionedPrincipalToInherit(), 744 aInfo->mInfo.GetPolicyContainer()); 745 746 for (size_t i = 0; i < mLoadingEntries.Length(); ++i) { 747 if (mLoadingEntries[i].mLoadId == aInfo->mLoadId) { 748 RefPtr<SessionHistoryEntry> loadingEntry = mLoadingEntries[i].mEntry; 749 loadingEntry->SetInfo(&newInfo); 750 751 if (IsTop()) { 752 // Only top level pages care about Get/SetTransient. 753 nsCOMPtr<nsIURI> uri; 754 aNewChannel->GetURI(getter_AddRefs(uri)); 755 if (!nsDocShell::ShouldAddToSessionHistory(uri, aNewChannel)) { 756 loadingEntry->SetTransient(); 757 } 758 } else { 759 loadingEntry->SetIsSubFrame(aInfo->mInfo.IsSubFrame()); 760 } 761 loadingEntry->SetDocshellID(GetHistoryID()); 762 loadingEntry->SetIsDynamicallyAdded(CreatedDynamically()); 763 764 if (aInfo->mTriggeringNavigationType && 765 *aInfo->mTriggeringNavigationType == NavigationType::Replace) { 766 MaybeReuseNavigationKeyFromActiveEntry(loadingEntry); 767 } 768 769 auto result = MakeUnique<LoadingSessionHistoryInfo>(loadingEntry, aInfo); 770 MOZ_LOG_FMT( 771 gNavigationAPILog, LogLevel::Debug, 772 "CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad: " 773 "Recreating the contiguous entries list after redirected navigation " 774 "to {}.", 775 ToMaybeRef(result->mInfo.GetURI()) 776 .map(std::mem_fn(&nsIURI::GetSpecOrDefault)) 777 .valueOr("(null URI)."_ns)); 778 GetContiguousEntriesForLoad(*result, loadingEntry); 779 return result; 780 } 781 } 782 return nullptr; 783 } 784 785 void CanonicalBrowsingContext::GetContiguousEntriesForLoad( 786 LoadingSessionHistoryInfo& aLoadingInfo, 787 const RefPtr<SessionHistoryEntry>& aEntry) { 788 nsCOMPtr<nsIURI> uri = 789 mActiveEntry ? mActiveEntry->GetURIOrInheritedForAboutBlank() : nullptr; 790 nsCOMPtr<nsIURI> targetURI = aEntry->GetURIOrInheritedForAboutBlank(); 791 bool sameOrigin = 792 NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI( 793 targetURI, uri, false, false)); 794 if (aEntry->isInList() || 795 (mActiveEntry && mActiveEntry->isInList() && sameOrigin)) { 796 MOZ_DIAGNOSTIC_ASSERT(aLoadingInfo.mTriggeringNavigationType); 797 NavigationType navigationType = 798 aLoadingInfo.mTriggeringNavigationType.valueOr(NavigationType::Push); 799 nsSHistory::WalkContiguousEntriesInOrder( 800 aEntry->isInList() ? aEntry : mActiveEntry, 801 [activeEntry = mActiveEntry, entries = &aLoadingInfo.mContiguousEntries, 802 navigationType](auto* aEntry) { 803 nsCOMPtr<SessionHistoryEntry> entry = do_QueryObject(aEntry); 804 MOZ_ASSERT(entry); 805 if (navigationType == NavigationType::Replace && 806 entry == activeEntry) { 807 // In the case of a replace navigation, we end up dropping the 808 // active entry and all following entries. 809 return false; 810 } 811 entries->AppendElement(entry->Info()); 812 // In the case of a push navigation, we end up keeping the 813 // current active entry but drop all following entries. 814 return !(navigationType == NavigationType::Push && 815 entry == activeEntry); 816 }); 817 } 818 819 if (!aLoadingInfo.mLoadIsFromSessionHistory || !sameOrigin) { 820 aLoadingInfo.mContiguousEntries.AppendElement(aEntry->Info()); 821 } 822 } 823 824 void CanonicalBrowsingContext::MaybeReuseNavigationKeyFromActiveEntry( 825 SessionHistoryEntry* aEntry) { 826 MOZ_ASSERT(aEntry); 827 828 // https://html.spec.whatwg.org/#finalize-a-cross-document-navigation 829 // 9. If entryToReplace is null, then: ... 830 // Otherwise: ... 831 // 4. If historyEntry's document state's origin is same origin with 832 // entryToReplace's document state's origin, then set 833 // historyEntry's navigation API key to entryToReplace's 834 // navigation API key. 835 if (!mActiveEntry) { 836 return; 837 } 838 839 nsCOMPtr<nsIURI> uri = mActiveEntry->GetURIOrInheritedForAboutBlank(); 840 nsCOMPtr<nsIURI> targetURI = aEntry->GetURIOrInheritedForAboutBlank(); 841 bool sameOrigin = 842 NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI( 843 targetURI, uri, false, false)); 844 if (!sameOrigin) { 845 return; 846 } 847 848 aEntry->SetNavigationKey(mActiveEntry->Info().NavigationKey()); 849 } 850 851 using PrintPromise = CanonicalBrowsingContext::PrintPromise; 852 #ifdef NS_PRINTING 853 // Clients must call StaticCloneForPrintingCreated or 854 // NoStaticCloneForPrintingWillBeCreated before the underlying promise can 855 // resolve. 856 class PrintListenerAdapter final : public nsIWebProgressListener { 857 public: 858 explicit PrintListenerAdapter(PrintPromise::Private* aPromise) 859 : mPromise(aPromise) {} 860 861 NS_DECL_ISUPPORTS 862 863 // NS_DECL_NSIWEBPROGRESSLISTENER 864 NS_IMETHOD OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 865 uint32_t aStateFlags, nsresult aStatus) override { 866 MOZ_ASSERT(NS_IsMainThread()); 867 if (aStateFlags & nsIWebProgressListener::STATE_STOP && 868 aStateFlags & nsIWebProgressListener::STATE_IS_DOCUMENT && mPromise) { 869 mPrintJobFinished = true; 870 if (mHaveSetBrowsingContext) { 871 mPromise->Resolve(mClonedStaticBrowsingContext, __func__); 872 mPromise = nullptr; 873 } 874 } 875 return NS_OK; 876 } 877 NS_IMETHOD OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 878 nsresult aStatus, 879 const char16_t* aMessage) override { 880 if (aStatus != NS_OK && mPromise) { 881 mPromise->Reject(aStatus, __func__); 882 mPromise = nullptr; 883 } 884 return NS_OK; 885 } 886 NS_IMETHOD OnProgressChange(nsIWebProgress* aWebProgress, 887 nsIRequest* aRequest, int32_t aCurSelfProgress, 888 int32_t aMaxSelfProgress, 889 int32_t aCurTotalProgress, 890 int32_t aMaxTotalProgress) override { 891 return NS_OK; 892 } 893 NS_IMETHOD OnLocationChange(nsIWebProgress* aWebProgress, 894 nsIRequest* aRequest, nsIURI* aLocation, 895 uint32_t aFlags) override { 896 return NS_OK; 897 } 898 NS_IMETHOD OnSecurityChange(nsIWebProgress* aWebProgress, 899 nsIRequest* aRequest, uint32_t aState) override { 900 return NS_OK; 901 } 902 NS_IMETHOD OnContentBlockingEvent(nsIWebProgress* aWebProgress, 903 nsIRequest* aRequest, 904 uint32_t aEvent) override { 905 return NS_OK; 906 } 907 908 void StaticCloneForPrintingCreated( 909 MaybeDiscardedBrowsingContext&& aClonedStaticBrowsingContext) { 910 MOZ_ASSERT(NS_IsMainThread()); 911 mClonedStaticBrowsingContext = std::move(aClonedStaticBrowsingContext); 912 mHaveSetBrowsingContext = true; 913 if (mPrintJobFinished && mPromise) { 914 mPromise->Resolve(mClonedStaticBrowsingContext, __func__); 915 mPromise = nullptr; 916 } 917 } 918 919 void NoStaticCloneForPrintingWillBeCreated() { 920 StaticCloneForPrintingCreated(nullptr); 921 } 922 923 private: 924 ~PrintListenerAdapter() = default; 925 926 RefPtr<PrintPromise::Private> mPromise; 927 MaybeDiscardedBrowsingContext mClonedStaticBrowsingContext = nullptr; 928 bool mHaveSetBrowsingContext = false; 929 bool mPrintJobFinished = false; 930 }; 931 932 NS_IMPL_ISUPPORTS(PrintListenerAdapter, nsIWebProgressListener) 933 #endif 934 935 already_AddRefed<Promise> CanonicalBrowsingContext::PrintJS( 936 nsIPrintSettings* aPrintSettings, ErrorResult& aRv) { 937 RefPtr<Promise> promise = Promise::Create(GetIncumbentGlobal(), aRv); 938 if (NS_WARN_IF(aRv.Failed())) { 939 return promise.forget(); 940 } 941 942 Print(aPrintSettings) 943 ->Then( 944 GetCurrentSerialEventTarget(), __func__, 945 [promise](MaybeDiscardedBrowsingContext) { 946 promise->MaybeResolveWithUndefined(); 947 }, 948 [promise](nsresult aResult) { promise->MaybeReject(aResult); }); 949 return promise.forget(); 950 } 951 952 RefPtr<PrintPromise> CanonicalBrowsingContext::Print( 953 nsIPrintSettings* aPrintSettings) { 954 #ifndef NS_PRINTING 955 return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); 956 #else 957 // Content analysis is not supported on non-Windows platforms. 958 # if defined(XP_WIN) 959 bool needContentAnalysis = false; 960 nsCOMPtr<nsIContentAnalysis> contentAnalysis = 961 mozilla::components::nsIContentAnalysis::Service(); 962 (void)NS_WARN_IF(!contentAnalysis); 963 if (contentAnalysis) { 964 nsresult rv = contentAnalysis->GetIsActive(&needContentAnalysis); 965 (void)NS_WARN_IF(NS_FAILED(rv)); 966 } 967 if (needContentAnalysis) { 968 auto done = MakeRefPtr<PrintPromise::Private>(__func__); 969 contentanalysis::ContentAnalysis::PrintToPDFToDetermineIfPrintAllowed( 970 this, aPrintSettings) 971 ->Then( 972 GetCurrentSerialEventTarget(), __func__, 973 [done, aPrintSettings = RefPtr{aPrintSettings}, 974 self = RefPtr{this}]( 975 contentanalysis::ContentAnalysis::PrintAllowedResult aResponse) 976 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA mutable { 977 if (aResponse.mAllowed) { 978 self->PrintWithNoContentAnalysis( 979 aPrintSettings, false, 980 aResponse.mCachedStaticDocumentBrowsingContext) 981 ->ChainTo(done.forget(), __func__); 982 } else { 983 // Since we are not doing the second print in this case, 984 // release the clone that is no longer needed. 985 self->ReleaseClonedPrint( 986 aResponse.mCachedStaticDocumentBrowsingContext); 987 done->Reject(NS_ERROR_CONTENT_BLOCKED, __func__); 988 } 989 }, 990 [done, self = RefPtr{this}]( 991 contentanalysis::ContentAnalysis::PrintAllowedError 992 aErrorResponse) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { 993 // Since we are not doing the second print in this case, release 994 // the clone that is no longer needed. 995 self->ReleaseClonedPrint( 996 aErrorResponse.mCachedStaticDocumentBrowsingContext); 997 done->Reject(aErrorResponse.mError, __func__); 998 }); 999 return done; 1000 } 1001 # endif 1002 return PrintWithNoContentAnalysis(aPrintSettings, false, nullptr); 1003 #endif 1004 } 1005 1006 void CanonicalBrowsingContext::ReleaseClonedPrint( 1007 const MaybeDiscardedBrowsingContext& aClonedStaticBrowsingContext) { 1008 #ifdef NS_PRINTING 1009 auto* browserParent = GetBrowserParent(); 1010 if (NS_WARN_IF(!browserParent)) { 1011 return; 1012 } 1013 (void)browserParent->SendDestroyPrintClone(aClonedStaticBrowsingContext); 1014 #endif 1015 } 1016 1017 RefPtr<PrintPromise> CanonicalBrowsingContext::PrintWithNoContentAnalysis( 1018 nsIPrintSettings* aPrintSettings, bool aForceStaticDocument, 1019 const MaybeDiscardedBrowsingContext& aCachedStaticDocument) { 1020 #ifndef NS_PRINTING 1021 return PrintPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); 1022 #else 1023 auto promise = MakeRefPtr<PrintPromise::Private>(__func__); 1024 auto listener = MakeRefPtr<PrintListenerAdapter>(promise); 1025 if (IsInProcess()) { 1026 RefPtr<nsGlobalWindowOuter> outerWindow = 1027 nsGlobalWindowOuter::Cast(GetDOMWindow()); 1028 if (NS_WARN_IF(!outerWindow)) { 1029 promise->Reject(NS_ERROR_FAILURE, __func__); 1030 return promise; 1031 } 1032 1033 ErrorResult rv; 1034 listener->NoStaticCloneForPrintingWillBeCreated(); 1035 outerWindow->Print(aPrintSettings, 1036 /* aRemotePrintJob = */ nullptr, listener, 1037 /* aDocShellToCloneInto = */ nullptr, 1038 nsGlobalWindowOuter::IsPreview::No, 1039 nsGlobalWindowOuter::IsForWindowDotPrint::No, 1040 /* aPrintPreviewCallback = */ nullptr, 1041 /* aCachedBrowsingContext = */ nullptr, rv); 1042 if (rv.Failed()) { 1043 promise->Reject(rv.StealNSResult(), __func__); 1044 } 1045 return promise; 1046 } 1047 1048 auto* browserParent = GetBrowserParent(); 1049 if (NS_WARN_IF(!browserParent)) { 1050 promise->Reject(NS_ERROR_FAILURE, __func__); 1051 return promise; 1052 } 1053 1054 nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = 1055 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 1056 if (NS_WARN_IF(!printSettingsSvc)) { 1057 promise->Reject(NS_ERROR_FAILURE, __func__); 1058 return promise; 1059 } 1060 1061 nsresult rv; 1062 nsCOMPtr<nsIPrintSettings> printSettings = aPrintSettings; 1063 if (!printSettings) { 1064 rv = 1065 printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings)); 1066 if (NS_WARN_IF(NS_FAILED(rv))) { 1067 promise->Reject(rv, __func__); 1068 return promise; 1069 } 1070 } 1071 1072 embedding::PrintData printData; 1073 rv = printSettingsSvc->SerializeToPrintData(printSettings, &printData); 1074 if (NS_WARN_IF(NS_FAILED(rv))) { 1075 promise->Reject(rv, __func__); 1076 return promise; 1077 } 1078 1079 layout::RemotePrintJobParent* remotePrintJob = 1080 new layout::RemotePrintJobParent(printSettings); 1081 printData.remotePrintJob() = 1082 browserParent->Manager()->SendPRemotePrintJobConstructor(remotePrintJob); 1083 1084 remotePrintJob->RegisterListener(listener); 1085 1086 if (!aCachedStaticDocument.IsNullOrDiscarded()) { 1087 // There is no cloned static browsing context that 1088 // SendPrintClonedPage() will return, so indicate this 1089 // so listener can resolve its promise. 1090 listener->NoStaticCloneForPrintingWillBeCreated(); 1091 if (NS_WARN_IF(!browserParent->SendPrintClonedPage( 1092 this, printData, aCachedStaticDocument))) { 1093 promise->Reject(NS_ERROR_FAILURE, __func__); 1094 } 1095 } else { 1096 RefPtr<PBrowserParent::PrintPromise> printPromise = 1097 browserParent->SendPrint(this, printData, aForceStaticDocument); 1098 printPromise->Then( 1099 GetMainThreadSerialEventTarget(), __func__, 1100 [listener](MaybeDiscardedBrowsingContext cachedStaticDocument) { 1101 // promise will get resolved by the listener 1102 listener->StaticCloneForPrintingCreated( 1103 std::move(cachedStaticDocument)); 1104 }, 1105 [promise](ResponseRejectReason reason) { 1106 NS_WARNING("SendPrint() failed"); 1107 promise->Reject(NS_ERROR_FAILURE, __func__); 1108 }); 1109 } 1110 return promise.forget(); 1111 #endif 1112 } 1113 1114 void CanonicalBrowsingContext::CallOnTopDescendants( 1115 const FunctionRef<CallState(CanonicalBrowsingContext*)>& aCallback, 1116 TopDescendantKind aKind) { 1117 // Calling with All on something other than a chrome root is unlikely to be 1118 // what you want, so lacking a use-case for it, we assert against it for now. 1119 MOZ_ASSERT_IF(aKind == TopDescendantKind::All, 1120 IsChrome() && !GetParentCrossChromeBoundary()); 1121 // Similarly, calling with {NonNested,All} on a non-top bc is unlikely to be 1122 // what you want. 1123 MOZ_ASSERT_IF(aKind != TopDescendantKind::ChildrenOnly, IsTop()); 1124 1125 if (!IsInProcess()) { 1126 // We rely on top levels having to be embedded in the parent process, so 1127 // we can only have top level descendants if embedded here... 1128 return; 1129 } 1130 1131 const auto* ourTop = Top(); 1132 1133 AutoTArray<RefPtr<BrowsingContextGroup>, 32> groups; 1134 BrowsingContextGroup::GetAllGroups(groups); 1135 for (auto& browsingContextGroup : groups) { 1136 for (auto& topLevel : browsingContextGroup->Toplevels()) { 1137 if (topLevel == ourTop) { 1138 // A nested toplevel can't be a descendant of our same toplevel. 1139 continue; 1140 } 1141 1142 // Walk up the CanonicalBrowsingContext tree, looking for a match. 1143 const bool topLevelIsRelevant = [&] { 1144 auto* current = topLevel->Canonical(); 1145 while (auto* parent = current->GetParentCrossChromeBoundary()) { 1146 if (parent == this) { 1147 return true; 1148 } 1149 // If we've reached aKind's stop condition, break out early. 1150 if (aKind == TopDescendantKind::ChildrenOnly || 1151 (aKind == TopDescendantKind::NonNested && parent->IsTop())) { 1152 return false; 1153 } 1154 current = parent; 1155 } 1156 return false; 1157 }(); 1158 1159 if (!topLevelIsRelevant) { 1160 continue; 1161 } 1162 1163 if (aCallback(topLevel->Canonical()) == CallState::Stop) { 1164 return; 1165 } 1166 } 1167 } 1168 } 1169 1170 void CanonicalBrowsingContext::SessionHistoryCommit( 1171 uint64_t aLoadId, const nsID& aChangeID, uint32_t aLoadType, 1172 bool aCloneEntryChildren, bool aChannelExpired, uint32_t aCacheKey) { 1173 MOZ_LOG(gSHLog, LogLevel::Verbose, 1174 ("CanonicalBrowsingContext::SessionHistoryCommit %p %" PRIu64, this, 1175 aLoadId)); 1176 MOZ_ASSERT(aLoadId != UINT64_MAX, 1177 "Must not send special about:blank loadinfo to parent."); 1178 for (size_t i = 0; i < mLoadingEntries.Length(); ++i) { 1179 if (mLoadingEntries[i].mLoadId == aLoadId) { 1180 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 1181 if (!shistory) { 1182 SessionHistoryEntry::RemoveLoadId(aLoadId); 1183 mLoadingEntries.RemoveElementAt(i); 1184 return; 1185 } 1186 1187 RefPtr<SessionHistoryEntry> newActiveEntry = mLoadingEntries[i].mEntry; 1188 if (aCacheKey != 0) { 1189 newActiveEntry->SetCacheKey(aCacheKey); 1190 } 1191 1192 if (aChannelExpired) { 1193 newActiveEntry->SharedInfo()->mExpired = true; 1194 } 1195 1196 bool loadFromSessionHistory = !newActiveEntry->ForInitialLoad(); 1197 newActiveEntry->SetForInitialLoad(false); 1198 SessionHistoryEntry::RemoveLoadId(aLoadId); 1199 mLoadingEntries.RemoveElementAt(i); 1200 1201 int32_t indexOfHistoryLoad = -1; 1202 if (loadFromSessionHistory) { 1203 nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(newActiveEntry); 1204 indexOfHistoryLoad = shistory->GetIndexOfEntry(root); 1205 if (indexOfHistoryLoad < 0) { 1206 // Entry has been removed from the session history. 1207 return; 1208 } 1209 } 1210 1211 CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory); 1212 1213 // If there is a name in the new entry, clear the name of all contiguous 1214 // entries. This is for https://html.spec.whatwg.org/#history-traversal 1215 // Step 4.4.2. 1216 nsAutoString nameOfNewEntry; 1217 newActiveEntry->GetName(nameOfNewEntry); 1218 if (!nameOfNewEntry.IsEmpty()) { 1219 nsSHistory::WalkContiguousEntries( 1220 newActiveEntry, 1221 [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); }); 1222 } 1223 1224 auto* activeEntries = GetActiveEntries(); 1225 MOZ_LOG(gSHLog, LogLevel::Verbose, 1226 ("SessionHistoryCommit called with mActiveEntry=%p, " 1227 "newActiveEntry=%p, " 1228 "active entry list does%s contain the active entry.", 1229 mActiveEntry.get(), newActiveEntry.get(), 1230 activeEntries->contains(mActiveEntry) ? "" : "n't")); 1231 1232 bool addEntry = ShouldUpdateSessionHistory(aLoadType); 1233 if (IsTop()) { 1234 if (mActiveEntry && !mActiveEntry->GetFrameLoader()) { 1235 bool sharesDocument = true; 1236 mActiveEntry->SharesDocumentWith(newActiveEntry, &sharesDocument); 1237 if (!sharesDocument) { 1238 // If the old page won't be in the bfcache, 1239 // clear the dynamic entries. 1240 RemoveDynEntriesFromActiveSessionHistoryEntry(); 1241 } 1242 } 1243 1244 if (LOAD_TYPE_HAS_FLAGS(aLoadType, 1245 nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY)) { 1246 // Replace the current entry with the new entry. 1247 int32_t index = shistory->GetTargetIndexForHistoryOperation(); 1248 1249 // If we're trying to replace an inexistant shistory entry then we 1250 // should append instead. 1251 addEntry = index < 0; 1252 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1253 "IsTop: Replacing history with addEntry={}", addEntry); 1254 1255 if (!addEntry) { 1256 shistory->ReplaceEntry(index, newActiveEntry); 1257 if (Navigation::IsAPIEnabled() && mActiveEntry && 1258 mActiveEntry->isInList() && !newActiveEntry->isInList()) { 1259 mActiveEntry->setNext(newActiveEntry); 1260 mActiveEntry->remove(); 1261 } 1262 } 1263 if (Navigation::IsAPIEnabled() && !newActiveEntry->isInList()) { 1264 activeEntries->insertBack(newActiveEntry); 1265 } 1266 mActiveEntry = newActiveEntry; 1267 } else if (LOAD_TYPE_HAS_FLAGS( 1268 aLoadType, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) && 1269 !ShouldAddEntryForRefresh(newActiveEntry) && mActiveEntry) { 1270 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1271 "IsTop: Refresh without adding entry"); 1272 addEntry = false; 1273 mActiveEntry->ReplaceWith(*newActiveEntry); 1274 } else if (!loadFromSessionHistory && mActiveEntry) { 1275 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, "IsTop: Adding new entry"); 1276 1277 if (Navigation::IsAPIEnabled() && mActiveEntry->isInList()) { 1278 RefPtr entry = mActiveEntry->getNext(); 1279 while (entry) { 1280 entry = entry->removeAndGetNext(); 1281 } 1282 // TODO(avandolder): Can this check ever actually be false? 1283 if (!newActiveEntry->isInList()) { 1284 activeEntries->insertBack(newActiveEntry); 1285 } 1286 } 1287 mActiveEntry = newActiveEntry; 1288 } else if (!mActiveEntry) { 1289 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1290 "IsTop: No active entry, adding new entry"); 1291 if (Navigation::IsAPIEnabled() && !newActiveEntry->isInList()) { 1292 activeEntries->insertBack(newActiveEntry); 1293 } 1294 mActiveEntry = newActiveEntry; 1295 } else { 1296 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1297 "IsTop: Loading from session history"); 1298 mActiveEntry = newActiveEntry; 1299 if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) { 1300 activeEntries->insertBack(mActiveEntry); 1301 } 1302 } 1303 1304 if (loadFromSessionHistory) { 1305 // XXX Synchronize browsing context tree and session history tree? 1306 shistory->InternalSetRequestedIndex(indexOfHistoryLoad); 1307 shistory->UpdateIndex(); 1308 1309 if (IsTop()) { 1310 mActiveEntry->SetWireframe(Nothing()); 1311 } 1312 } else if (addEntry) { 1313 shistory->AddEntry(mActiveEntry); 1314 shistory->InternalSetRequestedIndex(-1); 1315 } 1316 } else { 1317 // FIXME The old implementations adds it to the parent's mLSHE if there 1318 // is one, need to figure out if that makes sense here (peterv 1319 // doesn't think it would). 1320 if (loadFromSessionHistory) { 1321 if (mActiveEntry) { 1322 // mActiveEntry is null if we're loading iframes from session 1323 // history while also parent page is loading from session history. 1324 // In that case there isn't anything to sync. 1325 mActiveEntry->SyncTreesForSubframeNavigation(newActiveEntry, Top(), 1326 this); 1327 } 1328 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1329 "NotTop: Loading from session history"); 1330 mActiveEntry = newActiveEntry; 1331 if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) { 1332 shistory->ReconstructContiguousEntryListFrom(mActiveEntry); 1333 } 1334 shistory->InternalSetRequestedIndex(indexOfHistoryLoad); 1335 // FIXME UpdateIndex() here may update index too early (but even the 1336 // old implementation seems to have similar issues). 1337 shistory->UpdateIndex(); 1338 } else if (addEntry) { 1339 if (mActiveEntry) { 1340 if (LOAD_TYPE_HAS_FLAGS( 1341 aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY) || 1342 (LOAD_TYPE_HAS_FLAGS(aLoadType, 1343 nsIWebNavigation::LOAD_FLAGS_IS_REFRESH) && 1344 !ShouldAddEntryForRefresh(newActiveEntry))) { 1345 // FIXME We need to make sure that when we create the info we 1346 // make a copy of the shared state. 1347 mActiveEntry->ReplaceWith(*newActiveEntry); 1348 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1349 "NotTop: replace current active entry"); 1350 } else { 1351 // AddNestedSHEntry does update the index of the session 1352 // history! 1353 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1354 "NotTop: Adding entry with an active entry"); 1355 shistory->AddNestedSHEntry(mActiveEntry, newActiveEntry, Top(), 1356 aCloneEntryChildren); 1357 if (Navigation::IsAPIEnabled()) { 1358 if (!mActiveEntry->isInList()) { 1359 activeEntries->insertBack(mActiveEntry); 1360 } 1361 mActiveEntry->setNext(newActiveEntry); 1362 } 1363 mActiveEntry = newActiveEntry; 1364 } 1365 } else { 1366 SessionHistoryEntry* parentEntry = GetParent()->mActiveEntry; 1367 // XXX What should happen if parent doesn't have mActiveEntry? 1368 // Or can that even happen ever? 1369 if (parentEntry) { 1370 MOZ_LOG_FMT(gSHLog, LogLevel::Verbose, 1371 "NotTop: Adding entry without an active entry"); 1372 mActiveEntry = newActiveEntry; 1373 if (Navigation::IsAPIEnabled() && !mActiveEntry->isInList()) { 1374 activeEntries->insertBack(mActiveEntry); 1375 } 1376 // FIXME Using IsInProcess for aUseRemoteSubframes isn't quite 1377 // right, but aUseRemoteSubframes should be going away. 1378 parentEntry->AddChild( 1379 mActiveEntry, 1380 CreatedDynamically() ? -1 : GetParent()->IndexOf(this), 1381 IsInProcess()); 1382 } 1383 } 1384 shistory->InternalSetRequestedIndex(-1); 1385 } 1386 } 1387 1388 ResetSHEntryHasUserInteractionCache(); 1389 1390 HistoryCommitIndexAndLength(aChangeID, caller); 1391 1392 shistory->LogHistory(); 1393 1394 return; 1395 } 1396 // XXX Should the loading entries before [i] be removed? 1397 } 1398 // FIXME Should we throw an error if we don't find an entry for 1399 // aSessionHistoryEntryId? 1400 } 1401 1402 already_AddRefed<nsDocShellLoadState> CanonicalBrowsingContext::CreateLoadInfo( 1403 SessionHistoryEntry* aEntry, NavigationType aNavigationType) { 1404 const SessionHistoryInfo& info = aEntry->Info(); 1405 RefPtr<nsDocShellLoadState> loadState(new nsDocShellLoadState(info.GetURI())); 1406 info.FillLoadInfo(*loadState); 1407 UniquePtr<LoadingSessionHistoryInfo> loadingInfo; 1408 loadingInfo = MakeUnique<LoadingSessionHistoryInfo>(aEntry); 1409 loadingInfo->mTriggeringNavigationType = Some(aNavigationType); 1410 mLoadingEntries.AppendElement( 1411 LoadingSessionHistoryEntry{loadingInfo->mLoadId, aEntry}); 1412 loadState->SetLoadingSessionHistoryInfo(std::move(loadingInfo)); 1413 1414 return loadState.forget(); 1415 } 1416 1417 void CanonicalBrowsingContext::NotifyOnHistoryReload( 1418 bool aForceReload, bool& aCanReload, 1419 Maybe<NotNull<RefPtr<nsDocShellLoadState>>>& aLoadState, 1420 Maybe<bool>& aReloadActiveEntry) { 1421 MOZ_DIAGNOSTIC_ASSERT(!aLoadState); 1422 1423 aCanReload = true; 1424 nsISHistory* shistory = GetSessionHistory(); 1425 NS_ENSURE_TRUE_VOID(shistory); 1426 1427 shistory->NotifyOnHistoryReload(&aCanReload); 1428 if (!aCanReload) { 1429 return; 1430 } 1431 1432 if (mActiveEntry) { 1433 aLoadState.emplace(WrapMovingNotNull( 1434 RefPtr{CreateLoadInfo(mActiveEntry, NavigationType::Reload)})); 1435 aReloadActiveEntry.emplace(true); 1436 if (aForceReload) { 1437 shistory->RemoveFrameEntries(mActiveEntry); 1438 } 1439 } else if (!mLoadingEntries.IsEmpty()) { 1440 const LoadingSessionHistoryEntry& loadingEntry = 1441 mLoadingEntries.LastElement(); 1442 uint64_t loadId = loadingEntry.mLoadId; 1443 aLoadState.emplace(WrapMovingNotNull( 1444 RefPtr{CreateLoadInfo(loadingEntry.mEntry, NavigationType::Reload)})); 1445 aReloadActiveEntry.emplace(false); 1446 if (aForceReload) { 1447 SessionHistoryEntry::LoadingEntry* entry = 1448 SessionHistoryEntry::GetByLoadId(loadId); 1449 if (entry) { 1450 shistory->RemoveFrameEntries(entry->mEntry); 1451 } 1452 } 1453 } 1454 1455 if (aLoadState) { 1456 // Use 0 as the offset, since aLoadState will be be used for reload. 1457 aLoadState.ref()->SetLoadIsFromSessionHistory(0, 1458 aReloadActiveEntry.value()); 1459 } 1460 // If we don't have an active entry and we don't have a loading entry then 1461 // the nsDocShell will create a load state based on its document. 1462 } 1463 1464 void CanonicalBrowsingContext::SetActiveSessionHistoryEntry( 1465 const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo, 1466 uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) { 1467 nsISHistory* shistory = GetSessionHistory(); 1468 if (!shistory) { 1469 return; 1470 } 1471 CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory); 1472 1473 RefPtr<SessionHistoryEntry> oldActiveEntry = mActiveEntry; 1474 if (aPreviousScrollPos.isSome() && oldActiveEntry) { 1475 oldActiveEntry->SetScrollPosition(aPreviousScrollPos.ref().x, 1476 aPreviousScrollPos.ref().y); 1477 } 1478 mActiveEntry = new SessionHistoryEntry(aInfo); 1479 mActiveEntry->SetDocshellID(GetHistoryID()); 1480 mActiveEntry->AdoptBFCacheEntry(oldActiveEntry); 1481 if (aUpdatedCacheKey != 0) { 1482 mActiveEntry->SharedInfo()->mCacheKey = aUpdatedCacheKey; 1483 } 1484 1485 if (IsTop()) { 1486 Maybe<int32_t> previousEntryIndex, loadedEntryIndex; 1487 shistory->AddToRootSessionHistory(true, oldActiveEntry, this, mActiveEntry, 1488 aLoadType, &previousEntryIndex, 1489 &loadedEntryIndex); 1490 } else { 1491 if (oldActiveEntry) { 1492 shistory->AddNestedSHEntry(oldActiveEntry, mActiveEntry, Top(), true); 1493 } else if (GetParent() && GetParent()->mActiveEntry) { 1494 GetParent()->mActiveEntry->AddChild( 1495 mActiveEntry, CreatedDynamically() ? -1 : GetParent()->IndexOf(this), 1496 UseRemoteSubframes()); 1497 } 1498 } 1499 1500 auto* activeEntries = GetActiveEntries(); 1501 MOZ_LOG( 1502 gSHLog, LogLevel::Verbose, 1503 ("SetActiveSessionHistoryEntry called with oldActiveEntry=%p, " 1504 "mActiveEntry=%p, active entry list does%s contain the active entry. ", 1505 oldActiveEntry.get(), mActiveEntry.get(), 1506 activeEntries->contains(mActiveEntry) ? "" : "n't")); 1507 1508 if (Navigation::IsAPIEnabled() && 1509 (!oldActiveEntry || oldActiveEntry->isInList())) { 1510 RefPtr toRemove = 1511 oldActiveEntry ? oldActiveEntry->getNext() : activeEntries->getFirst(); 1512 while (toRemove) { 1513 toRemove = toRemove->removeAndGetNext(); 1514 } 1515 activeEntries->insertBack(mActiveEntry); 1516 } 1517 1518 ResetSHEntryHasUserInteractionCache(); 1519 1520 shistory->InternalSetRequestedIndex(-1); 1521 1522 // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry. 1523 HistoryCommitIndexAndLength(aChangeID, caller); 1524 1525 static_cast<nsSHistory*>(shistory)->LogHistory(); 1526 } 1527 1528 void CanonicalBrowsingContext::ReplaceActiveSessionHistoryEntry( 1529 SessionHistoryInfo* aInfo) { 1530 if (!mActiveEntry) { 1531 return; 1532 } 1533 1534 // aInfo comes from the entry stored in the current document's docshell, whose 1535 // interaction state does not get updated. So we instead propagate state from 1536 // the previous canonical entry. See bug 1917369. 1537 const bool hasUserInteraction = mActiveEntry->GetHasUserInteraction(); 1538 mActiveEntry->SetInfo(aInfo); 1539 mActiveEntry->SetHasUserInteraction(hasUserInteraction); 1540 // Notify children of the update 1541 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 1542 if (shistory) { 1543 shistory->NotifyOnHistoryReplaceEntry(); 1544 } 1545 1546 ResetSHEntryHasUserInteractionCache(); 1547 1548 if (IsTop()) { 1549 mActiveEntry->SetWireframe(Nothing()); 1550 } 1551 1552 MOZ_LOG(gSHLog, LogLevel::Verbose, 1553 ("Replacing active session history entry")); 1554 if (Navigation::IsAPIEnabled() && mActiveEntry->isInList()) { 1555 RefPtr toRemove = mActiveEntry->getNext(); 1556 while (toRemove) { 1557 toRemove = toRemove->removeAndGetNext(); 1558 } 1559 } 1560 1561 // FIXME Need to do the equivalent of EvictDocumentViewersOrReplaceEntry. 1562 } 1563 1564 void CanonicalBrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() { 1565 nsISHistory* shistory = GetSessionHistory(); 1566 // In theory shistory can be null here if the method is called right after 1567 // CanonicalBrowsingContext::ReplacedBy call. 1568 NS_ENSURE_TRUE_VOID(shistory); 1569 nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry); 1570 shistory->RemoveDynEntries(shistory->GetIndexOfEntry(root), mActiveEntry); 1571 } 1572 1573 void CanonicalBrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) { 1574 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 1575 if (shistory) { 1576 CallerWillNotifyHistoryIndexAndLengthChanges caller(shistory); 1577 nsCOMPtr<nsISHEntry> root = nsSHistory::GetRootSHEntry(mActiveEntry); 1578 bool didRemove; 1579 AutoTArray<nsID, 16> ids({GetHistoryID()}); 1580 shistory->RemoveEntries(ids, shistory->GetIndexOfEntry(root), &didRemove); 1581 if (didRemove) { 1582 RefPtr<BrowsingContext> rootBC = shistory->GetBrowsingContext(); 1583 if (rootBC) { 1584 if (!rootBC->IsInProcess()) { 1585 if (ContentParent* cp = rootBC->Canonical()->GetContentParent()) { 1586 (void)cp->SendDispatchLocationChangeEvent(rootBC); 1587 } 1588 } else if (rootBC->GetDocShell()) { 1589 rootBC->GetDocShell()->DispatchLocationChangeEvent(); 1590 } 1591 } 1592 } 1593 HistoryCommitIndexAndLength(aChangeID, caller); 1594 } 1595 } 1596 1597 // https://html.spec.whatwg.org/#apply-the-history-step 1598 // This might not seem to be #apply-the-history-step, but it is in fact exactly 1599 // what it is. 1600 Maybe<int32_t> CanonicalBrowsingContext::HistoryGo( 1601 int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction, 1602 bool aUserActivation, bool aCheckForCancelation, 1603 Maybe<ContentParentId> aContentId, 1604 std::function<void(nsresult)>&& aResolver) { 1605 if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) { 1606 NS_ERROR( 1607 "aRequireUserInteraction may only be used with an offset of -1 or 1"); 1608 return Nothing(); 1609 } 1610 1611 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 1612 if (!shistory) { 1613 return Nothing(); 1614 } 1615 1616 CheckedInt<int32_t> index = shistory->GetTargetIndexForHistoryOperation(); 1617 MOZ_LOG(gSHLog, LogLevel::Debug, 1618 ("HistoryGo(%d->%d) epoch %" PRIu64 "/id %" PRIu64, aOffset, 1619 (index + aOffset).value(), aHistoryEpoch, 1620 (uint64_t)(aContentId.isSome() ? aContentId.value() : 0))); 1621 1622 while (true) { 1623 index += aOffset; 1624 if (!index.isValid()) { 1625 MOZ_LOG(gSHLog, LogLevel::Debug, ("Invalid index")); 1626 return Nothing(); 1627 } 1628 1629 // Check for user interaction if desired, except for the first and last 1630 // history entries. We compare with >= to account for the case where 1631 // aOffset >= length. 1632 if (!StaticPrefs::browser_navigation_requireUserInteraction() || 1633 !aRequireUserInteraction || index.value() >= shistory->Length() - 1 || 1634 index.value() <= 0) { 1635 break; 1636 } 1637 if (shistory->HasUserInteractionAtIndex(index.value())) { 1638 break; 1639 } 1640 } 1641 1642 // Implement aborting additional history navigations from within the same 1643 // event spin of the content process. 1644 1645 uint64_t epoch; 1646 bool sameEpoch = false; 1647 Maybe<ContentParentId> id; 1648 shistory->GetEpoch(epoch, id); 1649 1650 if (aContentId == id && epoch >= aHistoryEpoch) { 1651 sameEpoch = true; 1652 MOZ_LOG(gSHLog, LogLevel::Debug, ("Same epoch/id")); 1653 } 1654 // Don't update the epoch until we know if the target index is valid 1655 1656 // GoToIndex checks that index is >= 0 and < length. 1657 nsTArray<nsSHistory::LoadEntryResult> loadResults; 1658 const int32_t oldRequestedIndex = shistory->GetRequestedIndex(); 1659 1660 nsresult rv = shistory->GotoIndex(this, index.value(), loadResults, sameEpoch, 1661 aOffset == 0, aUserActivation); 1662 if (NS_FAILED(rv)) { 1663 MOZ_LOG(gSHLog, LogLevel::Debug, 1664 ("Dropping HistoryGo - bad index or same epoch (not in same doc)")); 1665 return Nothing(); 1666 } 1667 1668 for (auto& loadResult : loadResults) { 1669 if (nsresult result = loadResult.mBrowsingContext->CheckSandboxFlags( 1670 loadResult.mLoadState); 1671 NS_FAILED(result)) { 1672 aResolver(result); 1673 MOZ_LOG(gSHLog, LogLevel::Debug, 1674 ("Dropping HistoryGo - sandbox check failed")); 1675 shistory->InternalSetRequestedIndex(oldRequestedIndex); 1676 return Nothing(); 1677 } 1678 } 1679 1680 if (epoch < aHistoryEpoch || aContentId != id) { 1681 MOZ_LOG(gSHLog, LogLevel::Debug, ("Set epoch")); 1682 shistory->SetEpoch(aHistoryEpoch, aContentId); 1683 } 1684 int32_t requestedIndex = shistory->GetRequestedIndex(); 1685 RefPtr traversable = Top(); 1686 nsSHistory::LoadURIs(loadResults, aCheckForCancelation, aResolver, 1687 traversable); 1688 return Some(requestedIndex); 1689 } 1690 1691 // https://html.spec.whatwg.org/#performing-a-navigation-api-traversal 1692 // Sub-steps for step 12 1693 void CanonicalBrowsingContext::NavigationTraverse( 1694 const nsID& aKey, uint64_t aHistoryEpoch, bool aUserActivation, 1695 bool aCheckForCancelation, Maybe<ContentParentId> aContentId, 1696 std::function<void(nsresult)>&& aResolver) { 1697 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "Traverse navigation to {}", 1698 aKey.ToString().get()); 1699 nsSHistory* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 1700 if (!shistory) { 1701 return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR); 1702 } 1703 1704 RefPtr<SessionHistoryEntry> targetEntry; 1705 // 12.1 Let navigableSHEs be the result of getting session history entries 1706 // given navigable. 1707 nsSHistory::WalkContiguousEntriesInOrder( 1708 mActiveEntry, [&targetEntry, aKey](auto* aEntry) { 1709 auto* entry = static_cast<SessionHistoryEntry*>(aEntry); 1710 if (entry->Info().NavigationKey() == aKey) { 1711 targetEntry = entry; 1712 return false; 1713 } 1714 return true; 1715 }); 1716 1717 // Step 12.2 1718 if (!targetEntry) { 1719 return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR); 1720 } 1721 1722 // Step 12.3 1723 if (targetEntry == mActiveEntry) { 1724 return aResolver(NS_OK); 1725 } 1726 1727 nsCOMPtr targetRoot = nsSHistory::GetRootSHEntry(targetEntry); 1728 nsCOMPtr activeRoot = nsSHistory::GetRootSHEntry(mActiveEntry); 1729 if (!targetRoot || !activeRoot) { 1730 return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR); 1731 } 1732 int32_t targetIndex = shistory->GetIndexOfEntry(targetRoot); 1733 int32_t activeIndex = shistory->GetIndexOfEntry(activeRoot); 1734 if (targetIndex == -1 || activeIndex == -1) { 1735 return aResolver(NS_ERROR_DOM_INVALID_STATE_ERR); 1736 } 1737 1738 int32_t offset = targetIndex - activeIndex; 1739 1740 int32_t requestedIndex = shistory->GetTargetIndexForHistoryOperation(); 1741 // Step 12.3 1742 if (requestedIndex == targetIndex) { 1743 return aResolver(NS_OK); 1744 } 1745 1746 // Reset the requested index since this is not a relative traversal, and the 1747 // offset is overriding any currently ongoing history traversals. 1748 shistory->InternalSetRequestedIndex(-1); 1749 1750 HistoryGo(offset, aHistoryEpoch, false, aUserActivation, aCheckForCancelation, 1751 aContentId, std::move(aResolver)); 1752 } 1753 1754 JSObject* CanonicalBrowsingContext::WrapObject( 1755 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 1756 return CanonicalBrowsingContext_Binding::Wrap(aCx, this, aGivenProto); 1757 } 1758 1759 void CanonicalBrowsingContext::DispatchWheelZoomChange(bool aIncrease) { 1760 Element* element = Top()->GetEmbedderElement(); 1761 if (!element) { 1762 return; 1763 } 1764 1765 auto event = aIncrease ? u"DoZoomEnlargeBy10"_ns : u"DoZoomReduceBy10"_ns; 1766 auto dispatcher = MakeRefPtr<AsyncEventDispatcher>( 1767 element, event, CanBubble::eYes, ChromeOnlyDispatch::eYes); 1768 dispatcher->PostDOMEvent(); 1769 } 1770 1771 void CanonicalBrowsingContext::CanonicalDiscard() { 1772 if (mTabMediaController) { 1773 mTabMediaController->Shutdown(); 1774 mTabMediaController = nullptr; 1775 } 1776 1777 if (mCurrentLoad) { 1778 mCurrentLoad->Cancel(NS_BINDING_ABORTED, 1779 "CanonicalBrowsingContext::CanonicalDiscard"_ns); 1780 } 1781 1782 if (mWebProgress) { 1783 RefPtr<BrowsingContextWebProgress> progress = mWebProgress; 1784 progress->ContextDiscarded(); 1785 } 1786 1787 if (IsTop()) { 1788 BackgroundSessionStorageManager::RemoveManager(Id()); 1789 } 1790 1791 CancelSessionStoreUpdate(); 1792 1793 if (UsePrivateBrowsing() && EverAttached() && IsContent()) { 1794 DecreasePrivateCount(); 1795 } 1796 } 1797 1798 void CanonicalBrowsingContext::CanonicalAttach() { 1799 if (UsePrivateBrowsing() && IsContent()) { 1800 IncreasePrivateCount(); 1801 } 1802 } 1803 1804 void CanonicalBrowsingContext::AddPendingDiscard() { 1805 MOZ_ASSERT(!mFullyDiscarded); 1806 mPendingDiscards++; 1807 } 1808 1809 void CanonicalBrowsingContext::RemovePendingDiscard() { 1810 mPendingDiscards--; 1811 if (!mPendingDiscards) { 1812 mFullyDiscarded = true; 1813 auto listeners = std::move(mFullyDiscardedListeners); 1814 for (const auto& listener : listeners) { 1815 listener(Id()); 1816 } 1817 } 1818 } 1819 1820 void CanonicalBrowsingContext::AddFinalDiscardListener( 1821 std::function<void(uint64_t)>&& aListener) { 1822 if (mFullyDiscarded) { 1823 aListener(Id()); 1824 return; 1825 } 1826 mFullyDiscardedListeners.AppendElement(std::move(aListener)); 1827 } 1828 1829 void CanonicalBrowsingContext::SetForceAppWindowActive(bool aForceActive, 1830 ErrorResult& aRv) { 1831 MOZ_DIAGNOSTIC_ASSERT(IsChrome()); 1832 MOZ_DIAGNOSTIC_ASSERT(IsTop()); 1833 if (!IsChrome() || !IsTop()) { 1834 return aRv.ThrowNotAllowedError( 1835 "You shouldn't need to force this BrowsingContext to be active, use " 1836 ".isActive instead"); 1837 } 1838 if (mForceAppWindowActive == aForceActive) { 1839 return; 1840 } 1841 mForceAppWindowActive = aForceActive; 1842 RecomputeAppWindowVisibility(); 1843 } 1844 1845 void CanonicalBrowsingContext::RecomputeAppWindowVisibility() { 1846 MOZ_RELEASE_ASSERT(IsChrome()); 1847 MOZ_RELEASE_ASSERT(IsTop()); 1848 1849 const bool wasAlreadyActive = IsActive(); 1850 1851 nsCOMPtr<nsIWidget> widget; 1852 if (auto* docShell = GetDocShell()) { 1853 widget = nsDocShell::Cast(docShell)->GetMainWidget(); 1854 } 1855 1856 (void)NS_WARN_IF(!widget); 1857 const bool isNowActive = 1858 ForceAppWindowActive() || (widget && !widget->IsFullyOccluded() && 1859 widget->SizeMode() != nsSizeMode_Minimized); 1860 1861 if (isNowActive == wasAlreadyActive) { 1862 return; 1863 } 1864 1865 SetIsActiveInternal(isNowActive, IgnoreErrors()); 1866 if (widget) { 1867 // Pause if we are not active, resume if we are active. 1868 widget->PauseOrResumeCompositor(!isNowActive); 1869 } 1870 } 1871 1872 void CanonicalBrowsingContext::AdjustPrivateBrowsingCount( 1873 bool aPrivateBrowsing) { 1874 if (IsDiscarded() || !EverAttached() || IsChrome()) { 1875 return; 1876 } 1877 1878 MOZ_DIAGNOSTIC_ASSERT(aPrivateBrowsing == UsePrivateBrowsing()); 1879 if (aPrivateBrowsing) { 1880 IncreasePrivateCount(); 1881 } else { 1882 DecreasePrivateCount(); 1883 } 1884 } 1885 1886 void CanonicalBrowsingContext::NotifyStartDelayedAutoplayMedia() { 1887 WindowContext* windowContext = GetCurrentWindowContext(); 1888 if (!windowContext) { 1889 return; 1890 } 1891 1892 // As this function would only be called when user click the play icon on the 1893 // tab bar. That's clear user intent to play, so gesture activate the window 1894 // context so that the block-autoplay logic allows the media to autoplay. 1895 windowContext->NotifyUserGestureActivation(); 1896 AUTOPLAY_LOG("NotifyStartDelayedAutoplayMedia for chrome bc 0x%08" PRIx64, 1897 Id()); 1898 StartDelayedAutoplayMediaComponents(); 1899 // Notfiy all content browsing contexts which are related with the canonical 1900 // browsing content tree to start delayed autoplay media. 1901 1902 Group()->EachParent([&](ContentParent* aParent) { 1903 (void)aParent->SendStartDelayedAutoplayMediaComponents(this); 1904 }); 1905 } 1906 1907 void CanonicalBrowsingContext::NotifyMediaMutedChanged(bool aMuted, 1908 ErrorResult& aRv) { 1909 MOZ_ASSERT(!GetParent(), 1910 "Notify media mute change on non top-level context!"); 1911 SetMuted(aMuted, aRv); 1912 } 1913 1914 uint32_t CanonicalBrowsingContext::CountSiteOrigins( 1915 GlobalObject& aGlobal, 1916 const Sequence<OwningNonNull<BrowsingContext>>& aRoots) { 1917 nsTHashSet<nsCString> uniqueSiteOrigins; 1918 1919 for (const auto& root : aRoots) { 1920 root->PreOrderWalk([&](BrowsingContext* aContext) { 1921 WindowGlobalParent* windowGlobalParent = 1922 aContext->Canonical()->GetCurrentWindowGlobal(); 1923 if (windowGlobalParent) { 1924 nsIPrincipal* documentPrincipal = 1925 windowGlobalParent->DocumentPrincipal(); 1926 1927 bool isContentPrincipal = documentPrincipal->GetIsContentPrincipal(); 1928 if (isContentPrincipal) { 1929 nsCString siteOrigin; 1930 documentPrincipal->GetSiteOrigin(siteOrigin); 1931 uniqueSiteOrigins.Insert(siteOrigin); 1932 } 1933 } 1934 }); 1935 } 1936 1937 return uniqueSiteOrigins.Count(); 1938 } 1939 1940 /* static */ 1941 bool CanonicalBrowsingContext::IsPrivateBrowsingActive() { 1942 return gNumberOfPrivateContexts > 0; 1943 } 1944 1945 void CanonicalBrowsingContext::UpdateMediaControlAction( 1946 const MediaControlAction& aAction) { 1947 if (IsDiscarded()) { 1948 return; 1949 } 1950 ContentMediaControlKeyHandler::HandleMediaControlAction(this, aAction); 1951 Group()->EachParent([&](ContentParent* aParent) { 1952 (void)aParent->SendUpdateMediaControlAction(this, aAction); 1953 }); 1954 } 1955 1956 void CanonicalBrowsingContext::LoadURI(nsIURI* aURI, 1957 const LoadURIOptions& aOptions, 1958 ErrorResult& aError) { 1959 RefPtr<nsDocShellLoadState> loadState; 1960 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions( 1961 this, aURI, aOptions, getter_AddRefs(loadState)); 1962 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI); 1963 1964 if (NS_FAILED(rv)) { 1965 aError.Throw(rv); 1966 return; 1967 } 1968 1969 // Set the captive portal tab flag on the browsing context if requested 1970 if (loadState->GetIsCaptivePortalTab()) { 1971 (void)SetIsCaptivePortalTab(true); 1972 } 1973 1974 LoadURI(loadState, true); 1975 } 1976 1977 void CanonicalBrowsingContext::FixupAndLoadURIString( 1978 const nsAString& aURI, const LoadURIOptions& aOptions, 1979 ErrorResult& aError) { 1980 RefPtr<nsDocShellLoadState> loadState; 1981 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions( 1982 this, aURI, aOptions, getter_AddRefs(loadState)); 1983 1984 if (rv == NS_ERROR_MALFORMED_URI) { 1985 DisplayLoadError(aURI); 1986 return; 1987 } 1988 1989 if (NS_FAILED(rv)) { 1990 aError.Throw(rv); 1991 return; 1992 } 1993 1994 // Set the captive portal tab flag on the browsing context if requested 1995 if (loadState->GetIsCaptivePortalTab()) { 1996 (void)SetIsCaptivePortalTab(true); 1997 } 1998 1999 LoadURI(loadState, true); 2000 } 2001 2002 void CanonicalBrowsingContext::GoBack( 2003 const Optional<int32_t>& aCancelContentJSEpoch, 2004 bool aRequireUserInteraction, bool aUserActivation) { 2005 if (IsDiscarded()) { 2006 return; 2007 } 2008 2009 // Stop any known network loads if necessary. 2010 if (mCurrentLoad) { 2011 mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns); 2012 } 2013 2014 if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) { 2015 if (aCancelContentJSEpoch.WasPassed()) { 2016 docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value()); 2017 } 2018 docShell->GoBack(aRequireUserInteraction, aUserActivation); 2019 } else if (ContentParent* cp = GetContentParent()) { 2020 Maybe<int32_t> cancelContentJSEpoch; 2021 if (aCancelContentJSEpoch.WasPassed()) { 2022 cancelContentJSEpoch = Some(aCancelContentJSEpoch.Value()); 2023 } 2024 (void)cp->SendGoBack(this, cancelContentJSEpoch, aRequireUserInteraction, 2025 aUserActivation); 2026 } 2027 } 2028 void CanonicalBrowsingContext::GoForward( 2029 const Optional<int32_t>& aCancelContentJSEpoch, 2030 bool aRequireUserInteraction, bool aUserActivation) { 2031 if (IsDiscarded()) { 2032 return; 2033 } 2034 2035 // Stop any known network loads if necessary. 2036 if (mCurrentLoad) { 2037 mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns); 2038 } 2039 2040 if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) { 2041 if (aCancelContentJSEpoch.WasPassed()) { 2042 docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value()); 2043 } 2044 docShell->GoForward(aRequireUserInteraction, aUserActivation); 2045 } else if (ContentParent* cp = GetContentParent()) { 2046 Maybe<int32_t> cancelContentJSEpoch; 2047 if (aCancelContentJSEpoch.WasPassed()) { 2048 cancelContentJSEpoch.emplace(aCancelContentJSEpoch.Value()); 2049 } 2050 (void)cp->SendGoForward(this, cancelContentJSEpoch, aRequireUserInteraction, 2051 aUserActivation); 2052 } 2053 } 2054 void CanonicalBrowsingContext::GoToIndex( 2055 int32_t aIndex, const Optional<int32_t>& aCancelContentJSEpoch, 2056 bool aUserActivation) { 2057 if (IsDiscarded()) { 2058 return; 2059 } 2060 2061 // Stop any known network loads if necessary. 2062 if (mCurrentLoad) { 2063 mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns); 2064 } 2065 2066 if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) { 2067 if (aCancelContentJSEpoch.WasPassed()) { 2068 docShell->SetCancelContentJSEpoch(aCancelContentJSEpoch.Value()); 2069 } 2070 docShell->GotoIndex(aIndex, aUserActivation); 2071 } else if (ContentParent* cp = GetContentParent()) { 2072 Maybe<int32_t> cancelContentJSEpoch; 2073 if (aCancelContentJSEpoch.WasPassed()) { 2074 cancelContentJSEpoch.emplace(aCancelContentJSEpoch.Value()); 2075 } 2076 (void)cp->SendGoToIndex(this, aIndex, cancelContentJSEpoch, 2077 aUserActivation); 2078 } 2079 } 2080 2081 void CanonicalBrowsingContext::Reload(uint32_t aReloadFlags) { 2082 if (IsDiscarded()) { 2083 return; 2084 } 2085 2086 // Stop any known network loads if necessary. 2087 if (mCurrentLoad) { 2088 mCurrentLoad->Cancel(NS_BINDING_CANCELLED_OLD_LOAD, ""_ns); 2089 } 2090 2091 if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell())) { 2092 docShell->Reload(aReloadFlags); 2093 } else if (ContentParent* cp = GetContentParent()) { 2094 (void)cp->SendReload(this, aReloadFlags); 2095 } 2096 } 2097 2098 void CanonicalBrowsingContext::Stop(uint32_t aStopFlags) { 2099 if (IsDiscarded()) { 2100 return; 2101 } 2102 2103 // Stop any known network loads if necessary. 2104 if (mCurrentLoad && (aStopFlags & nsIWebNavigation::STOP_NETWORK)) { 2105 mCurrentLoad->Cancel(NS_BINDING_ABORTED, 2106 "CanonicalBrowsingContext::Stop"_ns); 2107 } 2108 2109 // Ask the docshell to stop to handle loads that haven't 2110 // yet reached here, as well as non-network activity. 2111 if (auto* docShell = nsDocShell::Cast(GetDocShell())) { 2112 docShell->Stop(aStopFlags); 2113 } else if (ContentParent* cp = GetContentParent()) { 2114 (void)cp->SendStopLoad(this, aStopFlags); 2115 } 2116 } 2117 2118 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessLaunched() { 2119 if (!mPromise) { 2120 return; 2121 } 2122 2123 if (mContentParentKeepAlive) { 2124 // If our new content process is still unloading from a previous process 2125 // switch, wait for that unload to complete before continuing. 2126 auto found = mTarget->FindUnloadingHost(mContentParentKeepAlive->ChildID()); 2127 if (found != mTarget->mUnloadingHosts.end()) { 2128 found->mCallbacks.AppendElement( 2129 [self = RefPtr{this}]() 2130 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { self->ProcessReady(); }); 2131 return; 2132 } 2133 } 2134 2135 ProcessReady(); 2136 } 2137 2138 void CanonicalBrowsingContext::PendingRemotenessChange::ProcessReady() { 2139 if (!mPromise) { 2140 return; 2141 } 2142 2143 MOZ_ASSERT(!mProcessReady); 2144 mProcessReady = true; 2145 MaybeFinish(); 2146 } 2147 2148 void CanonicalBrowsingContext::PendingRemotenessChange::MaybeFinish() { 2149 if (!mPromise) { 2150 return; 2151 } 2152 2153 if (!mProcessReady || mWaitingForPrepareToChange) { 2154 return; 2155 } 2156 2157 // If this BrowsingContext is embedded within the parent process, perform the 2158 // process switch directly. 2159 nsresult rv = mTarget->IsTopContent() ? FinishTopContent() : FinishSubframe(); 2160 if (NS_FAILED(rv)) { 2161 NS_WARNING("Error finishing PendingRemotenessChange!"); 2162 Cancel(rv); 2163 } else { 2164 Clear(); 2165 } 2166 } 2167 2168 // Logic for finishing a toplevel process change embedded within the parent 2169 // process. Due to frontend integration the logic differs substantially from 2170 // subframe process switches, and is handled separately. 2171 nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishTopContent() { 2172 MOZ_DIAGNOSTIC_ASSERT(mTarget->IsTop(), 2173 "We shouldn't be trying to change the remoteness of " 2174 "non-remote iframes"); 2175 2176 // Abort if our ContentParent died while process switching. 2177 if (mContentParentKeepAlive && 2178 NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) { 2179 return NS_ERROR_FAILURE; 2180 } 2181 2182 // While process switching, we need to check if any of our ancestors are 2183 // discarded or no longer current, in which case the process switch needs to 2184 // be aborted. 2185 RefPtr<CanonicalBrowsingContext> target(mTarget); 2186 if (target->IsDiscarded() || !target->AncestorsAreCurrent()) { 2187 return NS_ERROR_FAILURE; 2188 } 2189 2190 Element* browserElement = target->GetEmbedderElement(); 2191 if (!browserElement) { 2192 return NS_ERROR_FAILURE; 2193 } 2194 2195 nsCOMPtr<nsIBrowser> browser = browserElement->AsBrowser(); 2196 if (!browser) { 2197 return NS_ERROR_FAILURE; 2198 } 2199 2200 RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(browserElement); 2201 MOZ_RELEASE_ASSERT(frameLoaderOwner, 2202 "embedder browser must be nsFrameLoaderOwner"); 2203 2204 // If we're process switching a browsing context in private browsing 2205 // mode we might decrease the private browsing count to '0', which 2206 // would make us fire "last-pb-context-exited" and drop the private 2207 // session. To prevent that we artificially increment the number of 2208 // private browsing contexts with '1' until the process switch is done. 2209 bool usePrivateBrowsing = mTarget->UsePrivateBrowsing(); 2210 if (usePrivateBrowsing) { 2211 IncreasePrivateCount(); 2212 } 2213 2214 auto restorePrivateCount = MakeScopeExit([usePrivateBrowsing]() { 2215 if (usePrivateBrowsing) { 2216 DecreasePrivateCount(); 2217 } 2218 }); 2219 2220 // Tell frontend code that this browser element is about to change process. 2221 nsresult rv = browser->BeforeChangeRemoteness(); 2222 if (NS_FAILED(rv)) { 2223 return rv; 2224 } 2225 2226 // Some frontend code checks the value of the `remote` attribute on the 2227 // browser to determine if it is remote, so update the value. 2228 browserElement->SetAttr(kNameSpaceID_None, nsGkAtoms::remote, 2229 mContentParentKeepAlive ? u"true"_ns : u"false"_ns, 2230 /* notify */ true); 2231 2232 // The process has been created, hand off to nsFrameLoaderOwner to finish 2233 // the process switch. 2234 ErrorResult error; 2235 RefPtr keepAlive = mContentParentKeepAlive.get(); 2236 RefPtr specificGroup = mSpecificGroup; 2237 frameLoaderOwner->ChangeRemotenessToProcess(keepAlive, mOptions, 2238 specificGroup, error); 2239 if (error.Failed()) { 2240 return error.StealNSResult(); 2241 } 2242 2243 // Tell frontend the load is done. 2244 bool loadResumed = false; 2245 rv = browser->FinishChangeRemoteness(mPendingSwitchId, &loadResumed); 2246 if (NS_WARN_IF(NS_FAILED(rv))) { 2247 return rv; 2248 } 2249 2250 // We did it! The process switch is complete. 2251 RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader(); 2252 RefPtr<BrowserParent> newBrowser = frameLoader->GetBrowserParent(); 2253 if (!newBrowser) { 2254 if (mContentParentKeepAlive) { 2255 // Failed to create the BrowserParent somehow! Abort the process switch 2256 // attempt. 2257 return NS_ERROR_UNEXPECTED; 2258 } 2259 2260 if (!loadResumed) { 2261 RefPtr<nsDocShell> newDocShell = frameLoader->GetDocShell(error); 2262 if (error.Failed()) { 2263 return error.StealNSResult(); 2264 } 2265 2266 rv = newDocShell->ResumeRedirectedLoad(mPendingSwitchId, 2267 /* aHistoryIndex */ -1); 2268 if (NS_FAILED(rv)) { 2269 return rv; 2270 } 2271 } 2272 } else if (!loadResumed) { 2273 newBrowser->ResumeLoad(mPendingSwitchId); 2274 } 2275 2276 mPromise->Resolve( 2277 std::pair{newBrowser, 2278 RefPtr{frameLoader->GetBrowsingContext()->Canonical()}}, 2279 __func__); 2280 return NS_OK; 2281 } 2282 2283 nsresult CanonicalBrowsingContext::PendingRemotenessChange::FinishSubframe() { 2284 MOZ_DIAGNOSTIC_ASSERT(!mOptions.mReplaceBrowsingContext, 2285 "Cannot replace BC for subframe"); 2286 MOZ_DIAGNOSTIC_ASSERT(!mTarget->IsTop()); 2287 2288 // While process switching, we need to check if any of our ancestors are 2289 // discarded or no longer current, in which case the process switch needs to 2290 // be aborted. 2291 RefPtr<CanonicalBrowsingContext> target(mTarget); 2292 if (target->IsDiscarded() || !target->AncestorsAreCurrent()) { 2293 return NS_ERROR_FAILURE; 2294 } 2295 2296 if (NS_WARN_IF(!mContentParentKeepAlive)) { 2297 return NS_ERROR_FAILURE; 2298 } 2299 2300 RefPtr<WindowGlobalParent> embedderWindow = target->GetParentWindowContext(); 2301 if (NS_WARN_IF(!embedderWindow) || NS_WARN_IF(!embedderWindow->CanSend())) { 2302 return NS_ERROR_FAILURE; 2303 } 2304 2305 RefPtr<BrowserParent> embedderBrowser = embedderWindow->GetBrowserParent(); 2306 if (NS_WARN_IF(!embedderBrowser)) { 2307 return NS_ERROR_FAILURE; 2308 } 2309 2310 // If we're creating a new remote browser, and the host process is already 2311 // dead, abort the process switch. 2312 if (mContentParentKeepAlive != embedderBrowser->Manager() && 2313 NS_WARN_IF(mContentParentKeepAlive->IsShuttingDown())) { 2314 return NS_ERROR_FAILURE; 2315 } 2316 2317 RefPtr<BrowserParent> oldBrowser = target->GetBrowserParent(); 2318 target->SetCurrentBrowserParent(nullptr); 2319 2320 // If we were in a remote frame, trigger unloading of the remote window. The 2321 // previous BrowserParent is registered in `mUnloadingHosts` and will only be 2322 // cleared when the BrowserParent is fully destroyed. 2323 bool wasRemote = oldBrowser && oldBrowser->GetBrowsingContext() == target; 2324 if (wasRemote) { 2325 MOZ_DIAGNOSTIC_ASSERT(oldBrowser != embedderBrowser); 2326 MOZ_DIAGNOSTIC_ASSERT(oldBrowser->IsDestroyed() || 2327 oldBrowser->GetBrowserBridgeParent()); 2328 2329 // `oldBrowser` will clear the `UnloadingHost` status once the actor has 2330 // been destroyed. 2331 if (oldBrowser->CanSend()) { 2332 target->StartUnloadingHost(oldBrowser->Manager()->ChildID()); 2333 (void)oldBrowser->SendWillChangeProcess(); 2334 oldBrowser->Destroy(); 2335 } 2336 } 2337 2338 // Update which process is considered the current owner 2339 target->SetOwnerProcessId(mContentParentKeepAlive->ChildID()); 2340 2341 // If we're switching from remote to local, we don't need to create a 2342 // BrowserBridge, and can instead perform the switch directly. 2343 if (mContentParentKeepAlive == embedderBrowser->Manager()) { 2344 MOZ_DIAGNOSTIC_ASSERT( 2345 mPendingSwitchId, 2346 "We always have a PendingSwitchId, except for print-preview loads, " 2347 "which will never perform a process-switch to being in-process with " 2348 "their embedder"); 2349 MOZ_DIAGNOSTIC_ASSERT(wasRemote, 2350 "Attempt to process-switch from local to local?"); 2351 2352 target->SetCurrentBrowserParent(embedderBrowser); 2353 (void)embedderWindow->SendMakeFrameLocal(target, mPendingSwitchId); 2354 mPromise->Resolve(std::pair{embedderBrowser, target}, __func__); 2355 return NS_OK; 2356 } 2357 2358 // The BrowsingContext will be remote, either as an already-remote frame 2359 // changing processes, or as a local frame becoming remote. Construct a new 2360 // BrowserBridgeParent to host the remote content. 2361 target->SetCurrentBrowserParent(nullptr); 2362 2363 MOZ_DIAGNOSTIC_ASSERT(target->UseRemoteTabs() && target->UseRemoteSubframes(), 2364 "Not supported without fission"); 2365 uint32_t chromeFlags = nsIWebBrowserChrome::CHROME_REMOTE_WINDOW | 2366 nsIWebBrowserChrome::CHROME_FISSION_WINDOW; 2367 if (target->UsePrivateBrowsing()) { 2368 chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; 2369 } 2370 2371 nsCOMPtr<nsIPrincipal> initialPrincipal = 2372 NullPrincipal::Create(target->OriginAttributesRef()); 2373 RefPtr<nsOpenWindowInfo> openWindowInfo = new nsOpenWindowInfo(); 2374 openWindowInfo->mPrincipalToInheritForAboutBlank = initialPrincipal; 2375 WindowGlobalInit windowInit = 2376 WindowGlobalActor::AboutBlankInitializer(target, initialPrincipal); 2377 2378 // Create and initialize our new BrowserBridgeParent. 2379 TabId tabId(nsContentUtils::GenerateTabId()); 2380 RefPtr<BrowserBridgeParent> bridge = new BrowserBridgeParent(); 2381 nsresult rv = 2382 bridge->InitWithProcess(embedderBrowser, mContentParentKeepAlive.get(), 2383 windowInit, chromeFlags, tabId); 2384 if (NS_WARN_IF(NS_FAILED(rv))) { 2385 // If we've already destroyed our previous document, make a best-effort 2386 // attempt to recover from this failure and show the crashed tab UI. We only 2387 // do this in the previously-remote case, as previously in-process frames 2388 // will have their navigation cancelled, and will remain visible. 2389 if (wasRemote) { 2390 target->ShowSubframeCrashedUI(oldBrowser->GetBrowserBridgeParent()); 2391 } 2392 return rv; 2393 } 2394 2395 // Tell the embedder process a remoteness change is in-process. When this is 2396 // acknowledged, reset the in-flight ID if it used to be an in-process load. 2397 RefPtr<BrowserParent> newBrowser = bridge->GetBrowserParent(); 2398 { 2399 // If we weren't remote, mark our embedder window browser as unloading until 2400 // our embedder process has acked our MakeFrameRemote message. 2401 Maybe<uint64_t> clearChildID; 2402 if (!wasRemote) { 2403 clearChildID = Some(embedderBrowser->Manager()->ChildID()); 2404 target->StartUnloadingHost(*clearChildID); 2405 } 2406 auto callback = [target, clearChildID](auto&&) { 2407 if (clearChildID) { 2408 target->ClearUnloadingHost(*clearChildID); 2409 } 2410 }; 2411 2412 ManagedEndpoint<PBrowserBridgeChild> endpoint = 2413 embedderBrowser->OpenPBrowserBridgeEndpoint(bridge); 2414 MOZ_DIAGNOSTIC_ASSERT(endpoint.IsValid()); 2415 embedderWindow->SendMakeFrameRemote(target, std::move(endpoint), tabId, 2416 newBrowser->GetLayersId(), callback, 2417 callback); 2418 } 2419 2420 // Resume the pending load in our new process. 2421 if (mPendingSwitchId) { 2422 newBrowser->ResumeLoad(mPendingSwitchId); 2423 } 2424 2425 // We did it! The process switch is complete. 2426 mPromise->Resolve(std::pair{newBrowser, target}, __func__); 2427 return NS_OK; 2428 } 2429 2430 void CanonicalBrowsingContext::PendingRemotenessChange::Cancel(nsresult aRv) { 2431 if (!mPromise) { 2432 return; 2433 } 2434 2435 mPromise->Reject(aRv, __func__); 2436 Clear(); 2437 } 2438 2439 void CanonicalBrowsingContext::PendingRemotenessChange::Clear() { 2440 // Make sure we don't die while we're doing cleanup. 2441 RefPtr<PendingRemotenessChange> kungFuDeathGrip(this); 2442 if (mTarget) { 2443 MOZ_DIAGNOSTIC_ASSERT(mTarget->mPendingRemotenessChange == this); 2444 mTarget->mPendingRemotenessChange = nullptr; 2445 } 2446 2447 // When this PendingRemotenessChange was created, it was given a 2448 // `mContentParentKeepAlive`. 2449 mContentParentKeepAlive = nullptr; 2450 2451 // If we were given a specific group, stop keeping that group alive manually. 2452 if (mSpecificGroup) { 2453 mSpecificGroup->RemoveKeepAlive(); 2454 mSpecificGroup = nullptr; 2455 } 2456 2457 mPromise = nullptr; 2458 mTarget = nullptr; 2459 } 2460 2461 CanonicalBrowsingContext::PendingRemotenessChange::PendingRemotenessChange( 2462 CanonicalBrowsingContext* aTarget, RemotenessPromise::Private* aPromise, 2463 uint64_t aPendingSwitchId, const NavigationIsolationOptions& aOptions) 2464 : mTarget(aTarget), 2465 mPromise(aPromise), 2466 mPendingSwitchId(aPendingSwitchId), 2467 mOptions(aOptions) {} 2468 2469 CanonicalBrowsingContext::PendingRemotenessChange::~PendingRemotenessChange() { 2470 MOZ_ASSERT( 2471 !mPromise && !mTarget && !mContentParentKeepAlive && !mSpecificGroup, 2472 "should've already been Cancel() or Complete()-ed"); 2473 } 2474 2475 BrowserParent* CanonicalBrowsingContext::GetBrowserParent() const { 2476 return mCurrentBrowserParent; 2477 } 2478 2479 void CanonicalBrowsingContext::SetCurrentBrowserParent( 2480 BrowserParent* aBrowserParent) { 2481 MOZ_DIAGNOSTIC_ASSERT(!mCurrentBrowserParent || !aBrowserParent, 2482 "BrowsingContext already has a current BrowserParent!"); 2483 MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent, aBrowserParent->CanSend()); 2484 MOZ_DIAGNOSTIC_ASSERT_IF(aBrowserParent, 2485 aBrowserParent->Manager()->ChildID() == mProcessId); 2486 2487 // BrowserParent must either be directly for this BrowsingContext, or the 2488 // manager out our embedder WindowGlobal. 2489 MOZ_DIAGNOSTIC_ASSERT_IF( 2490 aBrowserParent && aBrowserParent->GetBrowsingContext() != this, 2491 GetParentWindowContext() && 2492 GetParentWindowContext()->Manager() == aBrowserParent); 2493 2494 if (aBrowserParent && IsTopContent() && !ManuallyManagesActiveness()) { 2495 aBrowserParent->SetRenderLayers(IsActive()); 2496 } 2497 2498 mCurrentBrowserParent = aBrowserParent; 2499 } 2500 2501 bool CanonicalBrowsingContext::ManuallyManagesActiveness() const { 2502 auto* el = GetEmbedderElement(); 2503 return el && el->IsXULElement() && el->HasAttr(nsGkAtoms::manualactiveness); 2504 } 2505 2506 RefPtr<CanonicalBrowsingContext::RemotenessPromise> 2507 CanonicalBrowsingContext::ChangeRemoteness( 2508 const NavigationIsolationOptions& aOptions, uint64_t aPendingSwitchId) { 2509 MOZ_DIAGNOSTIC_ASSERT(IsContent(), 2510 "cannot change the process of chrome contexts"); 2511 MOZ_DIAGNOSTIC_ASSERT( 2512 IsTop() == IsEmbeddedInProcess(0), 2513 "toplevel content must be embedded in the parent process"); 2514 MOZ_DIAGNOSTIC_ASSERT(!aOptions.mReplaceBrowsingContext || IsTop(), 2515 "Cannot replace BrowsingContext for subframes"); 2516 MOZ_DIAGNOSTIC_ASSERT( 2517 aOptions.mSpecificGroupId == 0 || aOptions.mReplaceBrowsingContext, 2518 "Cannot specify group ID unless replacing BC"); 2519 MOZ_DIAGNOSTIC_ASSERT(aPendingSwitchId || !IsTop(), 2520 "Should always have aPendingSwitchId for top-level " 2521 "frames"); 2522 2523 if (!AncestorsAreCurrent()) { 2524 NS_WARNING("An ancestor context is no longer current"); 2525 return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 2526 } 2527 2528 // Ensure our embedder hasn't been destroyed or asked to shutdown already. 2529 RefPtr<WindowGlobalParent> embedderWindowGlobal = GetEmbedderWindowGlobal(); 2530 if (!embedderWindowGlobal) { 2531 NS_WARNING("Non-embedded BrowsingContext"); 2532 return RemotenessPromise::CreateAndReject(NS_ERROR_UNEXPECTED, __func__); 2533 } 2534 2535 if (!embedderWindowGlobal->CanSend()) { 2536 NS_WARNING("Embedder already been destroyed."); 2537 return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); 2538 } 2539 2540 RefPtr<BrowserParent> embedderBrowser = 2541 embedderWindowGlobal->GetBrowserParent(); 2542 if (embedderBrowser && embedderBrowser->Manager()->IsShuttingDown()) { 2543 NS_WARNING("Embedder already asked to shutdown."); 2544 return RemotenessPromise::CreateAndReject(NS_ERROR_NOT_AVAILABLE, __func__); 2545 } 2546 2547 if (aOptions.mRemoteType.IsEmpty() && (!IsTop() || !GetEmbedderElement())) { 2548 NS_WARNING("Cannot load non-remote subframes"); 2549 return RemotenessPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); 2550 } 2551 2552 // Cancel ongoing remoteness changes. 2553 if (mPendingRemotenessChange) { 2554 mPendingRemotenessChange->Cancel(NS_ERROR_ABORT); 2555 MOZ_DIAGNOSTIC_ASSERT(!mPendingRemotenessChange, "Should have cleared"); 2556 } 2557 2558 auto promise = MakeRefPtr<RemotenessPromise::Private>(__func__); 2559 promise->UseDirectTaskDispatch(__func__); 2560 2561 RefPtr<PendingRemotenessChange> change = 2562 new PendingRemotenessChange(this, promise, aPendingSwitchId, aOptions); 2563 mPendingRemotenessChange = change; 2564 2565 // If we're replacing BrowsingContext, determine which BrowsingContextGroup 2566 // we'll switch into, taking into account load options. 2567 if (aOptions.mReplaceBrowsingContext) { 2568 change->mSpecificGroup = 2569 aOptions.mSpecificGroupId 2570 ? BrowsingContextGroup::GetOrCreate(aOptions.mSpecificGroupId) 2571 : BrowsingContextGroup::Create(aOptions.mShouldCrossOriginIsolate); 2572 change->mSpecificGroup->AddKeepAlive(); 2573 } 2574 2575 // Call `prepareToChangeRemoteness` in parallel with starting a new process 2576 // for <browser> loads. 2577 if (IsTop() && GetEmbedderElement()) { 2578 nsCOMPtr<nsIBrowser> browser = GetEmbedderElement()->AsBrowser(); 2579 if (!browser) { 2580 change->Cancel(NS_ERROR_FAILURE); 2581 return promise.forget(); 2582 } 2583 2584 RefPtr<Promise> blocker; 2585 nsresult rv = browser->PrepareToChangeRemoteness(getter_AddRefs(blocker)); 2586 if (NS_FAILED(rv)) { 2587 change->Cancel(rv); 2588 return promise.forget(); 2589 } 2590 2591 // Mark prepareToChange as unresolved, and wait for it to become resolved. 2592 if (blocker && blocker->State() != Promise::PromiseState::Resolved) { 2593 change->mWaitingForPrepareToChange = true; 2594 blocker->AddCallbacksWithCycleCollectedArgs( 2595 [change](JSContext*, JS::Handle<JS::Value>, ErrorResult&) 2596 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { 2597 change->mWaitingForPrepareToChange = false; 2598 change->MaybeFinish(); 2599 }, 2600 [change](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) { 2601 change->Cancel( 2602 Promise::TryExtractNSResultFromRejectionValue(aValue)); 2603 }); 2604 } 2605 } 2606 2607 // Switching a subframe to be local within it's embedding process. 2608 if (embedderBrowser && 2609 aOptions.mRemoteType == embedderBrowser->Manager()->GetRemoteType()) { 2610 MOZ_DIAGNOSTIC_ASSERT( 2611 aPendingSwitchId, 2612 "We always have a PendingSwitchId, except for print-preview loads, " 2613 "which will never perform a process-switch to being in-process with " 2614 "their embedder"); 2615 MOZ_DIAGNOSTIC_ASSERT(!aOptions.mReplaceBrowsingContext); 2616 MOZ_DIAGNOSTIC_ASSERT(!aOptions.mRemoteType.IsEmpty()); 2617 MOZ_DIAGNOSTIC_ASSERT(!change->mWaitingForPrepareToChange); 2618 MOZ_DIAGNOSTIC_ASSERT(!change->mSpecificGroup); 2619 2620 // Switching to local, so we don't need to create a new process, and will 2621 // instead use our embedder process. 2622 change->mContentParentKeepAlive = 2623 embedderBrowser->Manager()->AddKeepAlive(BrowserId()); 2624 change->ProcessLaunched(); 2625 return promise.forget(); 2626 } 2627 2628 // Switching to the parent process. 2629 if (aOptions.mRemoteType.IsEmpty()) { 2630 change->ProcessLaunched(); 2631 return promise.forget(); 2632 } 2633 2634 // If we're aiming to end up in a new process of the same type as our old 2635 // process, and then putting our previous document in the BFCache, try to stay 2636 // in the same process to avoid creating new processes unnecessarily. 2637 RefPtr<ContentParent> existingProcess = GetContentParent(); 2638 if (existingProcess && !existingProcess->IsShuttingDown() && 2639 aOptions.mReplaceBrowsingContext && 2640 aOptions.mRemoteType == existingProcess->GetRemoteType()) { 2641 change->mContentParentKeepAlive = 2642 existingProcess->AddKeepAlive(BrowserId()); 2643 change->ProcessLaunched(); 2644 return promise.forget(); 2645 } 2646 2647 // Try to predict which BrowsingContextGroup will be used for the final load 2648 // in this BrowsingContext. This has to be accurate if switching into an 2649 // existing group, as it will control what pool of processes will be used 2650 // for process selection. 2651 // 2652 // It's _technically_ OK to provide a group here if we're actually going to 2653 // switch into a brand new group, though it's sub-optimal, as it can 2654 // restrict the set of processes we're using. 2655 BrowsingContextGroup* finalGroup = 2656 aOptions.mReplaceBrowsingContext ? change->mSpecificGroup.get() : Group(); 2657 2658 bool preferUsed = 2659 StaticPrefs::browser_tabs_remote_subframesPreferUsed() && !IsTop(); 2660 2661 change->mContentParentKeepAlive = 2662 ContentParent::GetNewOrUsedLaunchingBrowserProcess( 2663 /* aRemoteType = */ aOptions.mRemoteType, 2664 /* aGroup = */ finalGroup, 2665 /* aPriority = */ hal::PROCESS_PRIORITY_FOREGROUND, 2666 /* aPreferUsed = */ preferUsed, 2667 /* aBrowserId */ BrowserId()); 2668 if (!change->mContentParentKeepAlive) { 2669 change->Cancel(NS_ERROR_FAILURE); 2670 return promise.forget(); 2671 } 2672 2673 if (change->mContentParentKeepAlive->IsLaunching()) { 2674 change->mContentParentKeepAlive 2675 ->WaitForLaunchAsync(/* aPriority */ hal::PROCESS_PRIORITY_FOREGROUND, 2676 /* aBrowserId */ BrowserId()) 2677 ->Then( 2678 GetMainThreadSerialEventTarget(), __func__, 2679 [change](UniqueContentParentKeepAlive&&) 2680 MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { 2681 change->ProcessLaunched(); 2682 }, 2683 [change]() { change->Cancel(NS_ERROR_FAILURE); }); 2684 } else { 2685 change->ProcessLaunched(); 2686 } 2687 return promise.forget(); 2688 } 2689 2690 void CanonicalBrowsingContext::MaybeSetPermanentKey(Element* aEmbedder) { 2691 MOZ_DIAGNOSTIC_ASSERT(IsTop()); 2692 2693 if (aEmbedder) { 2694 if (nsCOMPtr<nsIBrowser> browser = aEmbedder->AsBrowser()) { 2695 JS::Rooted<JS::Value> key(RootingCx()); 2696 if (NS_SUCCEEDED(browser->GetPermanentKey(&key)) && key.isObject()) { 2697 mPermanentKey = key; 2698 } 2699 } 2700 } 2701 } 2702 2703 MediaController* CanonicalBrowsingContext::GetMediaController() { 2704 // We would only create one media controller per tab, so accessing the 2705 // controller via the top-level browsing context. 2706 if (GetParent()) { 2707 return Cast(Top())->GetMediaController(); 2708 } 2709 2710 MOZ_ASSERT(!GetParent(), 2711 "Must access the controller from the top-level browsing context!"); 2712 // Only content browsing context can create media controller, we won't create 2713 // controller for chrome document, such as the browser UI. 2714 if (!mTabMediaController && !IsDiscarded() && IsContent()) { 2715 mTabMediaController = new MediaController(Id()); 2716 } 2717 return mTabMediaController; 2718 } 2719 2720 bool CanonicalBrowsingContext::HasCreatedMediaController() const { 2721 return !!mTabMediaController; 2722 } 2723 2724 bool CanonicalBrowsingContext::SupportsLoadingInParent( 2725 nsDocShellLoadState* aLoadState, uint64_t* aOuterWindowId) { 2726 // We currently don't support initiating loads in the parent when they are 2727 // watched by devtools. This is because devtools tracks loads using content 2728 // process notifications, which happens after the load is initiated in this 2729 // case. Devtools clears all prior requests when it detects a new navigation, 2730 // so it drops the main document load that happened here. 2731 if (WatchedByDevTools()) { 2732 return false; 2733 } 2734 2735 // Session-history-in-parent implementation relies currently on getting a 2736 // round trip through a child process. 2737 if (aLoadState->LoadIsFromSessionHistory()) { 2738 return false; 2739 } 2740 2741 // DocumentChannel currently only supports connecting channels into the 2742 // content process, so we can only support schemes that will always be loaded 2743 // there for now. Restrict to just http(s) for simplicity. 2744 if (!net::SchemeIsHttpOrHttps(aLoadState->URI())) { 2745 return false; 2746 } 2747 2748 if (WindowGlobalParent* global = GetCurrentWindowGlobal()) { 2749 nsCOMPtr<nsIURI> currentURI = global->GetDocumentURI(); 2750 if (currentURI) { 2751 nsCOMPtr<nsIURI> uri = aLoadState->URI(); 2752 bool newURIHasRef = false; 2753 uri->GetHasRef(&newURIHasRef); 2754 bool equalsExceptRef = false; 2755 uri->EqualsExceptRef(currentURI, &equalsExceptRef); 2756 2757 if (equalsExceptRef && newURIHasRef) { 2758 // This navigation is same-doc WRT the current one, we should pass it 2759 // down to the docshell to be handled. 2760 return false; 2761 } 2762 } 2763 2764 // If unloading the current document will cause a beforeunload listener to 2765 // run, then we need to start the load in that process after we fire the 2766 // event. 2767 if (PreOrderWalkFlag([&](BrowsingContext* aBC) { 2768 WindowContext* wc = aBC->GetCurrentWindowContext(); 2769 if (wc && wc->NeedsBeforeUnload()) { 2770 // We can stop as soon as we know at least one beforeunload listener 2771 // exists. 2772 return WalkFlag::Stop; 2773 } 2774 return WalkFlag::Next; 2775 }) == WalkFlag::Stop) { 2776 return false; 2777 } 2778 2779 *aOuterWindowId = global->OuterWindowId(); 2780 } 2781 return true; 2782 } 2783 2784 bool CanonicalBrowsingContext::AttemptSpeculativeLoadInParent( 2785 nsDocShellLoadState* aLoadState) { 2786 // We currently only support starting loads directly from the 2787 // CanonicalBrowsingContext for top-level BCs. 2788 // We currently only support starting loads directly from the 2789 // CanonicalBrowsingContext for top-level BCs. 2790 if (!IsTopContent() || !GetContentParent()) { 2791 return false; 2792 } 2793 2794 uint64_t outerWindowId = 0; 2795 if (!SupportsLoadingInParent(aLoadState, &outerWindowId)) { 2796 return false; 2797 } 2798 2799 // If we successfully open the DocumentChannel, then it'll register 2800 // itself using aLoadIdentifier and be kept alive until it completes 2801 // loading. 2802 return net::DocumentLoadListener::SpeculativeLoadInParent(this, aLoadState); 2803 } 2804 2805 bool CanonicalBrowsingContext::StartDocumentLoad( 2806 net::DocumentLoadListener* aLoad) { 2807 mCurrentLoad = aLoad; 2808 2809 if (NS_FAILED(SetCurrentLoadIdentifier(Some(aLoad->GetLoadIdentifier())))) { 2810 mCurrentLoad = nullptr; 2811 return false; 2812 } 2813 2814 return true; 2815 } 2816 2817 void CanonicalBrowsingContext::EndDocumentLoad(bool aContinueNavigating) { 2818 mCurrentLoad = nullptr; 2819 2820 if (!aContinueNavigating) { 2821 // Resetting the current load identifier on a discarded context 2822 // has no effect when a document load has finished. 2823 (void)SetCurrentLoadIdentifier(Nothing()); 2824 } 2825 } 2826 2827 already_AddRefed<nsIURI> CanonicalBrowsingContext::GetCurrentURI() const { 2828 nsCOMPtr<nsIURI> currentURI; 2829 if (nsIDocShell* docShell = GetDocShell()) { 2830 MOZ_ALWAYS_SUCCEEDS( 2831 nsDocShell::Cast(docShell)->GetCurrentURI(getter_AddRefs(currentURI))); 2832 } else { 2833 currentURI = mCurrentRemoteURI; 2834 } 2835 return currentURI.forget(); 2836 } 2837 2838 void CanonicalBrowsingContext::SetCurrentRemoteURI(nsIURI* aCurrentRemoteURI) { 2839 MOZ_ASSERT(!GetDocShell()); 2840 mCurrentRemoteURI = aCurrentRemoteURI; 2841 } 2842 2843 void CanonicalBrowsingContext::ResetSHEntryHasUserInteractionCache() { 2844 WindowContext* topWc = GetTopWindowContext(); 2845 if (topWc && !topWc->IsDiscarded()) { 2846 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false)); 2847 } 2848 } 2849 2850 void CanonicalBrowsingContext::HistoryCommitIndexAndLength() { 2851 nsID changeID = {}; 2852 CallerWillNotifyHistoryIndexAndLengthChanges caller(nullptr); 2853 HistoryCommitIndexAndLength(changeID, caller); 2854 } 2855 void CanonicalBrowsingContext::HistoryCommitIndexAndLength( 2856 const nsID& aChangeID, 2857 const CallerWillNotifyHistoryIndexAndLengthChanges& aProofOfCaller) { 2858 if (!IsTop()) { 2859 Cast(Top())->HistoryCommitIndexAndLength(aChangeID, aProofOfCaller); 2860 return; 2861 } 2862 2863 nsISHistory* shistory = GetSessionHistory(); 2864 if (!shistory) { 2865 return; 2866 } 2867 int32_t index = 0; 2868 shistory->GetIndex(&index); 2869 int32_t length = shistory->GetCount(); 2870 2871 GetChildSessionHistory()->SetIndexAndLength(index, length, aChangeID); 2872 2873 shistory->EvictOutOfRangeDocumentViewers(index); 2874 2875 Group()->EachParent([&](ContentParent* aParent) { 2876 (void)aParent->SendHistoryCommitIndexAndLength(this, index, length, 2877 aChangeID); 2878 }); 2879 } 2880 2881 void CanonicalBrowsingContext::SynchronizeLayoutHistoryState() { 2882 if (mActiveEntry) { 2883 if (IsInProcess()) { 2884 nsIDocShell* docShell = GetDocShell(); 2885 if (docShell) { 2886 docShell->PersistLayoutHistoryState(); 2887 2888 nsCOMPtr<nsILayoutHistoryState> state; 2889 docShell->GetLayoutHistoryState(getter_AddRefs(state)); 2890 if (state) { 2891 mActiveEntry->SetLayoutHistoryState(state); 2892 } 2893 } 2894 } else if (ContentParent* cp = GetContentParent()) { 2895 cp->SendGetLayoutHistoryState(this)->Then( 2896 GetCurrentSerialEventTarget(), __func__, 2897 [activeEntry = mActiveEntry]( 2898 const std::tuple<RefPtr<nsILayoutHistoryState>, Maybe<Wireframe>>& 2899 aResult) { 2900 if (std::get<0>(aResult)) { 2901 activeEntry->SetLayoutHistoryState(std::get<0>(aResult)); 2902 } 2903 if (std::get<1>(aResult)) { 2904 activeEntry->SetWireframe(std::get<1>(aResult)); 2905 } 2906 }, 2907 []() {}); 2908 } 2909 } 2910 } 2911 2912 void CanonicalBrowsingContext::SynchronizeNavigationAPIState( 2913 nsIStructuredCloneContainer* aState) { 2914 if (mActiveEntry) { 2915 mActiveEntry->SetNavigationAPIState(aState); 2916 } 2917 } 2918 2919 void CanonicalBrowsingContext::ResetScalingZoom() { 2920 // This currently only ever gets called in the parent process, and we 2921 // pass the message on to the WindowGlobalChild for the rootmost browsing 2922 // context. 2923 if (WindowGlobalParent* topWindow = GetTopWindowContext()) { 2924 (void)topWindow->SendResetScalingZoom(); 2925 } 2926 } 2927 2928 void CanonicalBrowsingContext::SetRestoreData(SessionStoreRestoreData* aData, 2929 ErrorResult& aError) { 2930 MOZ_DIAGNOSTIC_ASSERT(aData); 2931 2932 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); 2933 RefPtr<Promise> promise = Promise::Create(global, aError); 2934 if (aError.Failed()) { 2935 return; 2936 } 2937 2938 if (NS_WARN_IF(NS_FAILED(SetHasRestoreData(true)))) { 2939 aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2940 return; 2941 } 2942 2943 mRestoreState = new RestoreState(); 2944 mRestoreState->mData = aData; 2945 mRestoreState->mPromise = promise; 2946 } 2947 2948 already_AddRefed<Promise> CanonicalBrowsingContext::GetRestorePromise() { 2949 if (mRestoreState) { 2950 return do_AddRef(mRestoreState->mPromise); 2951 } 2952 return nullptr; 2953 } 2954 2955 void CanonicalBrowsingContext::ClearRestoreState() { 2956 if (IsDiscarded()) { 2957 return; 2958 } 2959 2960 if (!mRestoreState) { 2961 MOZ_DIAGNOSTIC_ASSERT(!GetHasRestoreData()); 2962 return; 2963 } 2964 if (mRestoreState->mPromise) { 2965 mRestoreState->mPromise->MaybeRejectWithUndefined(); 2966 } 2967 mRestoreState = nullptr; 2968 2969 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false)); 2970 } 2971 2972 void CanonicalBrowsingContext::RequestRestoreTabContent( 2973 WindowGlobalParent* aWindow) { 2974 MOZ_DIAGNOSTIC_ASSERT(IsTop()); 2975 2976 if (IsDiscarded() || !mRestoreState || !mRestoreState->mData) { 2977 return; 2978 } 2979 2980 CanonicalBrowsingContext* context = aWindow->GetBrowsingContext(); 2981 MOZ_DIAGNOSTIC_ASSERT(!context->IsDiscarded()); 2982 2983 RefPtr<SessionStoreRestoreData> data = 2984 mRestoreState->mData->FindDataForChild(context); 2985 2986 if (context->IsTop()) { 2987 MOZ_DIAGNOSTIC_ASSERT(context == this); 2988 2989 // We need to wait until the appropriate load event has fired before we 2990 // can "complete" the restore process, so if we're holding an empty data 2991 // object, just resolve the promise immediately. 2992 if (mRestoreState->mData->IsEmpty()) { 2993 MOZ_DIAGNOSTIC_ASSERT(!data || data->IsEmpty()); 2994 mRestoreState->Resolve(); 2995 ClearRestoreState(); 2996 return; 2997 } 2998 2999 // Since we're following load event order, we'll only arrive here for a 3000 // toplevel context after we've already sent down data for all child frames, 3001 // so it's safe to clear this reference now. The completion callback below 3002 // relies on the mData field being null to determine if all requests have 3003 // been sent out. 3004 mRestoreState->ClearData(); 3005 MOZ_ALWAYS_SUCCEEDS(SetHasRestoreData(false)); 3006 } 3007 3008 if (data && !data->IsEmpty()) { 3009 auto onTabRestoreComplete = [self = RefPtr{this}, 3010 state = RefPtr{mRestoreState}](auto) { 3011 state->mResolves++; 3012 if (!state->mData && state->mRequests == state->mResolves) { 3013 state->Resolve(); 3014 if (state == self->mRestoreState) { 3015 self->ClearRestoreState(); 3016 } 3017 } 3018 }; 3019 3020 mRestoreState->mRequests++; 3021 3022 if (data->CanRestoreInto(aWindow->GetDocumentURI())) { 3023 if (!aWindow->IsInProcess()) { 3024 aWindow->SendRestoreTabContent(WrapNotNull(data.get()), 3025 onTabRestoreComplete, 3026 onTabRestoreComplete); 3027 return; 3028 } 3029 data->RestoreInto(context); 3030 } 3031 3032 // This must be called both when we're doing an in-process restore, and when 3033 // we didn't do a restore at all due to a URL mismatch. 3034 onTabRestoreComplete(true); 3035 } 3036 } 3037 3038 void CanonicalBrowsingContext::RestoreState::Resolve() { 3039 MOZ_DIAGNOSTIC_ASSERT(mPromise); 3040 mPromise->MaybeResolveWithUndefined(); 3041 mPromise = nullptr; 3042 } 3043 3044 nsresult CanonicalBrowsingContext::WriteSessionStorageToSessionStore( 3045 const nsTArray<SSCacheCopy>& aSesssionStorage, uint32_t aEpoch) { 3046 nsCOMPtr<nsISessionStoreFunctions> sessionStoreFuncs = 3047 do_GetService("@mozilla.org/toolkit/sessionstore-functions;1"); 3048 if (!sessionStoreFuncs) { 3049 return NS_ERROR_FAILURE; 3050 } 3051 3052 nsCOMPtr<nsIXPConnectWrappedJS> wrapped = 3053 do_QueryInterface(sessionStoreFuncs); 3054 AutoJSAPI jsapi; 3055 if (!jsapi.Init(wrapped->GetJSObjectGlobal())) { 3056 return NS_ERROR_FAILURE; 3057 } 3058 3059 JS::Rooted<JS::Value> key(jsapi.cx(), Top()->PermanentKey()); 3060 3061 Record<nsCString, Record<nsString, nsString>> storage; 3062 JS::Rooted<JS::Value> update(jsapi.cx()); 3063 3064 if (!aSesssionStorage.IsEmpty()) { 3065 SessionStoreUtils::ConstructSessionStorageValues(this, aSesssionStorage, 3066 storage); 3067 if (!ToJSValue(jsapi.cx(), storage, &update)) { 3068 return NS_ERROR_FAILURE; 3069 } 3070 } else { 3071 update.setNull(); 3072 } 3073 3074 return sessionStoreFuncs->UpdateSessionStoreForStorage( 3075 Top()->GetEmbedderElement(), this, key, aEpoch, update); 3076 } 3077 3078 void CanonicalBrowsingContext::UpdateSessionStoreSessionStorage( 3079 const std::function<void()>& aDone) { 3080 using DataPromise = BackgroundSessionStorageManager::DataPromise; 3081 BackgroundSessionStorageManager::GetData( 3082 this, StaticPrefs::browser_sessionstore_dom_storage_limit(), 3083 /* aClearSessionStoreTimer = */ true) 3084 ->Then(GetCurrentSerialEventTarget(), __func__, 3085 [self = RefPtr{this}, aDone, epoch = GetSessionStoreEpoch()]( 3086 const DataPromise::ResolveOrRejectValue& valueList) { 3087 if (valueList.IsResolve()) { 3088 self->WriteSessionStorageToSessionStore( 3089 valueList.ResolveValue(), epoch); 3090 } 3091 aDone(); 3092 }); 3093 } 3094 3095 /* static */ 3096 void CanonicalBrowsingContext::UpdateSessionStoreForStorage( 3097 uint64_t aBrowsingContextId) { 3098 RefPtr<CanonicalBrowsingContext> browsingContext = Get(aBrowsingContextId); 3099 3100 if (!browsingContext) { 3101 return; 3102 } 3103 3104 browsingContext->UpdateSessionStoreSessionStorage([]() {}); 3105 } 3106 3107 void CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate() { 3108 if (!SessionStorePlatformCollection()) { 3109 return; 3110 } 3111 3112 if (!IsTop()) { 3113 Top()->MaybeScheduleSessionStoreUpdate(); 3114 return; 3115 } 3116 3117 if (IsInBFCache()) { 3118 return; 3119 } 3120 3121 if (mSessionStoreSessionStorageUpdateTimer) { 3122 return; 3123 } 3124 3125 if (!StaticPrefs::browser_sessionstore_debug_no_auto_updates()) { 3126 auto result = NS_NewTimerWithFuncCallback( 3127 [](nsITimer*, void* aClosure) { 3128 auto* context = static_cast<CanonicalBrowsingContext*>(aClosure); 3129 context->UpdateSessionStoreSessionStorage([]() {}); 3130 }, 3131 this, StaticPrefs::browser_sessionstore_interval(), 3132 nsITimer::TYPE_ONE_SHOT, 3133 "CanonicalBrowsingContext::MaybeScheduleSessionStoreUpdate"_ns); 3134 3135 if (result.isErr()) { 3136 return; 3137 } 3138 3139 mSessionStoreSessionStorageUpdateTimer = result.unwrap(); 3140 } 3141 } 3142 3143 void CanonicalBrowsingContext::CancelSessionStoreUpdate() { 3144 if (mSessionStoreSessionStorageUpdateTimer) { 3145 mSessionStoreSessionStorageUpdateTimer->Cancel(); 3146 mSessionStoreSessionStorageUpdateTimer = nullptr; 3147 } 3148 } 3149 3150 void CanonicalBrowsingContext::SetContainerFeaturePolicy( 3151 Maybe<FeaturePolicyInfo>&& aContainerFeaturePolicyInfo) { 3152 mContainerFeaturePolicyInfo = std::move(aContainerFeaturePolicyInfo); 3153 } 3154 3155 already_AddRefed<CanonicalBrowsingContext> 3156 CanonicalBrowsingContext::GetCrossGroupOpener() const { 3157 return Get(mCrossGroupOpenerId); 3158 } 3159 3160 void CanonicalBrowsingContext::SetCrossGroupOpenerId(uint64_t aOpenerId) { 3161 MOZ_DIAGNOSTIC_ASSERT(IsTopContent()); 3162 MOZ_DIAGNOSTIC_ASSERT(mCrossGroupOpenerId == 0, 3163 "Can only set CrossGroupOpenerId once"); 3164 mCrossGroupOpenerId = aOpenerId; 3165 } 3166 3167 void CanonicalBrowsingContext::SetCrossGroupOpener( 3168 CanonicalBrowsingContext* aCrossGroupOpener, ErrorResult& aRv) { 3169 if (!IsTopContent()) { 3170 aRv.ThrowNotAllowedError( 3171 "Can only set crossGroupOpener on toplevel content"); 3172 return; 3173 } 3174 if (mCrossGroupOpenerId != 0) { 3175 aRv.ThrowNotAllowedError("Can only set crossGroupOpener once"); 3176 return; 3177 } 3178 if (!aCrossGroupOpener) { 3179 aRv.ThrowNotAllowedError("Can't set crossGroupOpener to null"); 3180 return; 3181 } 3182 3183 SetCrossGroupOpenerId(aCrossGroupOpener->Id()); 3184 } 3185 3186 auto CanonicalBrowsingContext::FindUnloadingHost(uint64_t aChildID) 3187 -> nsTArray<UnloadingHost>::iterator { 3188 return std::find_if( 3189 mUnloadingHosts.begin(), mUnloadingHosts.end(), 3190 [&](const auto& host) { return host.mChildID == aChildID; }); 3191 } 3192 3193 void CanonicalBrowsingContext::ClearUnloadingHost(uint64_t aChildID) { 3194 // Notify any callbacks which were waiting for the host to finish unloading 3195 // that it has. 3196 auto found = FindUnloadingHost(aChildID); 3197 if (found != mUnloadingHosts.end()) { 3198 auto callbacks = std::move(found->mCallbacks); 3199 mUnloadingHosts.RemoveElementAt(found); 3200 for (const auto& callback : callbacks) { 3201 callback(); 3202 } 3203 } 3204 } 3205 3206 void CanonicalBrowsingContext::StartUnloadingHost(uint64_t aChildID) { 3207 MOZ_DIAGNOSTIC_ASSERT(FindUnloadingHost(aChildID) == mUnloadingHosts.end()); 3208 mUnloadingHosts.AppendElement(UnloadingHost{aChildID, {}}); 3209 } 3210 3211 void CanonicalBrowsingContext::BrowserParentDestroyed( 3212 BrowserParent* aBrowserParent, bool aAbnormalShutdown) { 3213 ClearUnloadingHost(aBrowserParent->Manager()->ChildID()); 3214 3215 // Handling specific to when the current BrowserParent has been destroyed. 3216 if (mCurrentBrowserParent == aBrowserParent) { 3217 mCurrentBrowserParent = nullptr; 3218 3219 // If this BrowserParent is for a subframe, attempt to recover from a 3220 // subframe crash by rendering the subframe crashed page in the embedding 3221 // content. 3222 if (aAbnormalShutdown) { 3223 ShowSubframeCrashedUI(aBrowserParent->GetBrowserBridgeParent()); 3224 } 3225 } 3226 } 3227 3228 void CanonicalBrowsingContext::ShowSubframeCrashedUI( 3229 BrowserBridgeParent* aBridge) { 3230 if (!aBridge || IsDiscarded() || !aBridge->CanSend()) { 3231 return; 3232 } 3233 3234 MOZ_DIAGNOSTIC_ASSERT(!aBridge->GetBrowsingContext() || 3235 aBridge->GetBrowsingContext() == this); 3236 3237 // There is no longer a current inner window within this 3238 // BrowsingContext, update the `CurrentInnerWindowId` field to reflect 3239 // this. 3240 MOZ_ALWAYS_SUCCEEDS(SetCurrentInnerWindowId(0)); 3241 3242 // The owning process will now be the embedder to render the subframe 3243 // crashed page, switch ownership back over. 3244 SetOwnerProcessId(aBridge->Manager()->Manager()->ChildID()); 3245 SetCurrentBrowserParent(aBridge->Manager()); 3246 3247 (void)aBridge->SendSubFrameCrashed(); 3248 } 3249 3250 static void LogBFCacheBlockingForDoc(BrowsingContext* aBrowsingContext, 3251 uint32_t aBFCacheCombo, bool aIsSubDoc) { 3252 if (aIsSubDoc) { 3253 nsAutoCString uri("[no uri]"); 3254 nsCOMPtr<nsIURI> currentURI = 3255 aBrowsingContext->Canonical()->GetCurrentURI(); 3256 if (currentURI) { 3257 uri = currentURI->GetSpecOrDefault(); 3258 } 3259 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, 3260 (" ** Blocked for document %s", uri.get())); 3261 } 3262 if (aBFCacheCombo & BFCacheStatus::EVENT_HANDLING_SUPPRESSED) { 3263 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, 3264 (" * event handling suppression")); 3265 } 3266 if (aBFCacheCombo & BFCacheStatus::SUSPENDED) { 3267 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * suspended Window")); 3268 } 3269 if (aBFCacheCombo & BFCacheStatus::UNLOAD_LISTENER) { 3270 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * unload listener")); 3271 } 3272 if (aBFCacheCombo & BFCacheStatus::REQUEST) { 3273 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * requests in the loadgroup")); 3274 } 3275 if (aBFCacheCombo & BFCacheStatus::ACTIVE_GET_USER_MEDIA) { 3276 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * GetUserMedia")); 3277 } 3278 if (aBFCacheCombo & BFCacheStatus::ACTIVE_PEER_CONNECTION) { 3279 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * PeerConnection")); 3280 } 3281 if (aBFCacheCombo & BFCacheStatus::CONTAINS_EME_CONTENT) { 3282 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * EME content")); 3283 } 3284 if (aBFCacheCombo & BFCacheStatus::CONTAINS_MSE_CONTENT) { 3285 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * MSE use")); 3286 } 3287 if (aBFCacheCombo & BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS) { 3288 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * Speech use")); 3289 } 3290 if (aBFCacheCombo & BFCacheStatus::HAS_USED_VR) { 3291 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * used VR")); 3292 } 3293 if (aBFCacheCombo & BFCacheStatus::BEFOREUNLOAD_LISTENER) { 3294 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * beforeunload listener")); 3295 } 3296 if (aBFCacheCombo & BFCacheStatus::ACTIVE_LOCK) { 3297 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * has active Web Locks")); 3298 } 3299 if (aBFCacheCombo & BFCacheStatus::PAGE_LOADING) { 3300 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * has page loading")); 3301 } 3302 } 3303 3304 bool CanonicalBrowsingContext::AllowedInBFCache( 3305 const Maybe<uint64_t>& aChannelId, nsIURI* aNewURI) { 3306 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) { 3307 nsAutoCString uri("[no uri]"); 3308 nsCOMPtr<nsIURI> currentURI = GetCurrentURI(); 3309 if (currentURI) { 3310 uri = currentURI->GetSpecOrDefault(); 3311 } 3312 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, ("Checking %s", uri.get())); 3313 } 3314 3315 if (IsInProcess()) { 3316 return false; 3317 } 3318 3319 uint32_t bfcacheCombo = 0; 3320 if (mRestoreState) { 3321 bfcacheCombo |= BFCacheStatus::RESTORING; 3322 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * during session restore")); 3323 } 3324 3325 if (Group()->Toplevels().Length() > 1) { 3326 bfcacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG; 3327 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, 3328 (" * auxiliary BrowsingContexts")); 3329 } 3330 3331 // There are not a lot of about:* pages that are allowed to load in 3332 // subframes, so it's OK to allow those few about:* pages enter BFCache. 3333 MOZ_ASSERT(IsTop(), "Trying to put a non top level BC into BFCache"); 3334 3335 WindowGlobalParent* wgp = GetCurrentWindowGlobal(); 3336 if (wgp && wgp->GetDocumentURI()) { 3337 nsCOMPtr<nsIURI> currentURI = wgp->GetDocumentURI(); 3338 // Exempt about:* pages from bfcache, with the exception of about:blank 3339 if (currentURI->SchemeIs("about") && 3340 !NS_IsAboutBlankAllowQueryAndFragment(currentURI)) { 3341 bfcacheCombo |= BFCacheStatus::ABOUT_PAGE; 3342 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, (" * about:* page")); 3343 } 3344 3345 if (aNewURI) { 3346 bool equalUri = false; 3347 aNewURI->Equals(currentURI, &equalUri); 3348 if (equalUri) { 3349 // When loading the same uri, disable bfcache so that 3350 // nsDocShell::OnNewURI transforms the load to LOAD_NORMAL_REPLACE. 3351 return false; 3352 } 3353 } 3354 } 3355 3356 // For telemetry we're collecting all the flags for all the BCs hanging 3357 // from this top-level BC. 3358 PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 3359 WindowGlobalParent* wgp = 3360 aBrowsingContext->Canonical()->GetCurrentWindowGlobal(); 3361 uint32_t subDocBFCacheCombo = wgp ? wgp->GetBFCacheStatus() : 0; 3362 if (wgp) { 3363 const Maybe<uint64_t>& singleChannelId = wgp->GetSingleChannelId(); 3364 if (singleChannelId.isSome()) { 3365 if (singleChannelId.value() == 0 || aChannelId.isNothing() || 3366 singleChannelId.value() != aChannelId.value()) { 3367 subDocBFCacheCombo |= BFCacheStatus::REQUEST; 3368 } 3369 } 3370 } 3371 3372 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) { 3373 LogBFCacheBlockingForDoc(aBrowsingContext, subDocBFCacheCombo, 3374 aBrowsingContext != this); 3375 } 3376 3377 bfcacheCombo |= subDocBFCacheCombo; 3378 }); 3379 3380 nsDocShell::ReportBFCacheComboTelemetry(bfcacheCombo); 3381 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) { 3382 nsAutoCString uri("[no uri]"); 3383 nsCOMPtr<nsIURI> currentURI = GetCurrentURI(); 3384 if (currentURI) { 3385 uri = currentURI->GetSpecOrDefault(); 3386 } 3387 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, 3388 (" +> %s %s be blocked from going into the BFCache", uri.get(), 3389 bfcacheCombo == 0 ? "shouldn't" : "should")); 3390 } 3391 3392 if (StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners()) { 3393 bfcacheCombo &= ~BFCacheStatus::UNLOAD_LISTENER; 3394 } 3395 3396 return bfcacheCombo == 0; 3397 } 3398 3399 struct ClearSiteWalkHistoryData { 3400 nsIPrincipal* mPrincipal = nullptr; 3401 bool mShouldClear = false; 3402 }; 3403 3404 // static 3405 nsresult CanonicalBrowsingContext::ContainsSameOriginBfcacheEntry( 3406 nsISHEntry* aEntry, mozilla::dom::BrowsingContext* aBC, int32_t aChildIndex, 3407 void* aData) { 3408 if (!aEntry) { 3409 return NS_OK; 3410 } 3411 3412 nsCOMPtr<nsIPrincipal> entryPrincipal; 3413 nsresult rv = 3414 aEntry->GetPartitionedPrincipalToInherit(getter_AddRefs(entryPrincipal)); 3415 3416 if (NS_FAILED(rv) || !entryPrincipal) { 3417 return NS_OK; 3418 } 3419 3420 ClearSiteWalkHistoryData* data = 3421 static_cast<ClearSiteWalkHistoryData*>(aData); 3422 if (data->mPrincipal->OriginAttributesRef() == 3423 entryPrincipal->OriginAttributesRef()) { 3424 nsCOMPtr<nsIURI> entryURI = aEntry->GetURI(); 3425 if (data->mPrincipal->IsSameOrigin(entryURI)) { 3426 data->mShouldClear = true; 3427 } else { 3428 nsSHistory::WalkHistoryEntries(aEntry, aBC, 3429 ContainsSameOriginBfcacheEntry, aData); 3430 } 3431 } 3432 return NS_OK; 3433 } 3434 3435 // static 3436 nsresult CanonicalBrowsingContext::ClearBfcacheByPrincipal( 3437 nsIPrincipal* aPrincipal) { 3438 NS_ENSURE_ARG_POINTER(aPrincipal); 3439 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 3440 3441 // Allow disabling the feature if unexpected regressions occur 3442 if (!StaticPrefs::privacy_clearSiteDataHeader_cache_bfcache_enabled()) { 3443 return NS_OK; 3444 } 3445 3446 // Iter through all open tabs by going through all top-level browsing 3447 // contexts. 3448 AutoTArray<RefPtr<BrowsingContextGroup>, 32> groups; 3449 BrowsingContextGroup::GetAllGroups(groups); 3450 for (auto& browsingContextGroup : groups) { 3451 for (auto& topLevel : browsingContextGroup->Toplevels()) { 3452 if (topLevel->IsDiscarded()) { 3453 continue; 3454 } 3455 3456 auto* bc = topLevel->Canonical(); 3457 nsSHistory* sh = static_cast<nsSHistory*>(bc->GetSessionHistory()); 3458 if (!sh) { 3459 continue; 3460 } 3461 3462 AutoTArray<nsCOMPtr<nsISHEntry>, 4> entriesToDelete; 3463 // We only need to traverse all top-level history items due to bfcache 3464 // only caching top level sites and partitioning origins. If an iframe has 3465 // the same origin, we only want to clear it, if the top level has the 3466 // same origin. 3467 for (nsCOMPtr<nsISHEntry>& entry : sh->Entries()) { 3468 // Determine whether this history entry matches the origin, or contains 3469 // an iframe with that origin 3470 ClearSiteWalkHistoryData data; 3471 data.mPrincipal = aPrincipal; 3472 CanonicalBrowsingContext::ContainsSameOriginBfcacheEntry(entry, nullptr, 3473 0, &data); 3474 3475 if (data.mShouldClear) { 3476 entriesToDelete.AppendElement(entry); 3477 } 3478 } 3479 for (nsCOMPtr<nsISHEntry>& entry : entriesToDelete) { 3480 sh->EvictDocumentViewerForEntry(entry); 3481 } 3482 } 3483 } 3484 return NS_OK; 3485 } 3486 3487 void CanonicalBrowsingContext::SetIsActive(bool aIsActive, ErrorResult& aRv) { 3488 #ifdef DEBUG 3489 if (MOZ_UNLIKELY(!ManuallyManagesActiveness())) { 3490 xpc_DumpJSStack(true, true, false); 3491 MOZ_ASSERT_UNREACHABLE( 3492 "Trying to manually manage activeness of a browsing context that isn't " 3493 "manually managed (see manualactiveness attribute)"); 3494 } 3495 #endif 3496 SetIsActiveInternal(aIsActive, aRv); 3497 } 3498 3499 void CanonicalBrowsingContext::SetTouchEventsOverride( 3500 dom::TouchEventsOverride aOverride, ErrorResult& aRv) { 3501 SetTouchEventsOverrideInternal(aOverride, aRv); 3502 } 3503 3504 void CanonicalBrowsingContext::SetTargetTopLevelLinkClicksToBlank( 3505 bool aTargetTopLevelLinkClicksToBlank, ErrorResult& aRv) { 3506 SetTargetTopLevelLinkClicksToBlankInternal(aTargetTopLevelLinkClicksToBlank, 3507 aRv); 3508 } 3509 3510 void CanonicalBrowsingContext::AddPageAwakeRequest() { 3511 MOZ_ASSERT(IsTop()); 3512 auto count = GetPageAwakeRequestCount(); 3513 MOZ_ASSERT(count < UINT32_MAX); 3514 (void)SetPageAwakeRequestCount(++count); 3515 } 3516 3517 void CanonicalBrowsingContext::RemovePageAwakeRequest() { 3518 MOZ_ASSERT(IsTop()); 3519 auto count = GetPageAwakeRequestCount(); 3520 MOZ_ASSERT(count > 0); 3521 (void)SetPageAwakeRequestCount(--count); 3522 } 3523 3524 void CanonicalBrowsingContext::CloneDocumentTreeInto( 3525 CanonicalBrowsingContext* aSource, const nsACString& aRemoteType, 3526 embedding::PrintData&& aPrintData) { 3527 NavigationIsolationOptions options; 3528 options.mRemoteType = aRemoteType; 3529 3530 mClonePromise = 3531 ChangeRemoteness(options, /* aPendingSwitchId = */ 0) 3532 ->Then( 3533 GetMainThreadSerialEventTarget(), __func__, 3534 [source = MaybeDiscardedBrowsingContext{aSource}, 3535 data = std::move(aPrintData)]( 3536 const std::pair<RefPtr<BrowserParent>, 3537 RefPtr<CanonicalBrowsingContext>>& aResult) 3538 -> RefPtr<GenericNonExclusivePromise> { 3539 const auto& [browserParent, browsingContext] = aResult; 3540 3541 RefPtr<BrowserBridgeParent> bridge = 3542 browserParent->GetBrowserBridgeParent(); 3543 return browserParent 3544 ->SendCloneDocumentTreeIntoSelf(source, data) 3545 ->Then( 3546 GetMainThreadSerialEventTarget(), __func__, 3547 [bridge]( 3548 BrowserParent::CloneDocumentTreeIntoSelfPromise:: 3549 ResolveOrRejectValue&& aValue) { 3550 // We're cloning a remote iframe, so we created a 3551 // BrowserBridge which makes us register an OOP load 3552 // (see Document::OOPChildLoadStarted), even though 3553 // this isn't a real load. We call 3554 // SendMaybeFireEmbedderLoadEvents here so that we do 3555 // register the end of the load (see 3556 // Document::OOPChildLoadDone). 3557 if (bridge) { 3558 (void)bridge->SendMaybeFireEmbedderLoadEvents( 3559 EmbedderElementEventType::NoEvent); 3560 } 3561 if (aValue.IsResolve() && aValue.ResolveValue()) { 3562 return GenericNonExclusivePromise::CreateAndResolve( 3563 true, __func__); 3564 } 3565 return GenericNonExclusivePromise::CreateAndReject( 3566 NS_ERROR_FAILURE, __func__); 3567 }); 3568 }, 3569 [](nsresult aRv) -> RefPtr<GenericNonExclusivePromise> { 3570 NS_WARNING( 3571 nsPrintfCString("Remote clone failed: %x\n", unsigned(aRv)) 3572 .get()); 3573 return GenericNonExclusivePromise::CreateAndReject( 3574 NS_ERROR_FAILURE, __func__); 3575 }); 3576 3577 mClonePromise->Then( 3578 GetMainThreadSerialEventTarget(), __func__, 3579 [self = RefPtr{this}]() { self->mClonePromise = nullptr; }); 3580 } 3581 3582 bool CanonicalBrowsingContext::StartApzAutoscroll(float aAnchorX, 3583 float aAnchorY, 3584 nsViewID aScrollId, 3585 uint32_t aPresShellId) { 3586 nsCOMPtr<nsIWidget> widget; 3587 mozilla::layers::LayersId layersId{0}; 3588 3589 if (IsInProcess()) { 3590 nsCOMPtr<nsPIDOMWindowOuter> outer = GetDOMWindow(); 3591 if (!outer) { 3592 return false; 3593 } 3594 3595 widget = widget::WidgetUtils::DOMWindowToWidget(outer); 3596 if (widget) { 3597 layersId = widget->GetRootLayerTreeId(); 3598 } 3599 } else { 3600 RefPtr<BrowserParent> parent = GetBrowserParent(); 3601 if (!parent) { 3602 return false; 3603 } 3604 3605 widget = parent->GetWidget(); 3606 layersId = parent->GetLayersId(); 3607 } 3608 3609 if (!widget || !widget->AsyncPanZoomEnabled()) { 3610 return false; 3611 } 3612 3613 // The anchor coordinates that are passed in are relative to the origin of the 3614 // screen, but we are sending them to APZ which only knows about coordinates 3615 // relative to the widget, so convert them accordingly. 3616 const LayoutDeviceIntPoint anchor = 3617 RoundedToInt(LayoutDevicePoint(aAnchorX, aAnchorY)) - 3618 widget->WidgetToScreenOffset(); 3619 3620 mozilla::layers::ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId); 3621 3622 return widget->StartAsyncAutoscroll( 3623 ViewAs<ScreenPixel>( 3624 anchor, PixelCastJustification::LayoutDeviceIsScreenForBounds), 3625 guid); 3626 } 3627 3628 void CanonicalBrowsingContext::StopApzAutoscroll(nsViewID aScrollId, 3629 uint32_t aPresShellId) { 3630 nsCOMPtr<nsIWidget> widget; 3631 mozilla::layers::LayersId layersId{0}; 3632 3633 if (IsInProcess()) { 3634 nsCOMPtr<nsPIDOMWindowOuter> outer = GetDOMWindow(); 3635 if (!outer) { 3636 return; 3637 } 3638 3639 widget = widget::WidgetUtils::DOMWindowToWidget(outer); 3640 if (widget) { 3641 layersId = widget->GetRootLayerTreeId(); 3642 } 3643 } else { 3644 RefPtr<BrowserParent> parent = GetBrowserParent(); 3645 if (!parent) { 3646 return; 3647 } 3648 3649 widget = parent->GetWidget(); 3650 layersId = parent->GetLayersId(); 3651 } 3652 3653 if (!widget || !widget->AsyncPanZoomEnabled()) { 3654 return; 3655 } 3656 3657 mozilla::layers::ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId); 3658 widget->StopAsyncAutoscroll(guid); 3659 } 3660 3661 already_AddRefed<nsISHEntry> 3662 CanonicalBrowsingContext::GetMostRecentLoadingSessionHistoryEntry() { 3663 if (mLoadingEntries.IsEmpty()) { 3664 return nullptr; 3665 } 3666 3667 RefPtr<SessionHistoryEntry> entry = mLoadingEntries.LastElement().mEntry; 3668 return entry.forget(); 3669 } 3670 3671 already_AddRefed<BounceTrackingState> 3672 CanonicalBrowsingContext::GetBounceTrackingState() { 3673 if (!mWebProgress) { 3674 return nullptr; 3675 } 3676 return mWebProgress->GetBounceTrackingState(); 3677 } 3678 3679 bool CanonicalBrowsingContext::CanOpenModalPicker() { 3680 if (!mozilla::StaticPrefs::browser_disable_pickers_background_tabs()) { 3681 return true; 3682 } 3683 3684 // Alway allows to open picker from chrome. 3685 if (IsChrome()) { 3686 return true; 3687 } 3688 3689 if (!IsActive()) { 3690 return false; 3691 } 3692 3693 mozilla::dom::Element* topFrameElement = GetTopFrameElement(); 3694 if (!mozilla::StaticPrefs:: 3695 browser_disable_pickers_in_hidden_extension_pages() && 3696 Windowless()) { 3697 WindowGlobalParent* wgp = GetCurrentWindowGlobal(); 3698 if (wgp && BasePrincipal::Cast(wgp->DocumentPrincipal())->AddonPolicy()) { 3699 // This may be a HiddenExtensionPage, e.g. an extension background page. 3700 return true; 3701 } 3702 } 3703 3704 RefPtr<Document> chromeDoc = TopCrossChromeBoundary()->GetExtantDocument(); 3705 if (!chromeDoc || !chromeDoc->HasFocus(mozilla::IgnoreErrors())) { 3706 return false; 3707 } 3708 3709 // Only allow web content to open a picker when it has focus. For example, if 3710 // the focus is on the URL bar, web content cannot open a picker, even if it 3711 // is the foreground tab. 3712 // topFrameElement may be a <browser> embedded in another <browser>. In that 3713 // case, verify that the full chain of <browser> elements has focus. 3714 while (topFrameElement) { 3715 RefPtr<Document> doc = topFrameElement->OwnerDoc(); 3716 if (doc->GetActiveElement() != topFrameElement) { 3717 return false; 3718 } 3719 topFrameElement = doc->GetBrowsingContext()->GetTopFrameElement(); 3720 // Eventually topFrameElement == nullptr, implying that we have reached the 3721 // top browser window (and chromeDoc == doc). 3722 } 3723 return true; 3724 } 3725 3726 bool CanonicalBrowsingContext::ShouldEnforceParentalControls() { 3727 if (StaticPrefs::security_restrict_to_adults_always()) { 3728 return true; 3729 } 3730 if (StaticPrefs::security_restrict_to_adults_respect_platform()) { 3731 bool enabled; 3732 nsCOMPtr<nsIParentalControlsService> pcs = 3733 do_CreateInstance("@mozilla.org/parental-controls-service;1"); 3734 nsresult rv = pcs->GetParentalControlsEnabled(&enabled); 3735 if (NS_FAILED(rv)) { 3736 return false; 3737 } 3738 return enabled; 3739 } 3740 return false; 3741 } 3742 3743 void CanonicalBrowsingContext::MaybeReconstructActiveEntryList() { 3744 MOZ_ASSERT(IsTop()); 3745 if (!Navigation::IsAPIEnabled()) { 3746 return; 3747 } 3748 3749 auto* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 3750 if (mActiveEntry && !shistory->ContainsEntry(mActiveEntry)) { 3751 shistory->ReconstructContiguousEntryList(); 3752 } 3753 } 3754 3755 // https://html.spec.whatwg.org/#concept-internal-location-ancestor-origin-objects-list 3756 // Creates the internal ancestor origins list (we store it on a canonical 3757 // browsing context). `aThisDocumentPrincipal` represents the origin for the 3758 // document who we are computing the list for, and 3759 // `aFrameReferrerPolicyAttribute` is the referrer policy attribute on the frame 3760 // that hosts the document. 3761 // For normal navigations `aFrameReferrerPolicyAttribute` will have been 3762 // snapshotted at a spec-appropriate time and passed in here, whereas 3763 // about:blank can read the attribute directly without the attribute having time 3764 // to change which makes the timing consistent with "normal" documents and for 3765 // about:blank this happens in `ContentParent::RecvUpdateAncestorOriginsList`. 3766 void CanonicalBrowsingContext::CreateRedactedAncestorOriginsList( 3767 nsIPrincipal* aThisDocumentPrincipal, 3768 ReferrerPolicy aFrameReferrerPolicyAttribute) { 3769 MOZ_DIAGNOSTIC_ASSERT(aThisDocumentPrincipal); 3770 nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals; 3771 // 4. if parentDoc is null, then return output 3772 CanonicalBrowsingContext* parent = GetParent(); 3773 if (!parent) { 3774 mPossiblyRedactedAncestorOriginsList = std::move(ancestorPrincipals); 3775 return; 3776 } 3777 3778 // 7. Let ancestorOrigins be parentLocation's internal ancestor origin objects 3779 // list. 3780 const Span<const nsCOMPtr<nsIPrincipal>> parentAncestorOriginsList = 3781 parent->GetPossiblyRedactedAncestorOriginsList(); 3782 3783 // 8. Let container be innerDoc's node navigable's container. 3784 WindowGlobalParent* ancestorWGP = GetParentWindowContext(); 3785 3786 // 10. If container is an iframe element, then set referrerPolicy to 3787 // container's referrerpolicy attribute's state's corresponding keyword. 3788 // Note: becomes the empty string if there is none 3789 auto referrerPolicy = aFrameReferrerPolicyAttribute; 3790 3791 // 11. Let masked be false. 3792 bool masked = false; 3793 3794 if (referrerPolicy == ReferrerPolicy::No_referrer) { 3795 // 12. If referrerPolicy is "no-referrer", then set masked to true. 3796 masked = true; 3797 } else if (referrerPolicy == ReferrerPolicy::Same_origin && 3798 !ancestorWGP->DocumentPrincipal()->Equals( 3799 aThisDocumentPrincipal)) { 3800 // 13. Otherwise, if referrerPolicy is "same-origin" and parentDoc's 3801 // origin is not same origin with innerDoc's origin, then set masked to 3802 // true. 3803 masked = true; 3804 } 3805 3806 if (masked) { 3807 // 14. If masked is true, then append a new opaque origin to output. 3808 ancestorPrincipals.AppendElement(nullptr); 3809 } else { 3810 // 15. Otherwise, append parentDoc's origin to output. 3811 auto* principal = ancestorWGP->DocumentPrincipal(); 3812 // when we serialize a "null principal", we leak information. Represent 3813 // them as actual nullptr instead. 3814 ancestorPrincipals.AppendElement( 3815 principal->GetIsNullPrincipal() ? nullptr : principal); 3816 } 3817 3818 // 16. For each ancestorOrigin of ancestorOrigins: 3819 for (const auto& ancestorOrigin : parentAncestorOriginsList) { 3820 // 16.1 if masked is true 3821 if (masked && ancestorOrigin && 3822 ancestorOrigin->Equals(ancestorWGP->DocumentPrincipal())) { 3823 // 16.1.1. If ancestorOrigin is same origin with parentDoc's origin, then 3824 // append a new opaque origin to output. 3825 ancestorPrincipals.AppendElement(nullptr); 3826 } else { 3827 // 16.1.2. Otherwise, append ancestorOrigin to output and set masked to 3828 // false. or 16.2. Otherwise, append ancestorOrigin to output. 3829 ancestorPrincipals.AppendElement(ancestorOrigin); 3830 masked = false; 3831 } 3832 } 3833 3834 // 17. Return output. 3835 // Only we don't return it. We're in the parent process. 3836 mPossiblyRedactedAncestorOriginsList = std::move(ancestorPrincipals); 3837 } 3838 3839 Span<const nsCOMPtr<nsIPrincipal>> 3840 CanonicalBrowsingContext::GetPossiblyRedactedAncestorOriginsList() const { 3841 return mPossiblyRedactedAncestorOriginsList; 3842 } 3843 3844 void CanonicalBrowsingContext::SetPossiblyRedactedAncestorOriginsList( 3845 nsTArray<nsCOMPtr<nsIPrincipal>> aAncestorOriginsList) { 3846 mPossiblyRedactedAncestorOriginsList = std::move(aAncestorOriginsList); 3847 } 3848 3849 EntryList* CanonicalBrowsingContext::GetActiveEntries() { 3850 if (!mActiveEntryList) { 3851 auto* shistory = static_cast<nsSHistory*>(GetSessionHistory()); 3852 if (shistory) { 3853 mActiveEntryList = shistory->EntryListFor(GetHistoryID()); 3854 } 3855 } 3856 return mActiveEntryList; 3857 } 3858 3859 void CanonicalBrowsingContext::SetEmbedderFrameReferrerPolicy( 3860 ReferrerPolicy aPolicy) { 3861 mEmbedderFrameReferrerPolicy = aPolicy; 3862 } 3863 3864 already_AddRefed<net::DocumentLoadListener> 3865 CanonicalBrowsingContext::GetCurrentLoad() { 3866 return do_AddRef(this->mCurrentLoad); 3867 } 3868 3869 NS_IMPL_CYCLE_COLLECTION_CLASS(CanonicalBrowsingContext) 3870 3871 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CanonicalBrowsingContext, 3872 BrowsingContext) 3873 tmp->mActiveEntryList = nullptr; 3874 tmp->mPermanentKey.setNull(); 3875 if (tmp->mSessionHistory) { 3876 tmp->mSessionHistory->SetBrowsingContext(nullptr); 3877 } 3878 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionHistory, mCurrentBrowserParent, 3879 mWebProgress, 3880 mSessionStoreSessionStorageUpdateTimer) 3881 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 3882 3883 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CanonicalBrowsingContext, 3884 BrowsingContext) 3885 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionHistory, mCurrentBrowserParent, 3886 mWebProgress, 3887 mSessionStoreSessionStorageUpdateTimer) 3888 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 3889 3890 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CanonicalBrowsingContext, 3891 BrowsingContext) 3892 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPermanentKey) 3893 NS_IMPL_CYCLE_COLLECTION_TRACE_END 3894 3895 NS_IMPL_ADDREF_INHERITED(CanonicalBrowsingContext, BrowsingContext) 3896 NS_IMPL_RELEASE_INHERITED(CanonicalBrowsingContext, BrowsingContext) 3897 3898 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CanonicalBrowsingContext) 3899 NS_INTERFACE_MAP_END_INHERITING(BrowsingContext) 3900 3901 } // namespace mozilla::dom