nsDocShell.cpp (538250B)
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 "nsDocShell.h" 8 9 #include <algorithm> 10 #include "mozilla/dom/HTMLFormElement.h" 11 12 #ifdef XP_WIN 13 # include <process.h> 14 # define getpid _getpid 15 #else 16 # include <unistd.h> // for getpid() 17 #endif 18 19 #include "nsDeviceContext.h" 20 #include "mozilla/Attributes.h" 21 #include "mozilla/AutoRestore.h" 22 #include "mozilla/BasePrincipal.h" 23 #include "mozilla/Casting.h" 24 #include "mozilla/CheckedInt.h" 25 #include "mozilla/Components.h" 26 #include "mozilla/Encoding.h" 27 #include "mozilla/EventStateManager.h" 28 #include "mozilla/HTMLEditor.h" 29 #include "mozilla/InputTaskManager.h" 30 #include "mozilla/LoadInfo.h" 31 #include "mozilla/Logging.h" 32 #include "mozilla/MediaFeatureChange.h" 33 #include "mozilla/Preferences.h" 34 #include "mozilla/PresShell.h" 35 #include "mozilla/SchedulerGroup.h" 36 #include "mozilla/ScopeExit.h" 37 #include "mozilla/ScrollContainerFrame.h" 38 #include "mozilla/ScrollTypes.h" 39 #include "mozilla/SimpleEnumerator.h" 40 #include "mozilla/StaticPrefs_browser.h" 41 #include "mozilla/StaticPrefs_docshell.h" 42 #include "mozilla/StaticPrefs_dom.h" 43 #include "mozilla/StaticPrefs_extensions.h" 44 #include "mozilla/StaticPrefs_privacy.h" 45 #include "mozilla/StaticPrefs_security.h" 46 #include "mozilla/StaticPrefs_ui.h" 47 #include "mozilla/StaticPrefs_fission.h" 48 #include "mozilla/StartupTimeline.h" 49 #include "mozilla/StorageAccess.h" 50 #include "mozilla/StoragePrincipalHelper.h" 51 #include "mozilla/Telemetry.h" 52 53 #include "mozilla/WidgetUtils.h" 54 55 #include "mozilla/dom/AutoEntryScript.h" 56 #include "mozilla/dom/ChildProcessChannelListener.h" 57 #include "mozilla/dom/ClientChannelHelper.h" 58 #include "mozilla/dom/ClientHandle.h" 59 #include "mozilla/dom/ClientInfo.h" 60 #include "mozilla/dom/ClientManager.h" 61 #include "mozilla/dom/ClientSource.h" 62 #include "mozilla/dom/ContentChild.h" 63 #include "mozilla/dom/ContentFrameMessageManager.h" 64 #include "mozilla/dom/DocGroup.h" 65 #include "mozilla/dom/Element.h" 66 #include "mozilla/dom/FragmentDirective.h" 67 #include "mozilla/dom/HTMLAnchorElement.h" 68 #include "mozilla/dom/HTMLIFrameElement.h" 69 #include "mozilla/dom/Navigation.h" 70 #include "mozilla/dom/NavigationBinding.h" 71 #include "mozilla/dom/NavigationHistoryEntry.h" 72 #include "mozilla/dom/NavigationUtils.h" 73 #include "mozilla/dom/PerformanceNavigation.h" 74 #include "mozilla/dom/PermissionMessageUtils.h" 75 #include "mozilla/dom/PolicyContainer.h" 76 #include "mozilla/dom/PopupBlocker.h" 77 #include "mozilla/dom/ScreenOrientation.h" 78 #include "mozilla/dom/ScriptSettings.h" 79 #include "mozilla/dom/ServiceWorkerInterceptController.h" 80 #include "mozilla/dom/ServiceWorkerUtils.h" 81 #include "mozilla/dom/SessionHistoryEntry.h" 82 #include "mozilla/dom/SessionStorageManager.h" 83 #include "mozilla/dom/SessionStoreChangeListener.h" 84 #include "mozilla/dom/SessionStoreChild.h" 85 #include "mozilla/dom/SessionStoreUtils.h" 86 #include "mozilla/dom/TrustedTypeUtils.h" 87 #include "mozilla/dom/TrustedTypesConstants.h" 88 #include "mozilla/dom/BrowserChild.h" 89 #include "mozilla/dom/ToJSValue.h" 90 #include "mozilla/dom/UserActivation.h" 91 #include "mozilla/dom/ChildSHistory.h" 92 #include "mozilla/dom/nsCSPContext.h" 93 #include "mozilla/dom/nsHTTPSOnlyUtils.h" 94 #include "mozilla/dom/LoadURIOptionsBinding.h" 95 #include "mozilla/dom/JSWindowActorChild.h" 96 #include "mozilla/dom/DocumentBinding.h" 97 #include "mozilla/glean/DocshellMetrics.h" 98 #include "mozilla/ipc/ProtocolUtils.h" 99 #include "mozilla/net/DocumentChannel.h" 100 #include "mozilla/net/DocumentChannelChild.h" 101 #include "mozilla/net/ParentChannelWrapper.h" 102 #include "mozilla/net/UrlClassifierFeatureFactory.h" 103 #include "ReferrerInfo.h" 104 105 #include "nsIAuthPrompt.h" 106 #include "nsIAuthPrompt2.h" 107 #include "nsICachingChannel.h" 108 #include "nsICaptivePortalService.h" 109 #include "nsIChannel.h" 110 #include "nsIChannelEventSink.h" 111 #include "nsIClassifiedChannel.h" 112 #include "nsIClassOfService.h" 113 #include "nsIConsoleReportCollector.h" 114 #include "nsIContent.h" 115 #include "nsIContentInlines.h" 116 #include "nsIContentSecurityPolicy.h" 117 #include "nsIController.h" 118 #include "nsIDocShellTreeItem.h" 119 #include "nsIDocShellTreeOwner.h" 120 #include "nsIDocumentViewer.h" 121 #include "mozilla/dom/Document.h" 122 #include "nsHTMLDocument.h" 123 #include "nsIDocumentLoaderFactory.h" 124 #include "nsIDOMWindow.h" 125 #include "nsIEditingSession.h" 126 #include "nsIEffectiveTLDService.h" 127 #include "nsIExternalProtocolService.h" 128 #include "nsIFormPOSTActionChannel.h" 129 #include "nsIFrame.h" 130 #include "nsIGlobalObject.h" 131 #include "nsIHttpChannel.h" 132 #include "nsIHttpChannelInternal.h" 133 #include "nsIIDNService.h" 134 #include "nsIInputStreamChannel.h" 135 #include "nsIInterfaceRequestorUtils.h" 136 #include "nsILayoutHistoryState.h" 137 #include "nsILoadInfo.h" 138 #include "nsILoadURIDelegate.h" 139 #include "nsIMultiPartChannel.h" 140 #include "nsINestedURI.h" 141 #include "nsINode.h" 142 #include "nsINSSErrorsService.h" 143 #include "nsIObserverService.h" 144 #include "nsIOService.h" 145 #include "nsIPrincipal.h" 146 #include "nsIPrivacyTransitionObserver.h" 147 #include "nsIPrompt.h" 148 #include "nsIPromptCollection.h" 149 #include "nsIPromptFactory.h" 150 #include "nsIPublicKeyPinningService.h" 151 #include "nsIReflowObserver.h" 152 #include "nsIScriptChannel.h" 153 #include "nsIScriptObjectPrincipal.h" 154 #include "nsIScriptSecurityManager.h" 155 #include "nsScriptSecurityManager.h" 156 #include "nsIScrollObserver.h" 157 #include "nsISupportsPrimitives.h" 158 #include "nsISecureBrowserUI.h" 159 #include "nsISeekableStream.h" 160 #include "nsISelectionDisplay.h" 161 #include "nsISHEntry.h" 162 #include "nsISiteSecurityService.h" 163 #include "nsISocketProvider.h" 164 #include "nsIStringBundle.h" 165 #include "nsIStructuredCloneContainer.h" 166 #include "nsIBrowserChild.h" 167 #include "nsITextToSubURI.h" 168 #include "nsITimedChannel.h" 169 #include "nsITimer.h" 170 #include "nsITransportSecurityInfo.h" 171 #include "nsIUploadChannel.h" 172 #include "nsIURIFixup.h" 173 #include "nsIURIMutator.h" 174 #include "nsIURILoader.h" 175 #include "nsIViewSourceChannel.h" 176 #include "nsIWebBrowserChrome.h" 177 #include "nsIWebBrowserFind.h" 178 #include "nsIWebProgress.h" 179 #include "nsIWidget.h" 180 #include "nsIWindowWatcher.h" 181 #include "nsIWritablePropertyBag2.h" 182 #include "nsIX509Cert.h" 183 #include "nsIXULRuntime.h" 184 185 #include "nsCommandManager.h" 186 #include "nsPIDOMWindow.h" 187 #include "nsPIWindowRoot.h" 188 189 #include "IHistory.h" 190 #include "IUrlClassifierUITelemetry.h" 191 192 #include "nsAboutProtocolUtils.h" 193 #include "nsArray.h" 194 #include "nsArrayUtils.h" 195 #include "nsBrowserStatusFilter.h" 196 #include "nsCExternalHandlerService.h" 197 #include "nsContentDLF.h" 198 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...) 199 #include "nsContentSecurityManager.h" 200 #include "nsContentSecurityUtils.h" 201 #include "nsContentUtils.h" 202 #include "nsCURILoader.h" 203 #include "nsDocElementCreatedNotificationRunner.h" 204 #include "nsDocShellCID.h" 205 #include "nsDocShellEditorData.h" 206 #include "nsDocShellEnumerator.h" 207 #include "nsDocShellLoadState.h" 208 #include "nsDocShellLoadTypes.h" 209 #include "nsDOMCID.h" 210 #include "nsDOMNavigationTiming.h" 211 #include "nsDSURIContentListener.h" 212 #include "nsEditingSession.h" 213 #include "nsError.h" 214 #include "nsEscape.h" 215 #include "nsFocusManager.h" 216 #include "nsGlobalWindowInner.h" 217 #include "nsGlobalWindowOuter.h" 218 #include "nsJSEnvironment.h" 219 #include "nsNetCID.h" 220 #include "nsNetUtil.h" 221 #include "nsObjectLoadingContent.h" 222 #include "nsPingListener.h" 223 #include "nsPoint.h" 224 #include "nsQueryObject.h" 225 #include "nsQueryActor.h" 226 #include "nsRect.h" 227 #include "nsRefreshTimer.h" 228 #include "nsSandboxFlags.h" 229 #include "nsSHEntry.h" 230 #include "nsSHistory.h" 231 #include "nsSHEntry.h" 232 #include "nsStructuredCloneContainer.h" 233 #include "nsSubDocumentFrame.h" 234 #include "nsURILoader.h" 235 #include "nsURLHelper.h" 236 #include "nsViewSourceHandler.h" 237 #include "nsWebBrowserFind.h" 238 #include "nsWhitespaceTokenizer.h" 239 #include "nsWidgetsCID.h" 240 #include "nsXULAppAPI.h" 241 242 #include "CertVerifier.h" 243 #include "ThirdPartyUtil.h" 244 #include "GeckoProfiler.h" 245 #include "mozilla/NullPrincipal.h" 246 #include "Navigator.h" 247 #include "prenv.h" 248 #include "mozilla/ipc/URIUtils.h" 249 #include "sslerr.h" 250 #include "mozpkix/pkix.h" 251 #include "NSSErrorsService.h" 252 253 #include "nsDocShellTelemetryUtils.h" 254 255 #include "nsIOpenWindowInfo.h" 256 257 #ifdef MOZ_PLACES 258 # include "mozilla/places/nsFaviconService.h" 259 # include "mozIPlacesPendingOperation.h" 260 #endif 261 262 #if NS_PRINT_PREVIEW 263 # include "nsIDocumentViewerPrint.h" 264 # include "nsIWebBrowserPrint.h" 265 #endif 266 267 using namespace mozilla; 268 using namespace mozilla::dom; 269 using namespace mozilla::net; 270 271 using mozilla::ipc::Endpoint; 272 273 // Threshold value in ms for META refresh based redirects 274 #define REFRESH_REDIRECT_TIMER 15000 275 276 static mozilla::LazyLogModule gCharsetMenuLog("CharsetMenu"); 277 static mozilla::LazyLogModule gDocShellLog("nsDocShell"); 278 279 #define LOGCHARSETMENU(args) \ 280 MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args) 281 282 #ifdef DEBUG 283 unsigned long nsDocShell::gNumberOfDocShells = 0; 284 static uint64_t gDocshellIDCounter = 0; 285 286 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging( 287 "DocShellAndDOMWindowLeak"); 288 #endif 289 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak"); 290 extern mozilla::LazyLogModule gPageCacheLog; 291 extern mozilla::LazyLogModule gNavigationAPILog; 292 mozilla::LazyLogModule gSHLog("SessionHistory"); 293 extern mozilla::LazyLogModule gSHIPBFCacheLog; 294 295 const char kAppstringsBundleURL[] = 296 "chrome://global/locale/appstrings.properties"; 297 298 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext, 299 nsILoadInfo* aLoadInfo) { 300 MOZ_ASSERT(aBrowsingContext); 301 MOZ_ASSERT(aLoadInfo); 302 303 if (aLoadInfo->GetExternalContentPolicyType() != 304 ExtContentPolicy::TYPE_DOCUMENT) { 305 return false; 306 } 307 308 return aBrowsingContext->IsTopContent(); 309 } 310 311 // True if loading for top level document loading in active tab. 312 static bool IsUrgentStart(BrowsingContext* aBrowsingContext, 313 nsILoadInfo* aLoadInfo, uint32_t aLoadType) { 314 MOZ_ASSERT(aBrowsingContext); 315 MOZ_ASSERT(aLoadInfo); 316 317 if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) { 318 return false; 319 } 320 321 if (aLoadType & 322 (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) { 323 return true; 324 } 325 326 return aBrowsingContext->IsActive(); 327 } 328 329 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext, 330 uint64_t aContentWindowID) 331 : nsDocLoader(true), 332 mContentWindowID(aContentWindowID), 333 mBrowsingContext(aBrowsingContext), 334 mParentCharset(nullptr), 335 mTreeOwner(nullptr), 336 mScrollbarPref(ScrollbarPreference::Auto), 337 mCharsetReloadState(eCharsetReloadInit), 338 mParentCharsetSource(0), 339 mFrameMargins(-1, -1), 340 mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome), 341 mPreviousEntryIndex(-1), 342 mLoadedEntryIndex(-1), 343 mBusyFlags(BUSY_FLAGS_NONE), 344 mAppType(nsIDocShell::APP_TYPE_UNKNOWN), 345 mLoadType(0), 346 mFailedLoadType(0), 347 mChannelToDisconnectOnPageHide(0), 348 mCreatingDocument(false), 349 #ifdef DEBUG 350 mInEnsureScriptEnv(false), 351 #endif 352 mInitialized(false), 353 mAllowSubframes(true), 354 mAllowMetaRedirects(true), 355 mAllowImages(true), 356 mAllowMedia(true), 357 mAllowDNSPrefetch(true), 358 mAllowWindowControl(true), 359 mCSSErrorReportingEnabled(false), 360 mAllowAuth(mItemType == typeContent), 361 mAllowKeywordFixup(false), 362 mDisableMetaRefreshWhenInactive(false), 363 mWindowDraggingAllowed(false), 364 mInFrameSwap(false), 365 mFiredUnloadEvent(false), 366 mEODForCurrentDocument(false), 367 mURIResultedInDocument(false), 368 mIsBeingDestroyed(false), 369 mIsExecutingOnLoadHandler(false), 370 mSavingOldViewer(false), 371 mInvisible(false), 372 mHasLoadedNonBlankURI(false), 373 mHasStartedLoadingOtherThanInitialBlankURI(false), 374 mBlankTiming(false), 375 mTitleValidForCurrentURI(false), 376 mWillChangeProcess(false), 377 mIsNavigating(false), 378 mForcedAutodetection(false), 379 mCheckingSessionHistory(false), 380 mNeedToReportActiveAfterLoadingBecomesActive(false) { 381 // If no outer window ID was provided, generate a new one. 382 if (aContentWindowID == 0) { 383 mContentWindowID = nsContentUtils::GenerateWindowId(); 384 } 385 386 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this)); 387 388 #ifdef DEBUG 389 mDocShellID = gDocshellIDCounter++; 390 // We're counting the number of |nsDocShells| to help find leaks 391 ++gNumberOfDocShells; 392 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info, 393 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this, 394 gNumberOfDocShells, getpid(), mDocShellID)); 395 #endif 396 } 397 398 void nsDocShell::DestroyDocumentViewer() { 399 if (!mDocumentViewer) { 400 return; 401 } 402 mDocumentViewer->Close(nullptr); 403 mDocumentViewer->Destroy(); 404 mDocumentViewer = nullptr; 405 } 406 407 nsDocShell::~nsDocShell() { 408 // Avoid notifying observers while we're in the dtor. 409 mIsBeingDestroyed = true; 410 411 Destroy(); 412 413 DestroyDocumentViewer(); 414 415 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this)); 416 417 #ifdef DEBUG 418 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) { 419 nsAutoCString url; 420 if (mLastOpenedURI) { 421 url = mLastOpenedURI->GetSpecOrDefault(); 422 423 // Data URLs can be very long, so truncate to avoid flooding the log. 424 const uint32_t maxURLLength = 1000; 425 if (url.Length() > maxURLLength) { 426 url.Truncate(maxURLLength); 427 } 428 } 429 430 // We're counting the number of |nsDocShells| to help find leaks 431 --gNumberOfDocShells; 432 MOZ_LOG( 433 gDocShellAndDOMWindowLeakLogging, LogLevel::Info, 434 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n", 435 (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get())); 436 } 437 #endif 438 } 439 440 nsresult nsDocShell::InitWindow(nsIWidget* aParentWidget, int32_t aX, 441 int32_t aY, int32_t aWidth, int32_t aHeight, 442 nsIOpenWindowInfo* aOpenWindowInfo, 443 mozilla::dom::WindowGlobalChild* aWindowActor) { 444 SetParentWidget(aParentWidget); 445 SetPositionAndSize(aX, aY, aWidth, aHeight, 0); 446 if (!Initialize(aOpenWindowInfo, aWindowActor)) { 447 // Since bug 543435, Initialize can fail and propagating this would 448 // cause callers to crash when they didn't previously (bug 2003244). 449 NS_WARNING("Failed to initialize docshell"); 450 } 451 452 return NS_OK; 453 } 454 455 bool nsDocShell::Initialize(nsIOpenWindowInfo* aOpenWindowInfo, 456 mozilla::dom::WindowGlobalChild* aWindowActor) { 457 if (mInitialized) { 458 // We've already been initialized. 459 MOZ_ASSERT(!aOpenWindowInfo, 460 "Tried to reinitialize with override principal"); 461 MOZ_ASSERT(!aWindowActor, "Tried to reinitialize with a window actor"); 462 return true; 463 } 464 465 MOZ_ASSERT(aOpenWindowInfo, 466 "Must have openwindowinfo if not already initialized."); 467 468 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, 469 "Unexpected item type in docshell"); 470 471 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false); 472 mInitialized = true; 473 474 mDisableMetaRefreshWhenInactive = 475 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled", 476 mDisableMetaRefreshWhenInactive); 477 478 bool succeeded = 479 NS_SUCCEEDED(CreateInitialDocumentViewer(aOpenWindowInfo, aWindowActor)); 480 481 if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) { 482 const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE 483 : NS_CHROME_WEBNAVIGATION_CREATE; 484 serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr); 485 } 486 487 return succeeded; 488 } 489 490 /* static */ 491 already_AddRefed<nsDocShell> nsDocShell::Create( 492 BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) { 493 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!"); 494 495 nsresult rv; 496 RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID); 497 498 // Initialize the underlying nsDocLoader. 499 rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext); 500 if (NS_WARN_IF(NS_FAILED(rv))) { 501 return nullptr; 502 } 503 504 // Create our ContentListener 505 ds->mContentListener = new nsDSURIContentListener(ds); 506 507 // We enable if we're in the parent process in order to support non-e10s 508 // configurations. 509 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's 510 // constructor. 511 if (XRE_IsParentProcess()) { 512 ds->mInterceptController = new ServiceWorkerInterceptController(); 513 } 514 515 // We want to hold a strong ref to the loadgroup, so it better hold a weak 516 // ref to us... use an InterfaceRequestorProxy to do this. 517 nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds); 518 ds->mLoadGroup->SetNotificationCallbacks(proxy); 519 520 // XXX(nika): We have our BrowsingContext, so we might be able to skip this. 521 // It could be nice to directly set up our DocLoader tree? 522 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds); 523 if (NS_WARN_IF(NS_FAILED(rv))) { 524 return nullptr; 525 } 526 527 uint32_t notifyMask = 528 nsIWebProgress::NOTIFY_STATE_ALL | nsIWebProgress::NOTIFY_LOCATION | 529 nsIWebProgress::NOTIFY_SECURITY | nsIWebProgress::NOTIFY_STATUS; 530 531 // NOTE: Only listen for NOTIFY_PROGRESS on toplevel BrowsingContexts, as 532 // listeners in the browser UI only cares about total progress on the toplevel 533 // context. Aggregation of the total progress is currently handled within 534 // `nsDocLoader`, and does not take out-of-process iframes into account. 535 if (aBrowsingContext->IsTop()) { 536 notifyMask |= nsIWebProgress::NOTIFY_PROGRESS; 537 } 538 539 // Add |ds| as a progress listener to itself. A little weird, but simpler 540 // than reproducing all the listener-notification logic in overrides of the 541 // various methods via which nsDocLoader can be notified. Note that this 542 // holds an nsWeakPtr to |ds|, so it's ok. 543 rv = ds->AddProgressListener(ds, notifyMask); 544 if (NS_WARN_IF(NS_FAILED(rv))) { 545 return nullptr; 546 } 547 548 // If our BrowsingContext has private browsing enabled, update the number of 549 // private browsing docshells. 550 if (aBrowsingContext->UsePrivateBrowsing()) { 551 ds->NotifyPrivateBrowsingChanged(); 552 } 553 554 // If our parent window is present in this process, set up our parent now. 555 RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext(); 556 if (parentWC && parentWC->IsInProcess()) { 557 // If we don't have a parent element anymore, we can't finish this load! 558 // How'd we get here? 559 RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement(); 560 if (!parentElement) { 561 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement"); 562 return nullptr; 563 } 564 565 // We have an in-process parent window, but don't have a parent nsDocShell? 566 // How'd we get here! 567 nsCOMPtr<nsIDocShell> parentShell = 568 parentElement->OwnerDoc()->GetDocShell(); 569 if (!parentShell) { 570 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell"); 571 return nullptr; 572 } 573 parentShell->AddChild(ds); 574 } 575 576 // Make |ds| the primary DocShell for the given context. 577 aBrowsingContext->SetDocShell(ds); 578 579 // Set |ds| default load flags on load group. 580 ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags()); 581 582 return ds.forget(); 583 } 584 585 void nsDocShell::DestroyChildren() { 586 for (auto* child : mChildList.ForwardRange()) { 587 nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child); 588 NS_ASSERTION(shell, "docshell has null child"); 589 590 if (shell) { 591 shell->SetTreeOwner(nullptr); 592 } 593 } 594 595 nsDocLoader::DestroyChildren(); 596 } 597 598 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader, 599 mScriptGlobal, mInitialClientSource, 600 mBrowsingContext, 601 mChromeEventHandler, 602 mBCWebProgressStatusFilter) 603 604 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader) 605 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader) 606 607 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell) 608 NS_INTERFACE_MAP_ENTRY(nsIDocShell) 609 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem) 610 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation) 611 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) 612 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI) 613 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) 614 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 615 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor) 616 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) 617 NS_INTERFACE_MAP_ENTRY(nsILoadContext) 618 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController, 619 mInterceptController) 620 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader) 621 622 NS_IMETHODIMP 623 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) { 624 MOZ_ASSERT(aSink, "null out param"); 625 626 *aSink = nullptr; 627 628 if (aIID.Equals(NS_GET_IID(nsICommandManager))) { 629 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE); 630 *aSink = static_cast<nsICommandManager*>(mCommandManager.get()); 631 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) { 632 *aSink = mContentListener; 633 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) || 634 aIID.Equals(NS_GET_IID(nsIGlobalObject)) || 635 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) || 636 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) || 637 aIID.Equals(NS_GET_IID(nsIDOMWindow))) && 638 NS_SUCCEEDED(EnsureScriptEnvironment())) { 639 return mScriptGlobal->QueryInterface(aIID, aSink); 640 } else if (aIID.Equals(NS_GET_IID(Document)) && VerifyDocumentViewer()) { 641 RefPtr<Document> doc = mDocumentViewer->GetDocument(); 642 doc.forget(aSink); 643 return *aSink ? NS_OK : NS_NOINTERFACE; 644 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) && 645 NS_SUCCEEDED(EnsureScriptEnvironment())) { 646 nsresult rv; 647 nsCOMPtr<nsIWindowWatcher> wwatch = 648 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); 649 NS_ENSURE_SUCCESS(rv, rv); 650 651 // Get the an auth prompter for our window so that the parenting 652 // of the dialogs works as it should when using tabs. 653 nsIPrompt* prompt; 654 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt); 655 NS_ENSURE_SUCCESS(rv, rv); 656 657 *aSink = prompt; 658 return NS_OK; 659 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || 660 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { 661 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink)) 662 ? NS_OK 663 : NS_NOINTERFACE; 664 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) { 665 // This is deprecated, you should instead directly get 666 // ChildSHistory from the browsing context. 667 MOZ_DIAGNOSTIC_ASSERT( 668 false, "Do not try to get a nsISHistory interface from nsIDocShell"); 669 return NS_NOINTERFACE; 670 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) { 671 nsresult rv = EnsureFind(); 672 if (NS_FAILED(rv)) { 673 return rv; 674 } 675 676 *aSink = mFind; 677 NS_ADDREF((nsISupports*)*aSink); 678 return NS_OK; 679 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) { 680 if (PresShell* presShell = GetPresShell()) { 681 return presShell->QueryInterface(aIID, aSink); 682 } 683 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) { 684 nsCOMPtr<nsIDocShellTreeOwner> treeOwner; 685 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner)); 686 if (NS_SUCCEEDED(rv) && treeOwner) { 687 return treeOwner->QueryInterface(aIID, aSink); 688 } 689 } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) { 690 *aSink = GetBrowserChild().take(); 691 return *aSink ? NS_OK : NS_ERROR_FAILURE; 692 } else { 693 return nsDocLoader::GetInterface(aIID, aSink); 694 } 695 696 NS_IF_ADDREF(((nsISupports*)*aSink)); 697 return *aSink ? NS_OK : NS_NOINTERFACE; 698 } 699 700 NS_IMETHODIMP 701 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) { 702 // Note: this gets called fairly early (before a pageload actually starts). 703 // We could probably defer this even longer. 704 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild(); 705 static_cast<BrowserChild*>(browserChild.get()) 706 ->SetCancelContentJSEpoch(aEpoch); 707 return NS_OK; 708 } 709 710 nsresult nsDocShell::CheckDisallowedJavascriptLoad( 711 nsDocShellLoadState* aLoadState) { 712 if (!aLoadState->URI()->SchemeIs("javascript")) { 713 return NS_OK; 714 } 715 716 if (nsCOMPtr<nsIPrincipal> targetPrincipal = 717 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) { 718 if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) { 719 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI; 720 } 721 return NS_OK; 722 } 723 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI; 724 } 725 726 NS_IMETHODIMP 727 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) { 728 return LoadURI(aLoadState, aSetNavigating, false); 729 } 730 731 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, 732 bool aSetNavigating, 733 bool aContinueHandlingSubframeHistory) { 734 MOZ_ASSERT(aLoadState, "Must have a valid load state!"); 735 // NOTE: This comparison between what appears to be internal/external load 736 // flags is intentional, as it's ensuring that the caller isn't using any of 737 // the flags reserved for implementations by the `nsIWebNavigation` interface. 738 // In the future, this check may be dropped. 739 MOZ_ASSERT( 740 (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0, 741 "Should not have these flags set"); 742 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(), 743 "Targeting doesn't occur until InternalLoad"); 744 745 if (!aLoadState->TriggeringPrincipal()) { 746 MOZ_ASSERT(false, "LoadURI must have a triggering principal"); 747 return NS_ERROR_FAILURE; 748 } 749 750 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState)); 751 752 bool oldIsNavigating = mIsNavigating; 753 auto cleanupIsNavigating = 754 MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; }); 755 if (aSetNavigating) { 756 mIsNavigating = true; 757 } 758 759 PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden; 760 if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) { 761 popupState = PopupBlocker::openAllowed; 762 // If we allow popups as part of the navigation, ensure we fake a user 763 // interaction, so that popups can, in fact, be allowed to open. 764 if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) { 765 wc->NotifyUserGestureActivation(); 766 } 767 } 768 769 AutoPopupStatePusher statePusher(popupState); 770 771 if (aLoadState->GetCancelContentJSEpoch().isSome()) { 772 SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch()); 773 } 774 775 // Note: we allow loads to get through here even if mFiredUnloadEvent is 776 // true; that case will get handled in LoadInternal or LoadHistoryEntry, 777 // so we pass false as the second parameter to IsNavigationAllowed. 778 // However, we don't allow the page to change location *in the middle of* 779 // firing beforeunload, so we do need to check if *beforeunload* is currently 780 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP. 781 if (!IsNavigationAllowed(true, false)) { 782 return NS_OK; // JS may not handle returning of an error code 783 } 784 785 nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags(); 786 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) { 787 defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE; 788 } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) { 789 defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE; 790 } 791 792 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags)); 793 794 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) && 795 mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) { 796 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI); 797 } 798 799 // LoadType used to be set to a default value here, if no LoadInfo/LoadState 800 // object was passed in. That functionality has been removed as of bug 801 // 1492648. LoadType should now be set up by the caller at the time they 802 // create their nsDocShellLoadState object to pass into LoadURI. 803 804 MOZ_LOG( 805 gDocShellLeakLog, LogLevel::Debug, 806 ("nsDocShell[%p]: loading %s with flags 0x%08x", this, 807 aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags())); 808 809 if ((!aLoadState->LoadIsFromSessionHistory() && 810 !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), 811 LOAD_FLAGS_REPLACE_HISTORY)) || 812 aContinueHandlingSubframeHistory) { 813 // This is possibly a subframe, so handle it accordingly. 814 // 815 // If history exists, it will be loaded into the aLoadState object, and the 816 // LoadType will be changed. 817 if (MaybeHandleSubframeHistory(aLoadState, 818 aContinueHandlingSubframeHistory)) { 819 // MaybeHandleSubframeHistory returns true if we need to continue loading 820 // asynchronously. 821 return NS_OK; 822 } 823 } 824 825 if (aLoadState->LoadIsFromSessionHistory()) { 826 MOZ_LOG(gSHLog, LogLevel::Debug, 827 ("nsDocShell[%p]: loading from session history", this)); 828 829 if (!mozilla::SessionHistoryInParent()) { 830 nsCOMPtr<nsISHEntry> entry = aLoadState->SHEntry(); 831 return LoadHistoryEntry(entry, aLoadState->LoadType(), 832 aLoadState->HasValidUserGestureActivation()); 833 } 834 835 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()? 836 return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(), 837 aLoadState->LoadType(), 838 aLoadState->HasValidUserGestureActivation(), 839 aLoadState->NotifiedBeforeUnloadListeners()); 840 } 841 842 // On history navigation via Back/Forward buttons, don't execute 843 // automatic JavaScript redirection such as |location.href = ...| or 844 // |window.open()| 845 // 846 // LOAD_NORMAL: window.open(...) etc. 847 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...) 848 if ((aLoadState->LoadType() == LOAD_NORMAL || 849 aLoadState->LoadType() == LOAD_STOP_CONTENT) && 850 ShouldBlockLoadingForBackButton()) { 851 return NS_OK; 852 } 853 854 BrowsingContext::Type bcType = mBrowsingContext->GetType(); 855 856 // Set up the inheriting principal in LoadState. 857 nsresult rv = aLoadState->SetupInheritingPrincipal( 858 bcType, mBrowsingContext->OriginAttributesRef()); 859 NS_ENSURE_SUCCESS(rv, rv); 860 861 rv = aLoadState->SetupTriggeringPrincipal( 862 mBrowsingContext->OriginAttributesRef()); 863 NS_ENSURE_SUCCESS(rv, rv); 864 865 aLoadState->CalculateLoadURIFlags(); 866 867 MOZ_ASSERT(aLoadState->TypeHint().IsVoid(), 868 "Typehint should be null when calling InternalLoad from LoadURI"); 869 MOZ_ASSERT(aLoadState->FileName().IsVoid(), 870 "FileName should be null when calling InternalLoad from LoadURI"); 871 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(), 872 "Shouldn't be loading from an entry when calling InternalLoad " 873 "from LoadURI"); 874 875 // If we have a system triggering principal, we can assume that this load was 876 // triggered by some UI in the browser chrome, such as the URL bar or 877 // bookmark bar. This should count as a user interaction for the current sh 878 // entry, so that the user may navigate back to the current entry, from the 879 // entry that is going to be added as part of this load. 880 nsCOMPtr<nsIPrincipal> triggeringPrincipal = 881 aLoadState->TriggeringPrincipal(); 882 if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) { 883 if (mozilla::SessionHistoryInParent()) { 884 WindowContext* topWc = mBrowsingContext->GetTopWindowContext(); 885 if (topWc && !topWc->IsDiscarded()) { 886 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true)); 887 } 888 } else { 889 bool oshe = false; 890 nsCOMPtr<nsISHEntry> currentSHEntry; 891 GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe); 892 if (currentSHEntry) { 893 currentSHEntry->SetHasUserInteraction(true); 894 } 895 } 896 } 897 898 rv = InternalLoad(aLoadState); 899 NS_ENSURE_SUCCESS(rv, rv); 900 901 if (aLoadState->GetOriginalURIString().isSome()) { 902 // Save URI string in case it's needed later when 903 // sending to search engine service in EndPageLoad() 904 mOriginalUriString = *aLoadState->GetOriginalURIString(); 905 } 906 907 return NS_OK; 908 } 909 910 bool nsDocShell::IsLoadingFromSessionHistory() { 911 return mActiveEntryIsLoadingFromSessionHistory; 912 } 913 914 // StopDetector is modeled similarly to OnloadBlocker; it is a rather 915 // dummy nsIRequest implementation which can be added to an nsILoadGroup to 916 // detect Cancel calls. 917 class StopDetector final : public nsIRequest { 918 public: 919 StopDetector() = default; 920 921 NS_DECL_ISUPPORTS 922 NS_DECL_NSIREQUEST 923 924 bool Canceled() { return mCanceled; } 925 926 private: 927 ~StopDetector() = default; 928 929 bool mCanceled = false; 930 }; 931 932 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest) 933 934 NS_IMETHODIMP 935 StopDetector::GetName(nsACString& aResult) { 936 aResult.AssignLiteral("about:stop-detector"); 937 return NS_OK; 938 } 939 940 NS_IMETHODIMP 941 StopDetector::IsPending(bool* aRetVal) { 942 *aRetVal = true; 943 return NS_OK; 944 } 945 946 NS_IMETHODIMP 947 StopDetector::GetStatus(nsresult* aStatus) { 948 *aStatus = NS_OK; 949 return NS_OK; 950 } 951 952 NS_IMETHODIMP StopDetector::SetCanceledReason(const nsACString& aReason) { 953 return SetCanceledReasonImpl(aReason); 954 } 955 956 NS_IMETHODIMP StopDetector::GetCanceledReason(nsACString& aReason) { 957 return GetCanceledReasonImpl(aReason); 958 } 959 960 NS_IMETHODIMP StopDetector::CancelWithReason(nsresult aStatus, 961 const nsACString& aReason) { 962 return CancelWithReasonImpl(aStatus, aReason); 963 } 964 965 NS_IMETHODIMP 966 StopDetector::Cancel(nsresult aStatus) { 967 mCanceled = true; 968 return NS_OK; 969 } 970 971 NS_IMETHODIMP 972 StopDetector::Suspend(void) { return NS_OK; } 973 NS_IMETHODIMP 974 StopDetector::Resume(void) { return NS_OK; } 975 976 NS_IMETHODIMP 977 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) { 978 *aLoadGroup = nullptr; 979 return NS_OK; 980 } 981 982 NS_IMETHODIMP 983 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; } 984 985 NS_IMETHODIMP 986 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) { 987 *aLoadFlags = nsIRequest::LOAD_NORMAL; 988 return NS_OK; 989 } 990 991 NS_IMETHODIMP 992 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 993 return GetTRRModeImpl(aTRRMode); 994 } 995 996 NS_IMETHODIMP 997 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 998 return SetTRRModeImpl(aTRRMode); 999 } 1000 1001 NS_IMETHODIMP 1002 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; } 1003 1004 bool nsDocShell::MaybeHandleSubframeHistory( 1005 nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) { 1006 // First, verify if this is a subframe. 1007 // Note, it is ok to rely on docshell here and not browsing context since when 1008 // an iframe is created, it has first in-process docshell. 1009 nsCOMPtr<nsIDocShellTreeItem> parentAsItem; 1010 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)); 1011 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem)); 1012 1013 if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) { 1014 if (mBrowsingContext && mBrowsingContext->IsTop() && 1015 !aLoadState->HistoryBehavior()) { 1016 // This is the root docshell. If we got here while 1017 // executing an onLoad Handler,this load will not go 1018 // into session history. 1019 // XXX Why is this code in a method which deals with iframes! 1020 if (aLoadState->IsFormSubmission()) { 1021 #ifdef DEBUG 1022 if (!mEODForCurrentDocument) { 1023 const MaybeDiscarded<BrowsingContext>& targetBC = 1024 aLoadState->TargetBrowsingContext(); 1025 MOZ_ASSERT_IF(GetBrowsingContext() == targetBC.get(), 1026 aLoadState->LoadType() == LOAD_NORMAL_REPLACE); 1027 } 1028 #endif 1029 } else { 1030 bool inOnLoadHandler = false; 1031 GetIsExecutingOnLoadHandler(&inOnLoadHandler); 1032 if (inOnLoadHandler) { 1033 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); 1034 } 1035 } 1036 } 1037 return false; 1038 } 1039 1040 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was 1041 * loaded through a history mechanism, then get the SH entry for the child 1042 * from the parent. This is done to restore frameset navigation while going 1043 * back/forward. If the parent was loaded through any other loadType, set the 1044 * child's loadType too accordingly, so that session history does not get 1045 * confused. 1046 */ 1047 1048 // Get the parent's load type 1049 uint32_t parentLoadType; 1050 parentDS->GetLoadType(&parentLoadType); 1051 1052 if (!aContinueHandlingSubframeHistory) { 1053 if (mozilla::SessionHistoryInParent()) { 1054 if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() && 1055 !GetCreatedDynamically()) { 1056 if (XRE_IsContentProcess()) { 1057 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); 1058 nsCOMPtr<nsILoadGroup> loadGroup; 1059 GetLoadGroup(getter_AddRefs(loadGroup)); 1060 if (contentChild && loadGroup && !mCheckingSessionHistory) { 1061 RefPtr<Document> parentDoc = parentDS->GetDocument(); 1062 parentDoc->BlockOnload(); 1063 RefPtr<BrowsingContext> browsingContext = mBrowsingContext; 1064 Maybe<uint64_t> currentLoadIdentifier = 1065 mBrowsingContext->GetCurrentLoadIdentifier(); 1066 RefPtr<nsDocShellLoadState> loadState = aLoadState; 1067 bool isNavigating = mIsNavigating; 1068 RefPtr<StopDetector> stopDetector = new StopDetector(); 1069 loadGroup->AddRequest(stopDetector, nullptr); 1070 // Need to set mCheckingSessionHistory so that 1071 // GetIsAttemptingToNavigate() returns true. 1072 mCheckingSessionHistory = true; 1073 1074 auto resolve = 1075 [currentLoadIdentifier, browsingContext, parentDoc, loadState, 1076 isNavigating, loadGroup, stopDetector]( 1077 mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) { 1078 RefPtr<nsDocShell> docShell = 1079 static_cast<nsDocShell*>(browsingContext->GetDocShell()); 1080 auto unblockParent = MakeScopeExit( 1081 [loadGroup, stopDetector, parentDoc, docShell]() { 1082 if (docShell) { 1083 docShell->mCheckingSessionHistory = false; 1084 } 1085 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK); 1086 parentDoc->UnblockOnload(false); 1087 }); 1088 1089 if (!docShell || !docShell->mCheckingSessionHistory) { 1090 return; 1091 } 1092 1093 if (stopDetector->Canceled()) { 1094 return; 1095 } 1096 if (currentLoadIdentifier == 1097 browsingContext->GetCurrentLoadIdentifier() && 1098 aResult.isSome()) { 1099 loadState->SetLoadingSessionHistoryInfo(aResult.value()); 1100 // This is an initial subframe load from the session 1101 // history, index doesn't need to be updated. 1102 loadState->SetLoadIsFromSessionHistory(0, false); 1103 } 1104 1105 // We got the results back from the parent process, call 1106 // LoadURI again with the possibly updated data. 1107 docShell->LoadURI(loadState, isNavigating, true); 1108 }; 1109 auto reject = [loadGroup, stopDetector, browsingContext, 1110 parentDoc](mozilla::ipc::ResponseRejectReason) { 1111 RefPtr<nsDocShell> docShell = 1112 static_cast<nsDocShell*>(browsingContext->GetDocShell()); 1113 if (docShell) { 1114 docShell->mCheckingSessionHistory = false; 1115 } 1116 // In practise reject shouldn't be called ever. 1117 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK); 1118 parentDoc->UnblockOnload(false); 1119 }; 1120 contentChild->SendGetLoadingSessionHistoryInfoFromParent( 1121 mBrowsingContext, std::move(resolve), std::move(reject)); 1122 return true; 1123 } 1124 } else { 1125 Maybe<LoadingSessionHistoryInfo> info; 1126 mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent( 1127 info); 1128 if (info.isSome()) { 1129 aLoadState->SetLoadingSessionHistoryInfo(info.value()); 1130 // This is an initial subframe load from the session 1131 // history, index doesn't need to be updated. 1132 aLoadState->SetLoadIsFromSessionHistory(0, false); 1133 } 1134 } 1135 } 1136 } else { 1137 // Get the ShEntry for the child from the parent 1138 nsCOMPtr<nsISHEntry> currentSH; 1139 bool oshe = false; 1140 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe); 1141 bool dynamicallyAddedChild = GetCreatedDynamically(); 1142 1143 if (!dynamicallyAddedChild && !oshe && currentSH) { 1144 // Only use the old SHEntry, if we're sure enough that 1145 // it wasn't originally for some other frame. 1146 nsCOMPtr<nsISHEntry> shEntry; 1147 currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild( 1148 mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry)); 1149 if (shEntry) { 1150 aLoadState->SetSHEntry(shEntry); 1151 } 1152 } 1153 } 1154 } 1155 1156 // Make some decisions on the child frame's loadType based on the 1157 // parent's loadType, if the subframe hasn't loaded anything into it. 1158 // 1159 // In some cases privileged scripts may try to get the DOMWindow 1160 // reference of this docshell before the loading starts, causing the 1161 // initial about:blank content viewer being created and mCurrentURI being 1162 // set. To handle this case we check if mCurrentURI is about:blank and 1163 // currentSHEntry is null. 1164 bool oshe = false; 1165 nsCOMPtr<nsISHEntry> currentChildEntry; 1166 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe); 1167 1168 if (mCurrentURI && 1169 (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry || mLoadingEntry || 1170 mActiveEntry) && 1171 !aLoadState->HistoryBehavior()) { 1172 // This is a pre-existing subframe. If 1173 // 1. The load of this frame was not originally initiated by session 1174 // history directly (i.e. (!shEntry) condition succeeded, but it can 1175 // still be a history load on parent which causes this frame being 1176 // loaded), which we checked with the above assert, and 1177 // 2. mCurrentURI is not null, nor the initial about:blank, 1178 // it is possible that a parent's onLoadHandler or even self's 1179 // onLoadHandler is loading a new page in this child. Check parent's and 1180 // self's busy flag and if it is set, we don't want this onLoadHandler 1181 // load to get in to session history. 1182 BusyFlags parentBusy = parentDS->GetBusyFlags(); 1183 BusyFlags selfBusy = GetBusyFlags(); 1184 1185 if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) { 1186 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); 1187 aLoadState->ClearLoadIsFromSessionHistory(); 1188 } 1189 return false; 1190 } 1191 1192 // This is a newly created frame. Check for exception cases first. 1193 // By default the subframe will inherit the parent's loadType. 1194 if (aLoadState->LoadIsFromSessionHistory() && 1195 (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) { 1196 // The parent was loaded normally. In this case, this *brand new* 1197 // child really shouldn't have a SHEntry. If it does, it could be 1198 // because the parent is replacing an existing frame with a new frame, 1199 // in the onLoadHandler. We don't want this url to get into session 1200 // history. Clear off shEntry, and set load type to 1201 // LOAD_BYPASS_HISTORY. 1202 bool inOnLoadHandler = false; 1203 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler); 1204 if (inOnLoadHandler) { 1205 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); 1206 aLoadState->ClearLoadIsFromSessionHistory(); 1207 } 1208 } else if (parentLoadType == LOAD_REFRESH) { 1209 // Clear shEntry. For refresh loads, we have to load 1210 // what comes through the pipe, not what's in history. 1211 aLoadState->ClearLoadIsFromSessionHistory(); 1212 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) || 1213 (aLoadState->LoadIsFromSessionHistory() && 1214 ((parentLoadType & LOAD_CMD_HISTORY) || 1215 (parentLoadType == LOAD_RELOAD_NORMAL) || 1216 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) || 1217 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) || 1218 (parentLoadType == 1219 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) { 1220 // If the parent url, bypassed history or was loaded from 1221 // history, pass on the parent's loadType to the new child 1222 // frame too, so that the child frame will also 1223 // avoid getting into history. 1224 aLoadState->SetLoadType(parentLoadType); 1225 } else if (parentLoadType == LOAD_ERROR_PAGE) { 1226 // If the parent document is an error page, we don't 1227 // want to update global/session history. However, 1228 // this child frame is not an error page. 1229 aLoadState->SetLoadType(LOAD_BYPASS_HISTORY); 1230 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) || 1231 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) || 1232 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) { 1233 // the new frame should inherit the parent's load type so that it also 1234 // bypasses the cache and/or proxy 1235 aLoadState->SetLoadType(parentLoadType); 1236 } 1237 1238 return false; 1239 } 1240 1241 /* 1242 * Reset state to a new content model within the current document and the 1243 * document viewer. Called by the document before initiating an out of band 1244 * document.write(). 1245 */ 1246 NS_IMETHODIMP 1247 nsDocShell::PrepareForNewContentModel() { 1248 // Clear out our form control state, because the state of controls 1249 // in the pre-open() document should not affect the state of 1250 // controls that are now going to be written. 1251 SetLayoutHistoryState(nullptr); 1252 mEODForCurrentDocument = false; 1253 return NS_OK; 1254 } 1255 1256 NS_IMETHODIMP 1257 nsDocShell::FirePageHideNotification(bool aIsUnload) { 1258 FirePageHideNotificationInternal(aIsUnload, false); 1259 return NS_OK; 1260 } 1261 1262 void nsDocShell::FirePageHideNotificationInternal( 1263 bool aIsUnload, bool aSkipCheckingDynEntries) { 1264 if (mDocumentViewer && !mFiredUnloadEvent) { 1265 // Keep an explicit reference since calling PageHide could release 1266 // mDocumentViewer 1267 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer); 1268 mFiredUnloadEvent = true; 1269 1270 if (mTiming) { 1271 mTiming->NotifyUnloadEventStart(); 1272 } 1273 1274 viewer->PageHide(aIsUnload); 1275 1276 if (mTiming) { 1277 mTiming->NotifyUnloadEventEnd(); 1278 } 1279 1280 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids; 1281 uint32_t n = mChildList.Length(); 1282 kids.SetCapacity(n); 1283 for (uint32_t i = 0; i < n; i++) { 1284 kids.AppendElement(do_QueryInterface(ChildAt(i))); 1285 } 1286 1287 n = kids.Length(); 1288 for (uint32_t i = 0; i < n; ++i) { 1289 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get()); 1290 if (child) { 1291 // Skip checking dynamic subframe entries in our children. 1292 child->FirePageHideNotificationInternal(aIsUnload, true); 1293 } 1294 } 1295 1296 // If the document is unloading, remove all dynamic subframe entries. 1297 if (aIsUnload && !aSkipCheckingDynEntries) { 1298 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 1299 if (rootSH) { 1300 MOZ_LOG( 1301 gSHLog, LogLevel::Debug, 1302 ("nsDocShell %p unloading, remove dynamic subframe entries", this)); 1303 if (mozilla::SessionHistoryInParent()) { 1304 if (mActiveEntry) { 1305 mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry(); 1306 } 1307 MOZ_LOG(gSHLog, LogLevel::Debug, 1308 ("nsDocShell %p unloading, no active entries", this)); 1309 } else if (mOSHE) { 1310 int32_t index = rootSH->Index(); 1311 rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE); 1312 } 1313 } 1314 } 1315 1316 // Now make sure our editor, if any, is detached before we go 1317 // any farther. 1318 DetachEditorFromWindow(); 1319 } 1320 } 1321 1322 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) { 1323 MOZ_ASSERT(mozilla::BFCacheInParent()); 1324 1325 if (!mScriptGlobal) { 1326 return; 1327 } 1328 1329 if (RefPtr<nsGlobalWindowInner> inner = 1330 nsGlobalWindowInner::Cast(mScriptGlobal->GetCurrentInnerWindow())) { 1331 if (aThaw) { 1332 inner->Thaw(false); 1333 } else { 1334 inner->Freeze(false); 1335 } 1336 } 1337 } 1338 1339 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) { 1340 MOZ_ASSERT(mozilla::BFCacheInParent()); 1341 1342 if (!mDocumentViewer) { 1343 return; 1344 } 1345 1346 // Emulate what non-SHIP BFCache does too. In pageshow case 1347 // add and remove a request and before that call SetCurrentURI to get 1348 // the location change notification. 1349 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire. 1350 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer); 1351 if (aShow) { 1352 viewer->SetIsHidden(false); 1353 mRefreshURIList = std::move(mBFCachedRefreshURIList); 1354 RefreshURIFromQueue(); 1355 mFiredUnloadEvent = false; 1356 RefPtr<Document> doc = viewer->GetDocument(); 1357 if (doc) { 1358 doc->NotifyActivityChanged(); 1359 nsCOMPtr<nsPIDOMWindowInner> inner = 1360 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr; 1361 if (mBrowsingContext->IsTop()) { 1362 doc->NotifyPossibleTitleChange(false); 1363 doc->SetLoadingOrRestoredFromBFCacheTimeStampToNow(); 1364 if (inner) { 1365 // Now that we have found the inner window of the page restored 1366 // from the history, we have to make sure that 1367 // performance.navigation.type is 2. 1368 // Traditionally this type change has been done to the top level page 1369 // only. 1370 Performance* performance = inner->GetPerformance(); 1371 if (performance) { 1372 performance->GetDOMTiming()->NotifyRestoreStart(); 1373 } 1374 } 1375 } 1376 1377 nsCOMPtr<nsIChannel> channel = doc->GetChannel(); 1378 if (channel) { 1379 SetLoadType(LOAD_HISTORY); 1380 mEODForCurrentDocument = false; 1381 mIsRestoringDocument = true; 1382 mLoadGroup->AddRequest(channel, nullptr); 1383 nsCOMPtr<nsIURI> uri; 1384 if (doc->FragmentDirective()) { 1385 // If we have fragment directives, then we've mutated the document 1386 // uri. Set the current URI from session history instead. 1387 if (mozilla::SessionHistoryInParent()) { 1388 uri = mActiveEntry ? mActiveEntry->GetURI() : nullptr; 1389 } else if (mOSHE) { 1390 uri = mOSHE->GetURI(); 1391 } 1392 } 1393 if (!uri) { 1394 uri = doc->GetDocumentURI(); 1395 } 1396 SetCurrentURI(uri, channel, 1397 /* aFireOnLocationChange */ true, 1398 /* aIsInitialAboutBlank */ false, 1399 /* aLocationFlags */ 0); 1400 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK); 1401 mIsRestoringDocument = false; 1402 } 1403 RefPtr<PresShell> presShell = GetPresShell(); 1404 if (presShell) { 1405 presShell->Thaw(false); 1406 } 1407 1408 if (inner) { 1409 inner->FireDelayedDOMEvents(false); 1410 } 1411 } 1412 } else if (!mFiredUnloadEvent) { 1413 // XXXBFCache check again that the page can enter bfcache. 1414 // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here? 1415 1416 if (mRefreshURIList) { 1417 RefreshURIToQueue(); 1418 mBFCachedRefreshURIList = std::move(mRefreshURIList); 1419 } else { 1420 // If Stop was called, the list was moved to mSavedRefreshURIList after 1421 // calling SuspendRefreshURIs, which calls RefreshURIToQueue. 1422 mBFCachedRefreshURIList = std::move(mSavedRefreshURIList); 1423 } 1424 1425 mFiredUnloadEvent = true; 1426 viewer->PageHide(false); 1427 1428 RefPtr<PresShell> presShell = GetPresShell(); 1429 if (presShell) { 1430 presShell->Freeze(false); 1431 } 1432 } 1433 } 1434 1435 nsresult nsDocShell::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) { 1436 nsCOMPtr<nsIRunnable> runnable(aRunnable); 1437 if (NS_WARN_IF(!GetWindow())) { 1438 // Window should only be unavailable after destroyed. 1439 MOZ_ASSERT(mIsBeingDestroyed); 1440 return NS_ERROR_FAILURE; 1441 } 1442 return SchedulerGroup::Dispatch(runnable.forget()); 1443 } 1444 1445 NS_IMETHODIMP 1446 nsDocShell::DispatchLocationChangeEvent() { 1447 return Dispatch(NewRunnableMethod("nsDocShell::FireDummyOnLocationChange", 1448 this, 1449 &nsDocShell::FireDummyOnLocationChange)); 1450 } 1451 1452 NS_IMETHODIMP 1453 nsDocShell::StartDelayedAutoplayMediaComponents() { 1454 RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow(); 1455 if (outerWindow) { 1456 outerWindow->ActivateMediaComponents(); 1457 } 1458 return NS_OK; 1459 } 1460 1461 bool nsDocShell::MaybeInitTiming() { 1462 if (mTiming && !mBlankTiming) { 1463 return false; 1464 } 1465 1466 bool canBeReset = false; 1467 1468 if (mScriptGlobal && mBlankTiming) { 1469 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow(); 1470 if (innerWin && innerWin->GetPerformance()) { 1471 mTiming = innerWin->GetPerformance()->GetDOMTiming(); 1472 mBlankTiming = false; 1473 } 1474 } 1475 1476 if (!mTiming) { 1477 mTiming = new nsDOMNavigationTiming(this); 1478 canBeReset = true; 1479 } 1480 1481 mTiming->NotifyNavigationStart( 1482 mBrowsingContext->IsActive() 1483 ? nsDOMNavigationTiming::DocShellState::eActive 1484 : nsDOMNavigationTiming::DocShellState::eInactive); 1485 1486 return canBeReset; 1487 } 1488 1489 void nsDocShell::MaybeResetInitTiming(bool aReset) { 1490 if (aReset) { 1491 mTiming = nullptr; 1492 } 1493 } 1494 1495 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const { 1496 return mTiming; 1497 } 1498 1499 nsPresContext* nsDocShell::GetEldestPresContext() { 1500 nsIDocumentViewer* viewer = mDocumentViewer; 1501 while (viewer) { 1502 nsIDocumentViewer* prevViewer = viewer->GetPreviousViewer(); 1503 if (!prevViewer) { 1504 return viewer->GetPresContext(); 1505 } 1506 viewer = prevViewer; 1507 } 1508 1509 return nullptr; 1510 } 1511 1512 nsPresContext* nsDocShell::GetPresContext() { 1513 if (!mDocumentViewer) { 1514 return nullptr; 1515 } 1516 1517 return mDocumentViewer->GetPresContext(); 1518 } 1519 1520 PresShell* nsDocShell::GetPresShell() { 1521 nsPresContext* presContext = GetPresContext(); 1522 return presContext ? presContext->GetPresShell() : nullptr; 1523 } 1524 1525 PresShell* nsDocShell::GetEldestPresShell() { 1526 nsPresContext* presContext = GetEldestPresContext(); 1527 1528 if (presContext) { 1529 return presContext->GetPresShell(); 1530 } 1531 1532 return nullptr; 1533 } 1534 1535 NS_IMETHODIMP 1536 nsDocShell::GetDocViewer(nsIDocumentViewer** aDocumentViewer) { 1537 NS_ENSURE_ARG_POINTER(aDocumentViewer); 1538 1539 *aDocumentViewer = mDocumentViewer; 1540 NS_IF_ADDREF(*aDocumentViewer); 1541 return NS_OK; 1542 } 1543 1544 NS_IMETHODIMP 1545 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) { 1546 *aWindowID = mContentWindowID; 1547 return NS_OK; 1548 } 1549 1550 NS_IMETHODIMP 1551 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) { 1552 mChromeEventHandler = aChromeEventHandler; 1553 1554 if (mScriptGlobal) { 1555 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler); 1556 } 1557 1558 return NS_OK; 1559 } 1560 1561 NS_IMETHODIMP 1562 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) { 1563 NS_ENSURE_ARG_POINTER(aChromeEventHandler); 1564 RefPtr<EventTarget> handler = mChromeEventHandler; 1565 handler.forget(aChromeEventHandler); 1566 return NS_OK; 1567 } 1568 1569 NS_IMETHODIMP 1570 nsDocShell::SetCurrentURIForSessionStore(nsIURI* aURI) { 1571 // Note that securityUI will set STATE_IS_INSECURE, even if 1572 // the scheme of |aURI| is "https". 1573 SetCurrentURI(aURI, nullptr, 1574 /* aFireOnLocationChange */ 1575 true, 1576 /* aIsInitialAboutBlank */ 1577 false, 1578 /* aLocationFlags */ 1579 nsIWebProgressListener::LOCATION_CHANGE_SESSION_STORE); 1580 return NS_OK; 1581 } 1582 1583 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest, 1584 bool aFireOnLocationChange, 1585 bool aIsInitialAboutBlank, 1586 uint32_t aLocationFlags) { 1587 MOZ_ASSERT(!mIsBeingDestroyed); 1588 1589 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, 1590 ("DOCSHELL %p SetCurrentURI %s\n", this, 1591 aURI ? aURI->GetSpecOrDefault().get() : "")); 1592 1593 // We don't want to send a location change when we're displaying an error 1594 // page, and we don't want to change our idea of "current URI" either 1595 if (mLoadType == LOAD_ERROR_PAGE) { 1596 return false; 1597 } 1598 1599 bool uriIsEqual = false; 1600 if (!mCurrentURI || !aURI || 1601 NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) { 1602 mTitleValidForCurrentURI = false; 1603 } 1604 1605 SetCurrentURIInternal(aURI); 1606 1607 #ifdef DEBUG 1608 mLastOpenedURI = aURI; 1609 #endif 1610 1611 if (!NS_IsAboutBlankAllowQueryAndFragment(mCurrentURI)) { 1612 mHasLoadedNonBlankURI = true; 1613 } 1614 1615 // Don't fire onLocationChange when creating a the initial about:blank 1616 // document, as this can happen when it's not safe for us to run script. 1617 // Note that if this initial about:blank isn't immediately navigated 1618 // away from, the onLocationChange will be fired as part of committing 1619 // to keeping the initial about:blank as the actual first initial 1620 // navigation destination. 1621 if (aIsInitialAboutBlank) { 1622 MOZ_ASSERT(!mHasLoadedNonBlankURI && !aRequest && aLocationFlags == 0); 1623 return false; 1624 } 1625 1626 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); 1627 1628 if (aFireOnLocationChange) { 1629 FireOnLocationChange(this, aRequest, aURI, aLocationFlags); 1630 } 1631 return !aFireOnLocationChange; 1632 } 1633 1634 void nsDocShell::SetCurrentURIInternal(nsIURI* aURI) { 1635 mCurrentURI = aURI; 1636 if (mBrowsingContext) { 1637 mBrowsingContext->ClearCachedValuesOfLocations(); 1638 } 1639 } 1640 1641 NS_IMETHODIMP 1642 nsDocShell::GetCharset(nsACString& aCharset) { 1643 aCharset.Truncate(); 1644 1645 PresShell* presShell = GetPresShell(); 1646 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); 1647 Document* doc = presShell->GetDocument(); 1648 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1649 doc->GetDocumentCharacterSet()->Name(aCharset); 1650 return NS_OK; 1651 } 1652 1653 NS_IMETHODIMP 1654 nsDocShell::ForceEncodingDetection() { 1655 nsCOMPtr<nsIDocumentViewer> viewer; 1656 GetDocViewer(getter_AddRefs(viewer)); 1657 if (!viewer) { 1658 return NS_OK; 1659 } 1660 1661 Document* doc = viewer->GetDocument(); 1662 if (!doc || doc->WillIgnoreCharsetOverride()) { 1663 return NS_OK; 1664 } 1665 1666 mForcedAutodetection = true; 1667 1668 nsIURI* uri = doc->GetOriginalURI(); 1669 bool isFileURL = uri && uri->SchemeIs("file"); 1670 1671 int32_t charsetSource = doc->GetDocumentCharacterSetSource(); 1672 auto encoding = doc->GetDocumentCharacterSet(); 1673 // AsHTMLDocument is valid, because we called 1674 // WillIgnoreCharsetOverride() above. 1675 if (doc->AsHTMLDocument()->IsPlainText()) { 1676 switch (charsetSource) { 1677 case kCharsetFromInitialAutoDetectionASCII: 1678 // Deliberately no final version 1679 LOGCHARSETMENU(("TEXT:UnlabeledAscii")); 1680 break; 1681 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic: 1682 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic: 1683 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII: 1684 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content: 1685 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content: 1686 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII: 1687 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8")); 1688 break; 1689 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD: 1690 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD: 1691 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII: 1692 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD")); 1693 break; 1694 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8: 1695 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII: 1696 LOGCHARSETMENU(("TEXT:UnlabeledUtf8")); 1697 break; 1698 case kCharsetFromChannel: 1699 if (encoding == UTF_8_ENCODING) { 1700 LOGCHARSETMENU(("TEXT:ChannelUtf8")); 1701 } else { 1702 LOGCHARSETMENU(("TEXT:ChannelNonUtf8")); 1703 } 1704 break; 1705 default: 1706 LOGCHARSETMENU(("TEXT:Bug")); 1707 break; 1708 } 1709 } else { 1710 switch (charsetSource) { 1711 case kCharsetFromInitialAutoDetectionASCII: 1712 // Deliberately no final version 1713 LOGCHARSETMENU(("HTML:UnlabeledAscii")); 1714 break; 1715 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic: 1716 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic: 1717 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII: 1718 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content: 1719 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content: 1720 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII: 1721 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8")); 1722 break; 1723 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD: 1724 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD: 1725 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII: 1726 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD")); 1727 break; 1728 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8: 1729 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII: 1730 LOGCHARSETMENU(("HTML:UnlabeledUtf8")); 1731 break; 1732 case kCharsetFromChannel: 1733 if (encoding == UTF_8_ENCODING) { 1734 LOGCHARSETMENU(("HTML:ChannelUtf8")); 1735 } else { 1736 LOGCHARSETMENU(("HTML:ChannelNonUtf8")); 1737 } 1738 break; 1739 case kCharsetFromXmlDeclaration: 1740 case kCharsetFromMetaTag: 1741 if (isFileURL) { 1742 LOGCHARSETMENU(("HTML:LocalLabeled")); 1743 } else if (encoding == UTF_8_ENCODING) { 1744 LOGCHARSETMENU(("HTML:MetaUtf8")); 1745 } else { 1746 LOGCHARSETMENU(("HTML:MetaNonUtf8")); 1747 } 1748 break; 1749 default: 1750 LOGCHARSETMENU(("HTML:Bug")); 1751 break; 1752 } 1753 } 1754 return NS_OK; 1755 } 1756 1757 void nsDocShell::SetParentCharset(const Encoding*& aCharset, 1758 int32_t aCharsetSource, 1759 nsIPrincipal* aPrincipal) { 1760 mParentCharset = aCharset; 1761 mParentCharsetSource = aCharsetSource; 1762 mParentCharsetPrincipal = aPrincipal; 1763 } 1764 1765 void nsDocShell::GetParentCharset(const Encoding*& aCharset, 1766 int32_t* aCharsetSource, 1767 nsIPrincipal** aPrincipal) { 1768 aCharset = mParentCharset; 1769 *aCharsetSource = mParentCharsetSource; 1770 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal); 1771 } 1772 1773 NS_IMETHODIMP 1774 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) { 1775 MOZ_ASSERT(aPromise); 1776 1777 ErrorResult rv; 1778 RefPtr<Document> doc(GetDocument()); 1779 RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv); 1780 if (NS_WARN_IF(rv.Failed())) { 1781 return rv.StealNSResult(); 1782 } 1783 1784 // Retrieve the document's content blocking events from the parent process. 1785 RefPtr<Document::GetContentBlockingEventsPromise> promise = 1786 doc->GetContentBlockingEvents(); 1787 if (promise) { 1788 promise->Then( 1789 GetCurrentSerialEventTarget(), __func__, 1790 [retPromise](const Document::GetContentBlockingEventsPromise:: 1791 ResolveOrRejectValue& aValue) { 1792 if (aValue.IsResolve()) { 1793 bool has = aValue.ResolveValue() & 1794 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT; 1795 retPromise->MaybeResolve(has); 1796 } else { 1797 retPromise->MaybeResolve(false); 1798 } 1799 }); 1800 } else { 1801 retPromise->MaybeResolve(false); 1802 } 1803 1804 retPromise.forget(aPromise); 1805 return NS_OK; 1806 } 1807 1808 NS_IMETHODIMP 1809 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) { 1810 MOZ_ASSERT(aEnabled); 1811 *aEnabled = mCSSErrorReportingEnabled; 1812 return NS_OK; 1813 } 1814 1815 NS_IMETHODIMP 1816 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) { 1817 mCSSErrorReportingEnabled = aEnabled; 1818 return NS_OK; 1819 } 1820 1821 NS_IMETHODIMP 1822 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) { 1823 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing); 1824 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing); 1825 } 1826 1827 void nsDocShell::NotifyPrivateBrowsingChanged() { 1828 MOZ_ASSERT(!mIsBeingDestroyed); 1829 1830 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers); 1831 while (iter.HasMore()) { 1832 nsWeakPtr ref = iter.GetNext(); 1833 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref); 1834 if (!obs) { 1835 iter.Remove(); 1836 } else { 1837 obs->PrivateModeChanged(UsePrivateBrowsing()); 1838 } 1839 } 1840 } 1841 1842 NS_IMETHODIMP 1843 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) { 1844 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing); 1845 } 1846 1847 NS_IMETHODIMP 1848 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) { 1849 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing); 1850 } 1851 1852 NS_IMETHODIMP 1853 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) { 1854 NS_ENSURE_ARG_POINTER(aResult); 1855 1856 *aResult = mHasLoadedNonBlankURI; 1857 return NS_OK; 1858 } 1859 1860 bool nsDocShell::HasStartedLoadingOtherThanInitialBlankURI() { 1861 return mHasStartedLoadingOtherThanInitialBlankURI; 1862 } 1863 1864 NS_IMETHODIMP 1865 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) { 1866 NS_ENSURE_ARG_POINTER(aUseRemoteTabs); 1867 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs); 1868 } 1869 1870 NS_IMETHODIMP 1871 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) { 1872 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs); 1873 } 1874 1875 NS_IMETHODIMP 1876 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) { 1877 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes); 1878 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes); 1879 } 1880 1881 NS_IMETHODIMP 1882 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) { 1883 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes); 1884 } 1885 1886 NS_IMETHODIMP 1887 nsDocShell::AddWeakPrivacyTransitionObserver( 1888 nsIPrivacyTransitionObserver* aObserver) { 1889 nsWeakPtr weakObs = do_GetWeakReference(aObserver); 1890 if (!weakObs) { 1891 return NS_ERROR_NOT_AVAILABLE; 1892 } 1893 mPrivacyObservers.AppendElement(weakObs); 1894 return NS_OK; 1895 } 1896 1897 NS_IMETHODIMP 1898 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) { 1899 nsWeakPtr weakObs = do_GetWeakReference(aObserver); 1900 if (!weakObs) { 1901 return NS_ERROR_FAILURE; 1902 } 1903 mReflowObservers.AppendElement(weakObs); 1904 return NS_OK; 1905 } 1906 1907 NS_IMETHODIMP 1908 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) { 1909 nsWeakPtr obs = do_GetWeakReference(aObserver); 1910 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; 1911 } 1912 1913 NS_IMETHODIMP 1914 nsDocShell::NotifyReflowObservers(bool aInterruptible, 1915 DOMHighResTimeStamp aStart, 1916 DOMHighResTimeStamp aEnd) { 1917 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers); 1918 while (iter.HasMore()) { 1919 nsWeakPtr ref = iter.GetNext(); 1920 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref); 1921 if (!obs) { 1922 iter.Remove(); 1923 } else if (aInterruptible) { 1924 obs->ReflowInterruptible(aStart, aEnd); 1925 } else { 1926 obs->Reflow(aStart, aEnd); 1927 } 1928 } 1929 return NS_OK; 1930 } 1931 1932 NS_IMETHODIMP 1933 nsDocShell::GetAllowMetaRedirects(bool* aReturn) { 1934 NS_ENSURE_ARG_POINTER(aReturn); 1935 1936 *aReturn = mAllowMetaRedirects; 1937 return NS_OK; 1938 } 1939 1940 NS_IMETHODIMP 1941 nsDocShell::SetAllowMetaRedirects(bool aValue) { 1942 mAllowMetaRedirects = aValue; 1943 return NS_OK; 1944 } 1945 1946 NS_IMETHODIMP 1947 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) { 1948 NS_ENSURE_ARG_POINTER(aAllowSubframes); 1949 1950 *aAllowSubframes = mAllowSubframes; 1951 return NS_OK; 1952 } 1953 1954 NS_IMETHODIMP 1955 nsDocShell::SetAllowSubframes(bool aAllowSubframes) { 1956 mAllowSubframes = aAllowSubframes; 1957 return NS_OK; 1958 } 1959 1960 NS_IMETHODIMP 1961 nsDocShell::GetAllowImages(bool* aAllowImages) { 1962 NS_ENSURE_ARG_POINTER(aAllowImages); 1963 1964 *aAllowImages = mAllowImages; 1965 return NS_OK; 1966 } 1967 1968 NS_IMETHODIMP 1969 nsDocShell::SetAllowImages(bool aAllowImages) { 1970 mAllowImages = aAllowImages; 1971 return NS_OK; 1972 } 1973 1974 NS_IMETHODIMP 1975 nsDocShell::GetAllowMedia(bool* aAllowMedia) { 1976 *aAllowMedia = mAllowMedia; 1977 return NS_OK; 1978 } 1979 1980 NS_IMETHODIMP 1981 nsDocShell::SetAllowMedia(bool aAllowMedia) { 1982 mAllowMedia = aAllowMedia; 1983 1984 // Mute or unmute audio contexts attached to the inner window. 1985 if (mScriptGlobal) { 1986 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) { 1987 if (aAllowMedia) { 1988 innerWin->UnmuteAudioContexts(); 1989 } else { 1990 innerWin->MuteAudioContexts(); 1991 } 1992 } 1993 } 1994 1995 return NS_OK; 1996 } 1997 1998 NS_IMETHODIMP 1999 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) { 2000 *aAllowDNSPrefetch = mAllowDNSPrefetch; 2001 return NS_OK; 2002 } 2003 2004 NS_IMETHODIMP 2005 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) { 2006 mAllowDNSPrefetch = aAllowDNSPrefetch; 2007 return NS_OK; 2008 } 2009 2010 NS_IMETHODIMP 2011 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) { 2012 *aAllowWindowControl = mAllowWindowControl; 2013 return NS_OK; 2014 } 2015 2016 NS_IMETHODIMP 2017 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) { 2018 mAllowWindowControl = aAllowWindowControl; 2019 return NS_OK; 2020 } 2021 2022 NS_IMETHODIMP 2023 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) { 2024 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting(); 2025 return NS_OK; 2026 } 2027 2028 NS_IMETHODIMP 2029 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) { 2030 BrowsingContext::Transaction txn; 2031 txn.SetAllowContentRetargeting(aAllowContentRetargeting); 2032 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting); 2033 return txn.Commit(mBrowsingContext); 2034 } 2035 2036 NS_IMETHODIMP 2037 nsDocShell::GetAllowContentRetargetingOnChildren( 2038 bool* aAllowContentRetargetingOnChildren) { 2039 *aAllowContentRetargetingOnChildren = 2040 mBrowsingContext->GetAllowContentRetargetingOnChildren(); 2041 return NS_OK; 2042 } 2043 2044 NS_IMETHODIMP 2045 nsDocShell::SetAllowContentRetargetingOnChildren( 2046 bool aAllowContentRetargetingOnChildren) { 2047 return mBrowsingContext->SetAllowContentRetargetingOnChildren( 2048 aAllowContentRetargetingOnChildren); 2049 } 2050 2051 NS_IMETHODIMP 2052 nsDocShell::GetMayEnableCharacterEncodingMenu( 2053 bool* aMayEnableCharacterEncodingMenu) { 2054 *aMayEnableCharacterEncodingMenu = false; 2055 if (!mDocumentViewer) { 2056 return NS_OK; 2057 } 2058 Document* doc = mDocumentViewer->GetDocument(); 2059 if (!doc) { 2060 return NS_OK; 2061 } 2062 if (doc->WillIgnoreCharsetOverride()) { 2063 return NS_OK; 2064 } 2065 2066 *aMayEnableCharacterEncodingMenu = true; 2067 return NS_OK; 2068 } 2069 2070 NS_IMETHODIMP 2071 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType, 2072 DocShellEnumeratorDirection aDirection, 2073 nsTArray<RefPtr<nsIDocShell>>& aResult) { 2074 aResult.Clear(); 2075 2076 nsDocShellEnumerator docShellEnum( 2077 (aDirection == ENUMERATE_FORWARDS) 2078 ? nsDocShellEnumerator::EnumerationDirection::Forwards 2079 : nsDocShellEnumerator::EnumerationDirection::Backwards, 2080 aItemType, *this); 2081 2082 nsresult rv = docShellEnum.BuildDocShellArray(aResult); 2083 if (NS_FAILED(rv)) { 2084 return rv; 2085 } 2086 2087 return NS_OK; 2088 } 2089 2090 NS_IMETHODIMP 2091 nsDocShell::GetAppType(AppType* aAppType) { 2092 *aAppType = mAppType; 2093 return NS_OK; 2094 } 2095 2096 NS_IMETHODIMP 2097 nsDocShell::SetAppType(AppType aAppType) { 2098 mAppType = aAppType; 2099 return NS_OK; 2100 } 2101 2102 NS_IMETHODIMP 2103 nsDocShell::GetAllowAuth(bool* aAllowAuth) { 2104 *aAllowAuth = mAllowAuth; 2105 return NS_OK; 2106 } 2107 2108 NS_IMETHODIMP 2109 nsDocShell::SetAllowAuth(bool aAllowAuth) { 2110 mAllowAuth = aAllowAuth; 2111 return NS_OK; 2112 } 2113 2114 NS_IMETHODIMP 2115 nsDocShell::GetZoom(float* aZoom) { 2116 NS_ENSURE_ARG_POINTER(aZoom); 2117 *aZoom = 1.0f; 2118 return NS_OK; 2119 } 2120 2121 NS_IMETHODIMP 2122 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; } 2123 2124 NS_IMETHODIMP 2125 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) { 2126 NS_ENSURE_ARG_POINTER(aBusyFlags); 2127 2128 *aBusyFlags = mBusyFlags; 2129 return NS_OK; 2130 } 2131 2132 NS_IMETHODIMP 2133 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) { 2134 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate(); 2135 delegate.forget(aLoadURIDelegate); 2136 return NS_OK; 2137 } 2138 2139 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() { 2140 if (nsCOMPtr<nsILoadURIDelegate> result = 2141 do_QueryActor("LoadURIDelegate", GetDocument())) { 2142 return result.forget(); 2143 } 2144 2145 return nullptr; 2146 } 2147 2148 NS_IMETHODIMP 2149 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) { 2150 *aUseErrorPages = mBrowsingContext->GetUseErrorPages(); 2151 return NS_OK; 2152 } 2153 2154 NS_IMETHODIMP 2155 nsDocShell::SetUseErrorPages(bool aUseErrorPages) { 2156 return mBrowsingContext->SetUseErrorPages(aUseErrorPages); 2157 } 2158 2159 NS_IMETHODIMP 2160 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) { 2161 *aPreviousEntryIndex = mPreviousEntryIndex; 2162 return NS_OK; 2163 } 2164 2165 NS_IMETHODIMP 2166 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) { 2167 *aLoadedEntryIndex = mLoadedEntryIndex; 2168 return NS_OK; 2169 } 2170 2171 NS_IMETHODIMP 2172 nsDocShell::HistoryPurged(int32_t aNumEntries) { 2173 // These indices are used for fastback cache eviction, to determine 2174 // which session history entries are candidates for content viewer 2175 // eviction. We need to adjust by the number of entries that we 2176 // just purged from history, so that we look at the right session history 2177 // entries during eviction. 2178 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries); 2179 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries); 2180 2181 for (auto* child : mChildList.ForwardRange()) { 2182 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child); 2183 if (shell) { 2184 shell->HistoryPurged(aNumEntries); 2185 } 2186 } 2187 2188 return NS_OK; 2189 } 2190 2191 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) { 2192 // These indices are used for fastback cache eviction, to determine 2193 // which session history entries are candidates for content viewer 2194 // eviction. We need to adjust by the number of entries that we 2195 // just purged from history, so that we look at the right session history 2196 // entries during eviction. 2197 if (aIndex == mPreviousEntryIndex) { 2198 mPreviousEntryIndex = -1; 2199 } else if (aIndex < mPreviousEntryIndex) { 2200 --mPreviousEntryIndex; 2201 } 2202 if (mLoadedEntryIndex == aIndex) { 2203 mLoadedEntryIndex = 0; 2204 } else if (aIndex < mLoadedEntryIndex) { 2205 --mLoadedEntryIndex; 2206 } 2207 2208 for (auto* child : mChildList.ForwardRange()) { 2209 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child); 2210 if (shell) { 2211 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex); 2212 } 2213 } 2214 2215 return NS_OK; 2216 } 2217 2218 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) { 2219 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds(); 2220 return NS_OK; 2221 } 2222 2223 NS_IMETHODIMP 2224 nsDocShell::SetWindowDraggingAllowed(bool aValue) { 2225 RefPtr<nsDocShell> parent; 2226 if (!aValue && mItemType == typeChrome && 2227 !(parent = GetInProcessParentDocshell())) { 2228 // Window dragging is always allowed for top level 2229 // chrome docshells. 2230 return NS_ERROR_FAILURE; 2231 } 2232 mWindowDraggingAllowed = aValue; 2233 return NS_OK; 2234 } 2235 2236 NS_IMETHODIMP 2237 nsDocShell::GetWindowDraggingAllowed(bool* aValue) { 2238 // window dragging regions in CSS (-moz-window-drag:drag) 2239 // can be slow. Default behavior is to only allow it for 2240 // chrome top level windows. 2241 RefPtr<nsDocShell> parent; 2242 if (mItemType == typeChrome && !(parent = GetInProcessParentDocshell())) { 2243 // Top level chrome window 2244 *aValue = true; 2245 } else { 2246 *aValue = mWindowDraggingAllowed; 2247 } 2248 return NS_OK; 2249 } 2250 2251 NS_IMETHODIMP 2252 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) { 2253 NS_IF_ADDREF(*aResult = GetCurrentDocChannel()); 2254 return NS_OK; 2255 } 2256 2257 nsIChannel* nsDocShell::GetCurrentDocChannel() { 2258 if (mDocumentViewer) { 2259 Document* doc = mDocumentViewer->GetDocument(); 2260 if (doc) { 2261 return doc->GetChannel(); 2262 } 2263 } 2264 return nullptr; 2265 } 2266 2267 NS_IMETHODIMP 2268 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) { 2269 nsWeakPtr weakObs = do_GetWeakReference(aObserver); 2270 if (!weakObs) { 2271 return NS_ERROR_FAILURE; 2272 } 2273 mScrollObservers.AppendElement(weakObs); 2274 return NS_OK; 2275 } 2276 2277 NS_IMETHODIMP 2278 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) { 2279 nsWeakPtr obs = do_GetWeakReference(aObserver); 2280 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE; 2281 } 2282 2283 void nsDocShell::NotifyAsyncPanZoomStarted() { 2284 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers); 2285 while (iter.HasMore()) { 2286 nsWeakPtr ref = iter.GetNext(); 2287 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref); 2288 if (obs) { 2289 obs->AsyncPanZoomStarted(); 2290 } else { 2291 iter.Remove(); 2292 } 2293 } 2294 } 2295 2296 void nsDocShell::NotifyAsyncPanZoomStopped() { 2297 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers); 2298 while (iter.HasMore()) { 2299 nsWeakPtr ref = iter.GetNext(); 2300 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref); 2301 if (obs) { 2302 obs->AsyncPanZoomStopped(); 2303 } else { 2304 iter.Remove(); 2305 } 2306 } 2307 } 2308 2309 NS_IMETHODIMP 2310 nsDocShell::NotifyScrollObservers() { 2311 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers); 2312 while (iter.HasMore()) { 2313 nsWeakPtr ref = iter.GetNext(); 2314 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref); 2315 if (obs) { 2316 obs->ScrollPositionChanged(); 2317 } else { 2318 iter.Remove(); 2319 } 2320 } 2321 return NS_OK; 2322 } 2323 2324 //***************************************************************************** 2325 // nsDocShell::nsIDocShellTreeItem 2326 //***************************************************************************** 2327 2328 NS_IMETHODIMP 2329 nsDocShell::GetName(nsAString& aName) { 2330 aName = mBrowsingContext->Name(); 2331 return NS_OK; 2332 } 2333 2334 NS_IMETHODIMP 2335 nsDocShell::SetName(const nsAString& aName) { 2336 return mBrowsingContext->SetName(aName); 2337 } 2338 2339 NS_IMETHODIMP 2340 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) { 2341 NS_ENSURE_ARG_POINTER(aResult); 2342 *aResult = mBrowsingContext->NameEquals(aName); 2343 return NS_OK; 2344 } 2345 2346 NS_IMETHODIMP 2347 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) { 2348 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent); 2349 return NS_OK; 2350 } 2351 2352 NS_IMETHODIMP 2353 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) { 2354 if (mWillChangeProcess) { 2355 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set"); 2356 return NS_ERROR_FAILURE; 2357 } 2358 2359 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent); 2360 } 2361 2362 NS_IMETHODIMP 2363 nsDocShell::ClearCachedPlatform() { 2364 nsCOMPtr<nsPIDOMWindowInner> win = 2365 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr; 2366 if (win) { 2367 Navigator* navigator = win->Navigator(); 2368 if (navigator) { 2369 navigator->ClearPlatformCache(); 2370 } 2371 } 2372 2373 return NS_OK; 2374 } 2375 2376 NS_IMETHODIMP 2377 nsDocShell::ClearCachedUserAgent() { 2378 nsCOMPtr<nsPIDOMWindowInner> win = 2379 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr; 2380 if (win) { 2381 Navigator* navigator = win->Navigator(); 2382 if (navigator) { 2383 navigator->ClearUserAgentCache(); 2384 } 2385 } 2386 2387 return NS_OK; 2388 } 2389 2390 /* virtual */ 2391 int32_t nsDocShell::ItemType() { return mItemType; } 2392 2393 NS_IMETHODIMP 2394 nsDocShell::GetItemType(int32_t* aItemType) { 2395 NS_ENSURE_ARG_POINTER(aItemType); 2396 2397 MOZ_DIAGNOSTIC_ASSERT( 2398 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType); 2399 *aItemType = mItemType; 2400 return NS_OK; 2401 } 2402 2403 NS_IMETHODIMP 2404 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) { 2405 if (!mParent) { 2406 *aParent = nullptr; 2407 } else { 2408 CallQueryInterface(mParent, aParent); 2409 } 2410 // Note that in the case when the parent is not an nsIDocShellTreeItem we 2411 // don't want to throw; we just want to return null. 2412 return NS_OK; 2413 } 2414 2415 // With Fission, related nsDocShell objects may exist in a different process. In 2416 // that case, this method will return `nullptr`, despite a parent nsDocShell 2417 // object existing. 2418 // 2419 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the 2420 // parent entry is not in the current process, and handle the case where the 2421 // parent nsDocShell is inaccessible. 2422 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() { 2423 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent)); 2424 return docshell.forget().downcast<nsDocShell>(); 2425 } 2426 2427 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) { 2428 MOZ_ASSERT(!mIsBeingDestroyed); 2429 2430 // If there is an existing document then there is no need to create 2431 // a client for a future initial about:blank document. 2432 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindow() && 2433 mScriptGlobal->GetCurrentInnerWindow()->GetExtantDoc()) { 2434 MOZ_DIAGNOSTIC_ASSERT( 2435 mScriptGlobal->GetCurrentInnerWindow()->GetClientInfo().isSome()); 2436 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource); 2437 return; 2438 } 2439 2440 // Don't recreate the initial client source. We call this multiple times 2441 // when DoChannelLoad() is called before CreateAboutBlankDocumentViewer. 2442 if (mInitialClientSource) { 2443 return; 2444 } 2445 2446 // Don't pre-allocate the client when we are sandboxed. The inherited 2447 // principal does not take sandboxing into account. 2448 // TODO: Refactor sandboxing principal code out so we can use it here. 2449 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) { 2450 return; 2451 } 2452 2453 // We cannot get inherited foreign partitioned principal here. Instead, we 2454 // directly check which principal we want to inherit for the service worker. 2455 nsIPrincipal* principal = 2456 aPrincipal 2457 ? aPrincipal 2458 : GetInheritedPrincipal( 2459 false, StoragePrincipalHelper:: 2460 ShouldUsePartitionPrincipalForServiceWorker(this)); 2461 2462 // Sometimes there is no principal available when we are called from 2463 // CreateAboutBlankDocumentViewer. For example, sometimes the principal 2464 // is only extracted from the load context after the document is created 2465 // in Document::ResetToURI(). Ideally we would do something similar 2466 // here, but for now lets just avoid the issue by not preallocating the 2467 // client. 2468 if (!principal) { 2469 return; 2470 } 2471 2472 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow(); 2473 if (!win) { 2474 return; 2475 } 2476 2477 mInitialClientSource = ClientManager::CreateSource( 2478 ClientType::Window, GetMainThreadSerialEventTarget(), principal); 2479 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource); 2480 2481 // Mark the initial client as execution ready, but owned by the docshell. 2482 // If the client is actually used this will cause ClientSource to force 2483 // the creation of the initial about:blank by calling 2484 // nsDocShell::GetDocument(). 2485 mInitialClientSource->DocShellExecutionReady(this); 2486 2487 // Next, check to see if the parent is controlled. 2488 MaybeInheritController(mInitialClientSource.get(), principal); 2489 } 2490 2491 void VerifyCientPrincipalInfosMatch(const mozilla::ipc::PrincipalInfo& aLeft, 2492 const mozilla::ipc::PrincipalInfo& aRight) { 2493 // Inheriting a controller when the principals don't match would cause a 2494 // crash. Let's do the checks earlier to crash here already instead of 2495 // ClientSource::SetController. And assert each condition separately. See bug 2496 // 1880012. 2497 MOZ_RELEASE_ASSERT(aLeft.type() == aRight.type()); 2498 2499 switch (aLeft.type()) { 2500 case mozilla::ipc::PrincipalInfo::TContentPrincipalInfo: { 2501 const mozilla::ipc::ContentPrincipalInfo& leftContent = 2502 aLeft.get_ContentPrincipalInfo(); 2503 const mozilla::ipc::ContentPrincipalInfo& rightContent = 2504 aRight.get_ContentPrincipalInfo(); 2505 MOZ_RELEASE_ASSERT(leftContent.attrs() == rightContent.attrs() && 2506 leftContent.originNoSuffix() == 2507 rightContent.originNoSuffix()); 2508 return; 2509 } 2510 case mozilla::ipc::PrincipalInfo::TNullPrincipalInfo: { 2511 // null principal never matches 2512 MOZ_RELEASE_ASSERT(false, "Clients have null principals"); 2513 return; 2514 } 2515 default: { 2516 break; 2517 } 2518 } 2519 } 2520 2521 void nsDocShell::MaybeInheritController( 2522 mozilla::dom::ClientSource* aClientSource, nsIPrincipal* aPrincipal) { 2523 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell(); 2524 if (!parent) { 2525 if (RefPtr<BrowsingContext> opener = mBrowsingContext->GetOpener()) { 2526 parent = opener->GetDocShell(); 2527 } 2528 } 2529 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr; 2530 nsPIDOMWindowInner* parentInner = 2531 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr; 2532 if (!parentInner) { 2533 return; 2534 } 2535 2536 nsCOMPtr<nsIURI> uri; 2537 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns)); 2538 2539 // We're done if there is no parent controller or if this docshell 2540 // is not permitted to control for some reason. 2541 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController()); 2542 if (controller.isNothing() || 2543 !ServiceWorkerAllowedToControlWindow(aPrincipal, uri)) { 2544 return; 2545 } 2546 2547 VerifyCientPrincipalInfosMatch(aClientSource->Info().PrincipalInfo(), 2548 controller->PrincipalInfo()); 2549 aClientSource->InheritController(controller.ref()); 2550 } 2551 2552 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const { 2553 if (mInitialClientSource) { 2554 Maybe<ClientInfo> result; 2555 result.emplace(mInitialClientSource->Info()); 2556 return result; 2557 } 2558 2559 nsPIDOMWindowInner* innerWindow = 2560 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr; 2561 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr; 2562 2563 if (!doc || !doc->IsUncommittedInitialDocument()) { 2564 // We won't reuse the inner window so let the channel determine one 2565 return Maybe<ClientInfo>(); 2566 } 2567 2568 return innerWindow->GetClientInfo(); 2569 } 2570 2571 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) { 2572 bool wasFrame = IsSubframe(); 2573 2574 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent); 2575 NS_ENSURE_SUCCESS(rv, rv); 2576 2577 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup); 2578 if (wasFrame != IsSubframe() && priorityGroup) { 2579 priorityGroup->AdjustPriority(wasFrame ? -1 : 1); 2580 } 2581 2582 // Curse ambiguous nsISupports inheritance! 2583 nsISupports* parent = GetAsSupports(aParent); 2584 2585 // If parent is another docshell, we inherit all their flags for 2586 // allowing plugins, scripting etc. 2587 bool value; 2588 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent)); 2589 2590 if (parentAsDocShell) { 2591 if (mAllowMetaRedirects && 2592 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) { 2593 SetAllowMetaRedirects(value); 2594 } 2595 if (mAllowSubframes && 2596 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) { 2597 SetAllowSubframes(value); 2598 } 2599 if (mAllowImages && 2600 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) { 2601 SetAllowImages(value); 2602 } 2603 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia); 2604 if (mAllowWindowControl && 2605 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) { 2606 SetAllowWindowControl(value); 2607 } 2608 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) { 2609 value = false; 2610 } 2611 SetAllowDNSPrefetch(mAllowDNSPrefetch && value); 2612 } 2613 2614 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent)); 2615 if (parentURIListener) { 2616 mContentListener->SetParentContentListener(parentURIListener); 2617 } 2618 2619 return NS_OK; 2620 } 2621 2622 void nsDocShell::MaybeRestoreWindowName() { 2623 if (!StaticPrefs::privacy_window_name_update_enabled()) { 2624 return; 2625 } 2626 2627 // We only restore window.name for the top-level content. 2628 if (!mBrowsingContext->IsTopContent()) { 2629 return; 2630 } 2631 2632 nsAutoString name; 2633 2634 // Following implements https://html.spec.whatwg.org/#history-traversal: 2635 // Step 4.4. Check if the loading entry has a name. 2636 2637 if (mLSHE) { 2638 mLSHE->GetName(name); 2639 } 2640 2641 if (mLoadingEntry) { 2642 name = mLoadingEntry->mInfo.GetName(); 2643 } 2644 2645 if (name.IsEmpty()) { 2646 return; 2647 } 2648 2649 // Step 4.4.1. Set the name to the browsing context. 2650 (void)mBrowsingContext->SetName(name); 2651 2652 // Step 4.4.2. Clear the name of all entries that are contiguous and 2653 // same-origin with the loading entry. 2654 if (mLSHE) { 2655 nsSHistory::WalkContiguousEntries( 2656 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); }); 2657 } 2658 2659 if (mLoadingEntry) { 2660 // Clear the name of the session entry in the child side. For parent side, 2661 // the clearing will be done when we commit the history to the parent. 2662 mLoadingEntry->mInfo.SetName(EmptyString()); 2663 } 2664 } 2665 2666 void nsDocShell::StoreWindowNameToSHEntries() { 2667 MOZ_ASSERT(mBrowsingContext->IsTopContent()); 2668 2669 nsAutoString name; 2670 mBrowsingContext->GetName(name); 2671 2672 if (mOSHE) { 2673 nsSHistory::WalkContiguousEntries( 2674 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); }); 2675 } 2676 2677 if (mozilla::SessionHistoryInParent()) { 2678 if (XRE_IsParentProcess()) { 2679 SessionHistoryEntry* entry = 2680 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 2681 if (entry) { 2682 nsSHistory::WalkContiguousEntries( 2683 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); }); 2684 } 2685 } else { 2686 // Ask parent process to store the name in entries. 2687 (void)ContentChild::GetSingleton() 2688 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries( 2689 mBrowsingContext, name); 2690 } 2691 } 2692 } 2693 2694 NS_IMETHODIMP 2695 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) { 2696 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) { 2697 *aParent = do_AddRef(parentBC->GetDocShell()).take(); 2698 } 2699 return NS_OK; 2700 } 2701 2702 NS_IMETHODIMP 2703 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) { 2704 NS_ENSURE_ARG_POINTER(aRootTreeItem); 2705 2706 RefPtr<nsDocShell> root = this; 2707 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell(); 2708 while (parent) { 2709 root = parent; 2710 parent = root->GetInProcessParentDocshell(); 2711 } 2712 2713 root.forget(aRootTreeItem); 2714 return NS_OK; 2715 } 2716 2717 NS_IMETHODIMP 2718 nsDocShell::GetInProcessSameTypeRootTreeItem( 2719 nsIDocShellTreeItem** aRootTreeItem) { 2720 NS_ENSURE_ARG_POINTER(aRootTreeItem); 2721 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this); 2722 2723 nsCOMPtr<nsIDocShellTreeItem> parent; 2724 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)), 2725 NS_ERROR_FAILURE); 2726 while (parent) { 2727 *aRootTreeItem = parent; 2728 NS_ENSURE_SUCCESS( 2729 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)), 2730 NS_ERROR_FAILURE); 2731 } 2732 NS_ADDREF(*aRootTreeItem); 2733 return NS_OK; 2734 } 2735 2736 NS_IMETHODIMP 2737 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) { 2738 NS_ENSURE_ARG_POINTER(aTreeOwner); 2739 2740 *aTreeOwner = mTreeOwner; 2741 NS_IF_ADDREF(*aTreeOwner); 2742 return NS_OK; 2743 } 2744 2745 NS_IMETHODIMP 2746 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) { 2747 if (mIsBeingDestroyed && aTreeOwner) { 2748 return NS_ERROR_FAILURE; 2749 } 2750 2751 // Don't automatically set the progress based on the tree owner for frames 2752 if (!IsSubframe()) { 2753 nsCOMPtr<nsIWebProgress> webProgress = 2754 do_QueryInterface(GetAsSupports(this)); 2755 2756 if (webProgress) { 2757 nsCOMPtr<nsIWebProgressListener> oldListener = 2758 do_QueryInterface(mTreeOwner); 2759 nsCOMPtr<nsIWebProgressListener> newListener = 2760 do_QueryInterface(aTreeOwner); 2761 2762 if (oldListener) { 2763 webProgress->RemoveProgressListener(oldListener); 2764 } 2765 2766 if (newListener) { 2767 webProgress->AddProgressListener(newListener, 2768 nsIWebProgress::NOTIFY_ALL); 2769 } 2770 } 2771 } 2772 2773 mTreeOwner = aTreeOwner; // Weak reference per API 2774 2775 for (auto* childDocLoader : mChildList.ForwardRange()) { 2776 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader); 2777 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); 2778 2779 if (child->ItemType() == mItemType) { 2780 child->SetTreeOwner(aTreeOwner); 2781 } 2782 } 2783 2784 // If we're in the content process and have had a TreeOwner set on us, extract 2785 // our BrowserChild actor. If we've already had our BrowserChild set, assert 2786 // that it hasn't changed. 2787 if (mTreeOwner && XRE_IsContentProcess()) { 2788 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner); 2789 MOZ_ASSERT(newBrowserChild, 2790 "No BrowserChild actor for tree owner in Content!"); 2791 2792 if (mBrowserChild) { 2793 nsCOMPtr<nsIBrowserChild> oldBrowserChild = 2794 do_QueryReferent(mBrowserChild); 2795 MOZ_RELEASE_ASSERT( 2796 oldBrowserChild == newBrowserChild, 2797 "Cannot change BrowserChild during nsDocShell lifetime!"); 2798 } else { 2799 mBrowserChild = do_GetWeakReference(newBrowserChild); 2800 } 2801 } 2802 2803 return NS_OK; 2804 } 2805 2806 NS_IMETHODIMP 2807 nsDocShell::GetHistoryID(nsID& aID) { 2808 aID = mBrowsingContext->GetHistoryID(); 2809 return NS_OK; 2810 } 2811 2812 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); } 2813 2814 NS_IMETHODIMP 2815 nsDocShell::GetIsInUnload(bool* aIsInUnload) { 2816 *aIsInUnload = mFiredUnloadEvent; 2817 return NS_OK; 2818 } 2819 2820 NS_IMETHODIMP 2821 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) { 2822 NS_ENSURE_ARG_POINTER(aChildCount); 2823 *aChildCount = mChildList.Length(); 2824 return NS_OK; 2825 } 2826 2827 NS_IMETHODIMP 2828 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) { 2829 NS_ENSURE_ARG_POINTER(aChild); 2830 2831 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild); 2832 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED); 2833 2834 // Make sure we're not creating a loop in the docshell tree 2835 nsDocLoader* ancestor = this; 2836 do { 2837 if (childAsDocLoader == ancestor) { 2838 return NS_ERROR_ILLEGAL_VALUE; 2839 } 2840 ancestor = ancestor->GetParent(); 2841 } while (ancestor); 2842 2843 // Make sure to remove the child from its current parent. 2844 nsDocLoader* childsParent = childAsDocLoader->GetParent(); 2845 if (childsParent) { 2846 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader); 2847 NS_ENSURE_SUCCESS(rv, rv); 2848 } 2849 2850 // Make sure to clear the treeowner in case this child is a different type 2851 // from us. 2852 aChild->SetTreeOwner(nullptr); 2853 2854 nsresult res = AddChildLoader(childAsDocLoader); 2855 NS_ENSURE_SUCCESS(res, res); 2856 NS_ASSERTION(!mChildList.IsEmpty(), 2857 "child list must not be empty after a successful add"); 2858 2859 /* Set the child's global history if the parent has one */ 2860 if (mBrowsingContext->GetUseGlobalHistory()) { 2861 // childDocShell->SetUseGlobalHistory(true); 2862 // this should be set through BC inherit 2863 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory()); 2864 } 2865 2866 if (aChild->ItemType() != mItemType) { 2867 return NS_OK; 2868 } 2869 2870 aChild->SetTreeOwner(mTreeOwner); 2871 2872 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild)); 2873 if (!childAsDocShell) { 2874 return NS_OK; 2875 } 2876 2877 // charset, style-disabling, and zoom will be inherited in SetupNewViewer() 2878 2879 // Now take this document's charset and set the child's parentCharset field 2880 // to it. We'll later use that field, in the loading process, for the 2881 // charset choosing algorithm. 2882 // If we fail, at any point, we just return NS_OK. 2883 // This code has some performance impact. But this will be reduced when 2884 // the current charset will finally be stored as an Atom, avoiding the 2885 // alias resolution extra look-up. 2886 2887 // we are NOT going to propagate the charset is this Chrome's docshell 2888 if (mItemType == nsIDocShellTreeItem::typeChrome) { 2889 return NS_OK; 2890 } 2891 2892 // get the parent's current charset 2893 if (!mDocumentViewer) { 2894 return NS_OK; 2895 } 2896 Document* doc = mDocumentViewer->GetDocument(); 2897 if (!doc) { 2898 return NS_OK; 2899 } 2900 2901 const Encoding* parentCS = doc->GetDocumentCharacterSet(); 2902 int32_t charsetSource = doc->GetDocumentCharacterSetSource(); 2903 // set the child's parentCharset 2904 childAsDocShell->SetParentCharset(parentCS, charsetSource, 2905 doc->NodePrincipal()); 2906 2907 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n", 2908 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType); 2909 2910 return NS_OK; 2911 } 2912 2913 NS_IMETHODIMP 2914 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) { 2915 NS_ENSURE_ARG_POINTER(aChild); 2916 2917 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild); 2918 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED); 2919 2920 nsresult rv = RemoveChildLoader(childAsDocLoader); 2921 NS_ENSURE_SUCCESS(rv, rv); 2922 2923 aChild->SetTreeOwner(nullptr); 2924 2925 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader); 2926 } 2927 2928 NS_IMETHODIMP 2929 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) { 2930 NS_ENSURE_ARG_POINTER(aChild); 2931 2932 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex); 2933 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED); 2934 2935 child.forget(aChild); 2936 2937 return NS_OK; 2938 } 2939 2940 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) { 2941 #ifdef DEBUG 2942 if (aIndex < 0) { 2943 NS_WARNING("Negative index passed to GetChildAt"); 2944 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) { 2945 NS_WARNING("Too large an index passed to GetChildAt"); 2946 } 2947 #endif 2948 2949 nsIDocumentLoader* child = ChildAt(aIndex); 2950 2951 // child may be nullptr here. 2952 return static_cast<nsDocShell*>(child); 2953 } 2954 2955 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef, 2956 nsISHEntry* aNewEntry, 2957 int32_t aChildOffset, uint32_t aLoadType, 2958 bool aCloneChildren) { 2959 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 2960 nsresult rv = NS_OK; 2961 2962 if (mLSHE && aLoadType != LOAD_PUSHSTATE) { 2963 /* You get here if you are currently building a 2964 * hierarchy ie.,you just visited a frameset page 2965 */ 2966 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) { 2967 rv = mLSHE->AddChild(aNewEntry, aChildOffset); 2968 } 2969 } else if (!aCloneRef) { 2970 /* This is an initial load in some subframe. Just append it if we can */ 2971 if (mOSHE) { 2972 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes()); 2973 } 2974 } else { 2975 RefPtr<ChildSHistory> shistory = GetRootSessionHistory(); 2976 if (shistory) { 2977 rv = shistory->LegacySHistory()->AddNestedSHEntry( 2978 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren); 2979 } 2980 } 2981 return rv; 2982 } 2983 2984 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry, 2985 int32_t aChildOffset, 2986 bool aCloneChildren) { 2987 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 2988 /* You will get here when you are in a subframe and 2989 * a new url has been loaded on you. 2990 * The mOSHE in this subframe will be the previous url's 2991 * mOSHE. This mOSHE will be used as the identification 2992 * for this subframe in the CloneAndReplace function. 2993 */ 2994 2995 // In this case, we will end up calling AddEntry, which increases the 2996 // current index by 1 2997 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 2998 if (rootSH) { 2999 mPreviousEntryIndex = rootSH->Index(); 3000 } 3001 3002 nsresult rv; 3003 // XXX(farre): this is not Fission safe, expect errors. This never 3004 // get's executed once session history in the parent is enabled. 3005 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv); 3006 NS_WARNING_ASSERTION( 3007 parent || !UseRemoteSubframes(), 3008 "Failed to add child session history entry! This will be resolved once " 3009 "session history in the parent is enabled."); 3010 if (parent) { 3011 rv = nsDocShell::Cast(parent)->AddChildSHEntry( 3012 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren); 3013 } 3014 3015 if (rootSH) { 3016 mLoadedEntryIndex = rootSH->Index(); 3017 3018 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) { 3019 MOZ_LOG(gPageCacheLog, LogLevel::Verbose, 3020 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex, 3021 mLoadedEntryIndex)); 3022 } 3023 } 3024 3025 return rv; 3026 } 3027 3028 NS_IMETHODIMP 3029 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) { 3030 *aOSHE = false; 3031 *aEntry = nullptr; 3032 if (mLSHE) { 3033 NS_ADDREF(*aEntry = mLSHE); 3034 } else if (mOSHE) { 3035 NS_ADDREF(*aEntry = mOSHE); 3036 *aOSHE = true; 3037 } 3038 return NS_OK; 3039 } 3040 3041 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() { 3042 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() && 3043 mBrowsingContext) { 3044 if (XRE_IsContentProcess()) { 3045 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); 3046 if (contentChild) { 3047 contentChild->SendSynchronizeLayoutHistoryState( 3048 mBrowsingContext, mActiveEntry->GetLayoutHistoryState()); 3049 } 3050 } else { 3051 SessionHistoryEntry* entry = 3052 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 3053 if (entry) { 3054 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState()); 3055 } 3056 } 3057 if (mLoadingEntry && 3058 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) { 3059 mLoadingEntry->mInfo.SetLayoutHistoryState( 3060 mActiveEntry->GetLayoutHistoryState()); 3061 } 3062 } 3063 3064 return NS_OK; 3065 } 3066 3067 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) { 3068 if (mLoadGroup) { 3069 mLoadGroup->SetDefaultLoadFlags(aLoadFlags); 3070 } else { 3071 NS_WARNING( 3072 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to " 3073 "propagate the mode to"); 3074 } 3075 } 3076 3077 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() { 3078 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr); 3079 return mScriptGlobal; 3080 } 3081 3082 Document* nsDocShell::GetDocument() { 3083 NS_ENSURE_TRUE(VerifyDocumentViewer(), nullptr); 3084 return mDocumentViewer->GetDocument(); 3085 } 3086 3087 Document* nsDocShell::GetExtantDocument() { 3088 return mDocumentViewer ? mDocumentViewer->GetDocument() : nullptr; 3089 } 3090 3091 nsPIDOMWindowOuter* nsDocShell::GetWindow() { 3092 if (NS_FAILED(EnsureScriptEnvironment())) { 3093 return nullptr; 3094 } 3095 return mScriptGlobal; 3096 } 3097 3098 NS_IMETHODIMP 3099 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) { 3100 NS_ENSURE_ARG_POINTER(aWindow); 3101 3102 nsresult rv = EnsureScriptEnvironment(); 3103 NS_ENSURE_SUCCESS(rv, rv); 3104 3105 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal; 3106 window.forget(aWindow); 3107 return NS_OK; 3108 } 3109 3110 NS_IMETHODIMP 3111 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) { 3112 RefPtr<ContentFrameMessageManager> mm; 3113 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) { 3114 mm = browserChild->GetMessageManager(); 3115 } else if (nsPIDOMWindowOuter* win = GetWindow()) { 3116 mm = win->GetMessageManager(); 3117 } 3118 mm.forget(aMessageManager); 3119 return NS_OK; 3120 } 3121 3122 NS_IMETHODIMP 3123 nsDocShell::GetIsNavigating(bool* aOut) { 3124 *aOut = mIsNavigating; 3125 return NS_OK; 3126 } 3127 3128 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) { 3129 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 3130 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3131 if (!rootSH || !aEntry) { 3132 return; 3133 } 3134 3135 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry); 3136 } 3137 3138 //------------------------------------- 3139 //-- Helper Method for Print discovery 3140 //------------------------------------- 3141 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) { 3142 if (!mBrowsingContext->Top()->GetIsPrinting()) { 3143 return false; 3144 } 3145 if (aDisplayErrorDialog) { 3146 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr); 3147 } 3148 return true; 3149 } 3150 3151 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog, 3152 bool aCheckIfUnloadFired) { 3153 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) && 3154 (!aCheckIfUnloadFired || !mFiredUnloadEvent); 3155 if (!isAllowed) { 3156 return false; 3157 } 3158 if (!mDocumentViewer) { 3159 return true; 3160 } 3161 bool firingBeforeUnload; 3162 mDocumentViewer->GetBeforeUnloadFiring(&firingBeforeUnload); 3163 return !firingBeforeUnload; 3164 } 3165 3166 //***************************************************************************** 3167 // nsDocShell::nsIWebNavigation 3168 //***************************************************************************** 3169 3170 NS_IMETHODIMP 3171 nsDocShell::GetCanGoBack(bool* aCanGoBack) { 3172 *aCanGoBack = false; 3173 if (!IsNavigationAllowed(false)) { 3174 return NS_OK; // JS may not handle returning of an error code 3175 } 3176 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3177 if (rootSH) { 3178 *aCanGoBack = rootSH->CanGo( 3179 -1, StaticPrefs::browser_navigation_requireUserInteraction()); 3180 MOZ_LOG(gSHLog, LogLevel::Verbose, 3181 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack)); 3182 3183 return NS_OK; 3184 } 3185 return NS_ERROR_FAILURE; 3186 } 3187 3188 NS_IMETHODIMP 3189 nsDocShell::GetCanGoBackIgnoringUserInteraction(bool* aCanGoBack) { 3190 *aCanGoBack = false; 3191 if (!IsNavigationAllowed(false)) { 3192 return NS_OK; // JS may not handle returning of an error code 3193 } 3194 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3195 if (rootSH) { 3196 *aCanGoBack = rootSH->CanGo(-1, false); 3197 MOZ_LOG(gSHLog, LogLevel::Verbose, 3198 ("nsDocShell %p CanGoBackIgnoringUserInteraction()->%d", this, 3199 *aCanGoBack)); 3200 3201 return NS_OK; 3202 } 3203 return NS_ERROR_FAILURE; 3204 } 3205 3206 NS_IMETHODIMP 3207 nsDocShell::GetCanGoForward(bool* aCanGoForward) { 3208 *aCanGoForward = false; 3209 if (!IsNavigationAllowed(false)) { 3210 return NS_OK; // JS may not handle returning of an error code 3211 } 3212 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3213 if (rootSH) { 3214 *aCanGoForward = rootSH->CanGo( 3215 1, StaticPrefs::browser_navigation_requireUserInteraction()); 3216 MOZ_LOG(gSHLog, LogLevel::Verbose, 3217 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward)); 3218 return NS_OK; 3219 } 3220 return NS_ERROR_FAILURE; 3221 } 3222 3223 NS_IMETHODIMP 3224 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) { 3225 if (!IsNavigationAllowed()) { 3226 return NS_OK; // JS may not handle returning of an error code 3227 } 3228 3229 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); 3230 mIsNavigating = true; 3231 3232 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3233 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE); 3234 ErrorResult rv; 3235 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv); 3236 return rv.StealNSResult(); 3237 } 3238 3239 NS_IMETHODIMP 3240 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) { 3241 if (!IsNavigationAllowed()) { 3242 return NS_OK; // JS may not handle returning of an error code 3243 } 3244 3245 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); 3246 mIsNavigating = true; 3247 3248 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3249 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE); 3250 ErrorResult rv; 3251 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv); 3252 return rv.StealNSResult(); 3253 } 3254 3255 // XXX(nika): We may want to stop exposing this API in the child process? Going 3256 // to a specific index from multiple different processes could definitely race. 3257 NS_IMETHODIMP 3258 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) { 3259 if (!IsNavigationAllowed()) { 3260 return NS_OK; // JS may not handle returning of an error code 3261 } 3262 3263 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; }); 3264 mIsNavigating = true; 3265 3266 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 3267 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE); 3268 3269 ErrorResult rv; 3270 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation, 3271 rv); 3272 return rv.StealNSResult(); 3273 } 3274 3275 nsresult nsDocShell::LoadURI(nsIURI* aURI, 3276 const LoadURIOptions& aLoadURIOptions) { 3277 if (!IsNavigationAllowed()) { 3278 return NS_OK; // JS may not handle returning of an error code 3279 } 3280 RefPtr<nsDocShellLoadState> loadState; 3281 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions( 3282 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState)); 3283 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI); 3284 if (NS_FAILED(rv) || !loadState) { 3285 return NS_ERROR_FAILURE; 3286 } 3287 3288 // Set the captive portal tab flag on the browsing context if requested 3289 if (loadState->GetIsCaptivePortalTab()) { 3290 (void)mBrowsingContext->SetIsCaptivePortalTab(true); 3291 } 3292 3293 return LoadURI(loadState, true); 3294 } 3295 3296 NS_IMETHODIMP 3297 nsDocShell::LoadURIFromScript(nsIURI* aURI, 3298 JS::Handle<JS::Value> aLoadURIOptions, 3299 JSContext* aCx) { 3300 // generate dictionary for aLoadURIOptions and forward call 3301 LoadURIOptions loadURIOptions; 3302 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) { 3303 return NS_ERROR_INVALID_ARG; 3304 } 3305 return LoadURI(aURI, loadURIOptions); 3306 } 3307 3308 nsresult nsDocShell::FixupAndLoadURIString( 3309 const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) { 3310 if (!IsNavigationAllowed()) { 3311 return NS_OK; // JS may not handle returning of an error code 3312 } 3313 3314 RefPtr<nsDocShellLoadState> loadState; 3315 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions( 3316 mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState)); 3317 3318 uint32_t loadFlags = aLoadURIOptions.mLoadFlags; 3319 if (NS_ERROR_MALFORMED_URI == rv) { 3320 MOZ_LOG(gSHLog, LogLevel::Debug, 3321 ("Creating an active entry on nsDocShell %p to %s (because " 3322 "we're showing an error page)", 3323 this, NS_ConvertUTF16toUTF8(aURIString).get())); 3324 3325 // We need to store a session history entry. We don't have a valid URI, so 3326 // we use about:blank instead. 3327 nsCOMPtr<nsIURI> uri; 3328 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns)); 3329 nsCOMPtr<nsIPrincipal> triggeringPrincipal; 3330 if (aLoadURIOptions.mTriggeringPrincipal) { 3331 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal; 3332 } else { 3333 triggeringPrincipal = nsContentUtils::GetSystemPrincipal(); 3334 } 3335 if (mozilla::SessionHistoryInParent()) { 3336 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release()); 3337 mActiveEntry = MakeUnique<SessionHistoryInfo>( 3338 uri, triggeringPrincipal, nullptr, nullptr, nullptr, 3339 nsLiteralCString("text/html")); 3340 mBrowsingContext->SetActiveSessionHistoryEntry( 3341 Nothing(), mActiveEntry.get(), previousActiveEntry.get(), 3342 MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags), 3343 /* aUpdatedCacheKey = */ 0); 3344 } 3345 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(), 3346 nullptr) && 3347 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) { 3348 return NS_ERROR_LOAD_SHOWED_ERRORPAGE; 3349 } 3350 } 3351 3352 if (NS_FAILED(rv) || !loadState) { 3353 return NS_ERROR_FAILURE; 3354 } 3355 3356 // Set the captive portal tab flag on the browsing context if requested 3357 if (loadState->GetIsCaptivePortalTab()) { 3358 (void)mBrowsingContext->SetIsCaptivePortalTab(true); 3359 } 3360 3361 return LoadURI(loadState, true); 3362 } 3363 3364 NS_IMETHODIMP 3365 nsDocShell::FixupAndLoadURIStringFromScript( 3366 const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions, 3367 JSContext* aCx) { 3368 // generate dictionary for aLoadURIOptions and forward call 3369 LoadURIOptions loadURIOptions; 3370 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) { 3371 return NS_ERROR_INVALID_ARG; 3372 } 3373 return FixupAndLoadURIString(aURIString, loadURIOptions); 3374 } 3375 3376 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) { 3377 // If we're not in a content frame, or are at a BrowsingContext tree boundary, 3378 // such as the content-chrome boundary, don't fire the error event. 3379 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) { 3380 return; 3381 } 3382 3383 // If embedder is same-process, then unblocking the load event is already 3384 // handled by nsDocLoader. Fire the error event on our embedder element if 3385 // requested. 3386 // 3387 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act 3388 // more like the remote case when in-process. 3389 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement(); 3390 if (element) { 3391 if (aFireFrameErrorEvent) { 3392 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) { 3393 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) { 3394 fl->FireErrorEvent(); 3395 } 3396 } 3397 } 3398 return; 3399 } 3400 3401 // If we have a cross-process parent document, we must notify it that we no 3402 // longer block its load event. This is necessary for OOP sub-documents 3403 // because error documents do not result in a call to 3404 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths. 3405 // (Obviously, we must do this before any of the returns below.) 3406 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this); 3407 if (browserChild && 3408 !mBrowsingContext->GetParentWindowContext()->IsInProcess()) { 3409 (void)browserChild->SendMaybeFireEmbedderLoadEvents( 3410 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent 3411 : EmbedderElementEventType::NoEvent); 3412 } 3413 } 3414 3415 NS_IMETHODIMP 3416 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, 3417 const char16_t* aURL, nsIChannel* aFailedChannel, 3418 bool* aDisplayedErrorPage) { 3419 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, 3420 ("DOCSHELL %p DisplayLoadError %s\n", this, 3421 aURI ? aURI->GetSpecOrDefault().get() : "")); 3422 3423 *aDisplayedErrorPage = false; 3424 // Get prompt and string bundle services 3425 nsCOMPtr<nsIPrompt> prompter; 3426 nsCOMPtr<nsIStringBundle> stringBundle; 3427 GetPromptAndStringBundle(getter_AddRefs(prompter), 3428 getter_AddRefs(stringBundle)); 3429 3430 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE); 3431 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE); 3432 3433 const char* error = nullptr; 3434 // The key used to select the appropriate error message from the properties 3435 // file. 3436 const char* errorDescriptionID = nullptr; 3437 AutoTArray<nsString, 3> formatStrs; 3438 bool addHostPort = false; 3439 bool isBadStsCertError = false; 3440 nsresult rv = NS_OK; 3441 nsAutoString messageStr; 3442 nsAutoCString cssClass; 3443 nsAutoCString errorPage; 3444 3445 errorPage.AssignLiteral("neterror"); 3446 3447 // Turn the error code into a human readable error message. 3448 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) { 3449 NS_ENSURE_ARG_POINTER(aURI); 3450 3451 // Extract the schemes into a comma delimited list. 3452 nsAutoCString scheme; 3453 aURI->GetScheme(scheme); 3454 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement()); 3455 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI); 3456 while (nestedURI) { 3457 nsCOMPtr<nsIURI> tempURI; 3458 nsresult rv2; 3459 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI)); 3460 if (NS_SUCCEEDED(rv2) && tempURI) { 3461 tempURI->GetScheme(scheme); 3462 formatStrs[0].AppendLiteral(", "); 3463 AppendASCIItoUTF16(scheme, formatStrs[0]); 3464 } 3465 nestedURI = do_QueryInterface(tempURI); 3466 } 3467 error = "unknownProtocolFound"; 3468 } else if (NS_ERROR_NET_EMPTY_RESPONSE == aError) { 3469 NS_ENSURE_ARG_POINTER(aURI); 3470 error = "httpErrorPage"; 3471 } else if (NS_ERROR_NET_ERROR_RESPONSE == aError) { 3472 NS_ENSURE_ARG_POINTER(aURI); 3473 error = "serverError"; 3474 } else if (NS_ERROR_FILE_NOT_FOUND == aError) { 3475 NS_ENSURE_ARG_POINTER(aURI); 3476 error = "fileNotFound"; 3477 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) { 3478 NS_ENSURE_ARG_POINTER(aURI); 3479 error = "fileAccessDenied"; 3480 } else if (NS_ERROR_UNKNOWN_HOST == aError) { 3481 NS_ENSURE_ARG_POINTER(aURI); 3482 // Get the host 3483 nsAutoCString host; 3484 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI); 3485 innermostURI->GetHost(host); 3486 CopyUTF8toUTF16(host, *formatStrs.AppendElement()); 3487 errorDescriptionID = "dnsNotFound2"; 3488 error = "dnsNotFound"; 3489 } else if (NS_ERROR_CONNECTION_REFUSED == aError || 3490 NS_ERROR_PROXY_BAD_GATEWAY == aError) { 3491 NS_ENSURE_ARG_POINTER(aURI); 3492 addHostPort = true; 3493 error = "connectionFailure"; 3494 } else if (NS_ERROR_NET_INTERRUPT == aError) { 3495 NS_ENSURE_ARG_POINTER(aURI); 3496 addHostPort = true; 3497 error = "netInterrupt"; 3498 } else if (NS_ERROR_NET_TIMEOUT == aError || 3499 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError || 3500 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) { 3501 NS_ENSURE_ARG_POINTER(aURI); 3502 // Get the host 3503 nsAutoCString host; 3504 aURI->GetHost(host); 3505 CopyUTF8toUTF16(host, *formatStrs.AppendElement()); 3506 error = "netTimeout"; 3507 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError || 3508 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) { 3509 // CSP error 3510 cssClass.AssignLiteral("neterror"); 3511 error = "cspBlocked"; 3512 } else if (NS_ERROR_XFO_VIOLATION == aError) { 3513 // XFO error 3514 cssClass.AssignLiteral("neterror"); 3515 error = "xfoBlocked"; 3516 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) { 3517 nsCOMPtr<nsINSSErrorsService> nsserr = 3518 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID); 3519 3520 uint32_t errorClass; 3521 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) { 3522 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL; 3523 } 3524 3525 nsCOMPtr<nsITransportSecurityInfo> tsi; 3526 if (aFailedChannel) { 3527 aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi)); 3528 } 3529 if (tsi) { 3530 uint32_t securityState; 3531 tsi->GetSecurityState(&securityState); 3532 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) { 3533 error = "sslv3Used"; 3534 addHostPort = true; 3535 } else if (securityState & 3536 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) { 3537 error = "weakCryptoUsed"; 3538 addHostPort = true; 3539 } 3540 } else { 3541 // No channel, let's obtain the generic error message 3542 if (nsserr) { 3543 nsserr->GetErrorMessage(aError, messageStr); 3544 } 3545 } 3546 // We don't have a message string here anymore but DisplayLoadError 3547 // requires a non-empty messageStr. 3548 messageStr.Truncate(); 3549 messageStr.AssignLiteral(u" "); 3550 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) { 3551 error = "nssBadCert"; 3552 3553 // If this is an HTTP Strict Transport Security host or a pinned host 3554 // and the certificate is bad, don't allow overrides (RFC 6797 section 3555 // 12.1). 3556 bool isStsHost = false; 3557 bool isPinnedHost = false; 3558 OriginAttributes attrsForHSTS; 3559 if (aFailedChannel) { 3560 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel, 3561 attrsForHSTS); 3562 } else { 3563 attrsForHSTS = GetOriginAttributes(); 3564 } 3565 3566 if (XRE_IsParentProcess()) { 3567 nsCOMPtr<nsISiteSecurityService> sss = 3568 do_GetService(NS_SSSERVICE_CONTRACTID, &rv); 3569 NS_ENSURE_SUCCESS(rv, rv); 3570 rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost); 3571 NS_ENSURE_SUCCESS(rv, rv); 3572 } else { 3573 mozilla::dom::ContentChild* cc = 3574 mozilla::dom::ContentChild::GetSingleton(); 3575 cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost); 3576 } 3577 nsCOMPtr<nsIPublicKeyPinningService> pkps = 3578 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv); 3579 NS_ENSURE_SUCCESS(rv, rv); 3580 rv = pkps->HostHasPins(aURI, &isPinnedHost); 3581 3582 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert", 3583 false)) { 3584 cssClass.AssignLiteral("expertBadCert"); 3585 } 3586 3587 // HSTS/pinning takes precedence over the expert bad cert pref. We 3588 // never want to show the "Add Exception" button for these sites. 3589 // In the future we should differentiate between an HSTS host and a 3590 // pinned host and display a more informative message to the user. 3591 if (isStsHost || isPinnedHost) { 3592 isBadStsCertError = true; 3593 cssClass.AssignLiteral("badStsCert"); 3594 } 3595 3596 errorPage.Assign("certerror"); 3597 } else { 3598 error = "nssFailure2"; 3599 } 3600 } else if (NS_ERROR_PHISHING_URI == aError || 3601 NS_ERROR_MALWARE_URI == aError || 3602 NS_ERROR_UNWANTED_URI == aError || 3603 NS_ERROR_HARMFULADDON_URI == aError || 3604 NS_ERROR_HARMFUL_URI == aError) { 3605 nsAutoCString host; 3606 aURI->GetHost(host); 3607 CopyUTF8toUTF16(host, *formatStrs.AppendElement()); 3608 3609 // Malware and phishing detectors may want to use an alternate error 3610 // page, but if the pref's not set, we'll fall back on the standard page 3611 nsAutoCString alternateErrorPage; 3612 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page", 3613 alternateErrorPage); 3614 if (NS_SUCCEEDED(rv)) { 3615 errorPage.Assign(alternateErrorPage); 3616 } 3617 3618 if (NS_ERROR_PHISHING_URI == aError) { 3619 error = "deceptiveBlocked"; 3620 } else if (NS_ERROR_MALWARE_URI == aError) { 3621 error = "malwareBlocked"; 3622 } else if (NS_ERROR_UNWANTED_URI == aError) { 3623 error = "unwantedBlocked"; 3624 } else if (NS_ERROR_HARMFUL_URI == aError) { 3625 error = "harmfulBlocked"; 3626 } else if (NS_ERROR_HARMFULADDON_URI == aError) { 3627 error = "addonBlocked"; 3628 } 3629 3630 cssClass.AssignLiteral("blacklist"); 3631 } else if (NS_ERROR_CONTENT_CRASHED == aError) { 3632 errorPage.AssignLiteral("tabcrashed"); 3633 error = "tabcrashed"; 3634 3635 RefPtr<EventTarget> handler = mChromeEventHandler; 3636 if (handler) { 3637 nsCOMPtr<Element> element = do_QueryInterface(handler); 3638 element->GetAttribute(u"crashedPageTitle"_ns, messageStr); 3639 } 3640 3641 // DisplayLoadError requires a non-empty messageStr to proceed and call 3642 // LoadErrorPage. If the page doesn't have a title, we will use a blank 3643 // space which will be trimmed and thus treated as empty by the front-end. 3644 if (messageStr.IsEmpty()) { 3645 messageStr.AssignLiteral(u" "); 3646 } 3647 } else if (NS_ERROR_FRAME_CRASHED == aError) { 3648 errorPage.AssignLiteral("framecrashed"); 3649 error = "framecrashed"; 3650 messageStr.AssignLiteral(u" "); 3651 } else if (NS_ERROR_BUILDID_MISMATCH == aError) { 3652 errorPage.AssignLiteral("restartrequired"); 3653 error = "restartrequired"; 3654 3655 // DisplayLoadError requires a non-empty messageStr to proceed and call 3656 // LoadErrorPage. If the page doesn't have a title, we will use a blank 3657 // space which will be trimmed and thus treated as empty by the front-end. 3658 if (messageStr.IsEmpty()) { 3659 messageStr.AssignLiteral(u" "); 3660 } 3661 } else if (aError == NS_ERROR_RESTRICTED_CONTENT) { 3662 errorPage.AssignLiteral("restricted"); 3663 error = "restrictedcontent"; 3664 if (messageStr.IsEmpty()) { 3665 messageStr.AssignLiteral(u" "); 3666 } 3667 } else { 3668 // Errors requiring simple formatting 3669 bool isOnionAuthError = false; 3670 bool isOnionError = false; 3671 switch (aError) { 3672 case NS_ERROR_MALFORMED_URI: 3673 // URI is malformed 3674 error = "malformedURI"; 3675 errorDescriptionID = "malformedURI2"; 3676 break; 3677 case NS_ERROR_REDIRECT_LOOP: 3678 // Doc failed to load because the server generated too many redirects 3679 error = "redirectLoop"; 3680 break; 3681 case NS_ERROR_UNKNOWN_SOCKET_TYPE: 3682 // Doc failed to load because PSM is not installed 3683 error = "unknownSocketType"; 3684 break; 3685 case NS_ERROR_NET_RESET: 3686 // Doc failed to load because the server kept reseting the connection 3687 // before we could read any data from it 3688 error = "netReset"; 3689 break; 3690 case NS_ERROR_DOCUMENT_NOT_CACHED: 3691 // Doc failed to load because the cache does not contain a copy of 3692 // the document. 3693 error = "notCached"; 3694 break; 3695 case NS_ERROR_OFFLINE: 3696 // Doc failed to load because we are offline. 3697 error = "netOffline"; 3698 break; 3699 case NS_ERROR_DOCUMENT_IS_PRINTMODE: 3700 // Doc navigation attempted while Printing or Print Preview 3701 error = "isprinting"; 3702 break; 3703 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED: 3704 // Port blocked for security reasons 3705 addHostPort = true; 3706 error = "deniedPortAccess"; 3707 break; 3708 case NS_ERROR_UNKNOWN_PROXY_HOST: 3709 // Proxy hostname could not be resolved. 3710 error = "proxyResolveFailure"; 3711 break; 3712 case NS_ERROR_PROXY_CONNECTION_REFUSED: 3713 case NS_ERROR_PROXY_FORBIDDEN: 3714 case NS_ERROR_PROXY_NOT_IMPLEMENTED: 3715 case NS_ERROR_PROXY_AUTHENTICATION_FAILED: 3716 case NS_ERROR_PROXY_TOO_MANY_REQUESTS: 3717 // Proxy connection was refused. 3718 error = "proxyConnectFailure"; 3719 break; 3720 case NS_ERROR_INVALID_CONTENT_ENCODING: 3721 // Bad Content Encoding. 3722 error = "contentEncodingError"; 3723 break; 3724 case NS_ERROR_UNSAFE_CONTENT_TYPE: 3725 // Channel refused to load from an unrecognized content type. 3726 error = "unsafeContentType"; 3727 break; 3728 case NS_ERROR_CORRUPTED_CONTENT: 3729 // Broken Content Detected. e.g. Content-MD5 check failure. 3730 error = "corruptedContentErrorv2"; 3731 break; 3732 case NS_ERROR_INTERCEPTION_FAILED: 3733 // ServiceWorker intercepted request, but something went wrong. 3734 error = "corruptedContentErrorv2"; 3735 break; 3736 case NS_ERROR_NET_INADEQUATE_SECURITY: 3737 // Server negotiated bad TLS for HTTP/2. 3738 error = "inadequateSecurityError"; 3739 addHostPort = true; 3740 break; 3741 case NS_ERROR_BLOCKED_BY_POLICY: 3742 // Page blocked by policy 3743 error = "blockedByPolicy"; 3744 break; 3745 case NS_ERROR_DOM_COOP_FAILED: 3746 error = "blockedByCOOP"; 3747 errorDescriptionID = "blockedByCORP"; 3748 break; 3749 case NS_ERROR_DOM_COEP_FAILED: 3750 error = "blockedByCOEP"; 3751 errorDescriptionID = "blockedByCORP"; 3752 break; 3753 case NS_ERROR_DOM_INVALID_HEADER_VALUE: 3754 error = "invalidHeaderValue"; 3755 break; 3756 case NS_ERROR_NET_HTTP2_SENT_GOAWAY: 3757 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR: 3758 // HTTP/2 or HTTP/3 stack detected a protocol error 3759 error = "networkProtocolError"; 3760 break; 3761 case NS_ERROR_BASIC_HTTP_AUTH_DISABLED: 3762 error = "basicHttpAuthDisabled"; 3763 break; 3764 case NS_ERROR_TOR_ONION_SVC_NOT_FOUND: 3765 error = "onionServices.descNotFound"; 3766 isOnionError = true; 3767 break; 3768 case NS_ERROR_TOR_ONION_SVC_IS_INVALID: 3769 error = "onionServices.descInvalid"; 3770 isOnionError = true; 3771 break; 3772 case NS_ERROR_TOR_ONION_SVC_INTRO_FAILED: 3773 error = "onionServices.introFailed"; 3774 isOnionError = true; 3775 break; 3776 case NS_ERROR_TOR_ONION_SVC_REND_FAILED: 3777 error = "onionServices.rendezvousFailed"; 3778 isOnionError = true; 3779 break; 3780 case NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH: 3781 error = "onionServices.clientAuthMissing"; 3782 isOnionError = true; 3783 isOnionAuthError = true; 3784 break; 3785 case NS_ERROR_TOR_ONION_SVC_BAD_CLIENT_AUTH: 3786 error = "onionServices.clientAuthIncorrect"; 3787 isOnionError = true; 3788 isOnionAuthError = true; 3789 break; 3790 case NS_ERROR_TOR_ONION_SVC_BAD_ADDRESS: 3791 error = "onionServices.badAddress"; 3792 isOnionError = true; 3793 break; 3794 case NS_ERROR_TOR_ONION_SVC_INTRO_TIMEDOUT: 3795 error = "onionServices.introTimedOut"; 3796 isOnionError = true; 3797 break; 3798 default: 3799 break; 3800 } 3801 3802 // The presence of aFailedChannel indicates that we arrived here due to a 3803 // failed connection attempt. Note that we will arrive here a second time 3804 // if the user cancels the Tor client auth prompt, but in that case we 3805 // will not have a failed channel and therefore we will not prompt again. 3806 if (isOnionAuthError && aFailedChannel) { 3807 // Display about:neterror with a style emulating about:blank while the 3808 // Tor client auth prompt is open. Do not use about:blank directly: it 3809 // will mess with the failed channel information persistence! 3810 cssClass.AssignLiteral("onionAuthPrompt"); 3811 } 3812 if (isOnionError) { 3813 // DisplayLoadError requires a non-empty messageStr to proceed and call 3814 // LoadErrorPage. We use a blank space. 3815 if (messageStr.IsEmpty()) { 3816 messageStr.AssignLiteral(u" "); 3817 } 3818 } 3819 } 3820 3821 nsresult delegateErrorCode = aError; 3822 // If the HTTPS-Only Mode upgraded this request and the upgrade might have 3823 // caused this error, we replace the error-page with about:httpsonlyerror 3824 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) { 3825 errorPage.AssignLiteral("httpsonlyerror"); 3826 delegateErrorCode = NS_ERROR_HTTPS_ONLY; 3827 } else if (isBadStsCertError) { 3828 delegateErrorCode = NS_ERROR_BAD_HSTS_CERT; 3829 } 3830 3831 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) { 3832 nsCOMPtr<nsIURI> errorPageURI; 3833 rv = loadURIDelegate->HandleLoadError( 3834 aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode), 3835 getter_AddRefs(errorPageURI)); 3836 // If the docshell is going away there's no point in showing an error page. 3837 if (NS_FAILED(rv) || mIsBeingDestroyed) { 3838 *aDisplayedErrorPage = false; 3839 return NS_OK; 3840 } 3841 3842 if (errorPageURI) { 3843 *aDisplayedErrorPage = 3844 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel)); 3845 return NS_OK; 3846 } 3847 } 3848 3849 // Test if the error should be displayed 3850 if (!error) { 3851 return NS_OK; 3852 } 3853 3854 if (!errorDescriptionID) { 3855 errorDescriptionID = error; 3856 } 3857 3858 glean::page::load_error 3859 .Get(IsSubframe() ? "frame"_ns : "top"_ns, 3860 mozilla::dom::LoadErrorToTelemetryLabel(aError)) 3861 .Add(); 3862 3863 // Test if the error needs to be formatted 3864 if (!messageStr.IsEmpty()) { 3865 // already obtained message 3866 } else { 3867 if (addHostPort) { 3868 // Build up the host:port string. 3869 nsAutoCString hostport; 3870 if (aURI) { 3871 aURI->GetHostPort(hostport); 3872 } else { 3873 hostport.Assign('?'); 3874 } 3875 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement()); 3876 } 3877 3878 nsAutoCString spec; 3879 rv = NS_ERROR_NOT_AVAILABLE; 3880 auto& nextFormatStr = *formatStrs.AppendElement(); 3881 if (aURI) { 3882 // displaying "file://" is aesthetically unpleasing and could even be 3883 // confusing to the user 3884 if (aURI->SchemeIs("file")) { 3885 aURI->GetPathQueryRef(spec); 3886 } else { 3887 aURI->GetSpec(spec); 3888 } 3889 3890 nsCOMPtr<nsITextToSubURI> textToSubURI( 3891 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv)); 3892 if (NS_SUCCEEDED(rv)) { 3893 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr); 3894 } 3895 } else { 3896 spec.Assign('?'); 3897 } 3898 if (NS_FAILED(rv)) { 3899 CopyUTF8toUTF16(spec, nextFormatStr); 3900 } 3901 rv = NS_OK; 3902 3903 nsAutoString str; 3904 rv = 3905 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str); 3906 NS_ENSURE_SUCCESS(rv, rv); 3907 messageStr.Assign(str); 3908 } 3909 3910 // Display the error as a page or an alert prompt 3911 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE); 3912 3913 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) && 3914 aURI->SchemeIs("https")) { 3915 // Maybe TLS intolerant. Treat this as an SSL error. 3916 error = "nssFailure2"; 3917 } 3918 3919 if (mBrowsingContext->GetUseErrorPages()) { 3920 // Display an error page 3921 nsresult loadedPage = 3922 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(), 3923 cssClass.get(), aFailedChannel); 3924 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage); 3925 } else { 3926 // The prompter reqires that our private window has a document (or it 3927 // asserts). Satisfy that assertion now since GetDoc will force 3928 // creation of one if it hasn't already been created. 3929 if (mScriptGlobal) { 3930 (void)mScriptGlobal->GetDoc(); 3931 } 3932 3933 // Display a message box 3934 prompter->Alert(nullptr, messageStr.get()); 3935 } 3936 3937 return NS_OK; 3938 } 3939 3940 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride" 3941 3942 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, 3943 const char* aErrorPage, 3944 const char* aErrorType, 3945 const char16_t* aDescription, 3946 const char* aCSSClass, 3947 nsIChannel* aFailedChannel) { 3948 if (mIsBeingDestroyed) { 3949 return NS_ERROR_NOT_AVAILABLE; 3950 } 3951 3952 #if defined(DEBUG) 3953 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) { 3954 nsAutoCString chanName; 3955 if (aFailedChannel) { 3956 aFailedChannel->GetName(chanName); 3957 } else { 3958 chanName.AssignLiteral("<no channel>"); 3959 } 3960 3961 MOZ_LOG(gDocShellLog, LogLevel::Debug, 3962 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n", 3963 this, aURI ? aURI->GetSpecOrDefault().get() : "", 3964 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get())); 3965 } 3966 #endif 3967 3968 nsAutoCString url; 3969 if (aURI) { 3970 nsresult rv = aURI->GetSpec(url); 3971 NS_ENSURE_SUCCESS(rv, rv); 3972 } else if (aURL) { 3973 CopyUTF16toUTF8(MakeStringSpan(aURL), url); 3974 } else { 3975 return NS_ERROR_INVALID_POINTER; 3976 } 3977 3978 // Create a URL to pass all the error information through to the page. 3979 3980 #undef SAFE_ESCAPE 3981 #define SAFE_ESCAPE(output, input, params) \ 3982 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \ 3983 return NS_ERROR_OUT_OF_MEMORY; \ 3984 } 3985 3986 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass; 3987 SAFE_ESCAPE(escapedUrl, url, url_Path); 3988 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path); 3989 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription), 3990 url_Path); 3991 if (aCSSClass) { 3992 nsCString cssClass(aCSSClass); 3993 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path); 3994 } 3995 nsCString errorPageUrl("about:"); 3996 errorPageUrl.AppendASCII(aErrorPage); 3997 errorPageUrl.AppendLiteral("?e="); 3998 3999 errorPageUrl.AppendASCII(escapedError.get()); 4000 errorPageUrl.AppendLiteral("&u="); 4001 errorPageUrl.AppendASCII(escapedUrl.get()); 4002 if ((strcmp(aErrorPage, "blocked") == 0) && 4003 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) { 4004 errorPageUrl.AppendLiteral("&o=1"); 4005 } 4006 if (!escapedCSSClass.IsEmpty()) { 4007 errorPageUrl.AppendLiteral("&s="); 4008 errorPageUrl.AppendASCII(escapedCSSClass.get()); 4009 } 4010 errorPageUrl.AppendLiteral("&c=UTF-8"); 4011 4012 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID); 4013 int32_t cpsState; 4014 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) && 4015 cpsState == nsICaptivePortalService::LOCKED_PORTAL) { 4016 errorPageUrl.AppendLiteral("&captive=true"); 4017 } 4018 4019 errorPageUrl.AppendLiteral("&d="); 4020 errorPageUrl.AppendASCII(escapedDescription.get()); 4021 4022 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aFailedChannel)); 4023 if (props) { 4024 nsAutoCString addonName; 4025 props->GetPropertyAsACString(u"blockedExtension"_ns, addonName); 4026 4027 nsCString escapedAddonName; 4028 SAFE_ESCAPE(escapedAddonName, addonName, url_Path); 4029 4030 errorPageUrl.AppendLiteral("&a="); 4031 errorPageUrl.AppendASCII(escapedAddonName.get()); 4032 } 4033 4034 nsCOMPtr<nsIURI> errorPageURI; 4035 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl); 4036 NS_ENSURE_SUCCESS(rv, rv); 4037 4038 return LoadErrorPage(errorPageURI, aURI, aFailedChannel); 4039 } 4040 4041 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI, 4042 nsIChannel* aFailedChannel) { 4043 mFailedChannel = aFailedChannel; 4044 mFailedURI = aFailedURI; 4045 mFailedLoadType = mLoadType; 4046 4047 if (mLSHE) { 4048 // Abandon mLSHE's BFCache entry and create a new one. This way, if 4049 // we go back or forward to another SHEntry with the same doc 4050 // identifier, the error page won't persist. 4051 mLSHE->AbandonBFCacheEntry(); 4052 } 4053 4054 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI); 4055 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); 4056 if (mBrowsingContext) { 4057 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags()); 4058 loadState->SetTriggeringWindowId( 4059 mBrowsingContext->GetCurrentInnerWindowId()); 4060 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow(); 4061 if (innerWin) { 4062 loadState->SetTriggeringStorageAccess(innerWin->UsingStorageAccess()); 4063 } 4064 } 4065 loadState->SetLoadType(LOAD_ERROR_PAGE); 4066 loadState->SetFirstParty(true); 4067 loadState->SetSourceBrowsingContext(mBrowsingContext); 4068 if (mozilla::SessionHistoryInParent() && mLoadingEntry) { 4069 // We keep the loading entry for the load that failed here. If the user 4070 // reloads we want to try to reload the original load, not the error page. 4071 loadState->SetLoadingSessionHistoryInfo( 4072 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry)); 4073 } 4074 4075 // Prevent initial about:blank handling, as it's likely irrelevant and 4076 // keeps us from needing to change GeckoView / NavigationDelegateTest. 4077 // It also makes the load more consistent with non-about-blank cases. 4078 loadState->ProhibitInitialAboutBlankHandling(); 4079 4080 return InternalLoad(loadState); 4081 } 4082 4083 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP 4084 nsDocShell::Reload(uint32_t aReloadFlags) { 4085 return ReloadNavigable(Nothing(), aReloadFlags, nullptr, 4086 UserNavigationInvolvement::BrowserUI); 4087 } 4088 4089 // https://html.spec.whatwg.org/#reload 4090 // To reload a navigable navigable given an optional serialized state-or-null 4091 // navigationAPIState (default null) and an optional user navigation 4092 // involvement userInvolvement (default "none"): 4093 nsresult nsDocShell::ReloadNavigable( 4094 mozilla::Maybe<NotNull<JSContext*>> aCx, uint32_t aReloadFlags, 4095 nsIStructuredCloneContainer* aNavigationAPIState, 4096 UserNavigationInvolvement aUserInvolvement, 4097 NavigationAPIMethodTracker* aNavigationAPIMethodTracker) { 4098 if (!IsNavigationAllowed()) { 4099 return NS_OK; // JS may not handle returning of an error code 4100 } 4101 4102 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0), 4103 "Reload command not updated to use load flags!"); 4104 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0, 4105 "Don't pass these flags to Reload"); 4106 4107 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags); 4108 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG); 4109 NS_ENSURE_TRUE( 4110 aUserInvolvement == UserNavigationInvolvement::BrowserUI || aCx, 4111 NS_ERROR_INVALID_ARG); 4112 4113 RefPtr<nsDocShell> docShell(this); 4114 4115 // 1. If userInvolvement is not "browser UI", then: 4116 if (aUserInvolvement != UserNavigationInvolvement::BrowserUI) { 4117 // 1.1 Let navigation be navigable's active window's navigation API. 4118 nsPIDOMWindowOuter* windowOuter = GetWindow(); 4119 MOZ_DIAGNOSTIC_ASSERT(windowOuter); 4120 nsPIDOMWindowInner* windowInner = windowOuter->GetCurrentInnerWindow(); 4121 MOZ_DIAGNOSTIC_ASSERT(windowInner); 4122 RefPtr navigation = windowInner->Navigation(); 4123 4124 // 1.2 Let destinationNavigationAPIState be navigable's active session 4125 // history entry's navigation API state. 4126 // 1.3 If navigationAPIState is not null, then set 4127 // destinationNavigationAPIState to navigationAPIState. 4128 RefPtr<nsIStructuredCloneContainer> destinationNavigationAPIState = 4129 aNavigationAPIState; 4130 if (!destinationNavigationAPIState) { 4131 destinationNavigationAPIState = 4132 mActiveEntry ? mActiveEntry->GetNavigationAPIState() : nullptr; 4133 } 4134 4135 // 1.4 Let continue be the result of firing a push/replace/reload navigate 4136 // event at navigation with navigationType set to "reload", 4137 // isSameDocument set to false, userInvolvement set to userInvolvement, 4138 // destinationURL set to navigable's active session history entry's URL, 4139 // and navigationAPIState set to destinationNavigationAPIState. 4140 // 1.5 If continue is false, then return. 4141 RefPtr destinationURL = mActiveEntry ? mActiveEntry->GetURI() : nullptr; 4142 if (navigation && 4143 !navigation->FirePushReplaceReloadNavigateEvent( 4144 *aCx, NavigationType::Reload, destinationURL, 4145 /* aIsSameDocument */ false, Some(aUserInvolvement), 4146 /* aSourceElement*/ nullptr, /* aFormDataEntryList */ nullptr, 4147 destinationNavigationAPIState, 4148 /* aClassiCHistoryAPIState */ nullptr, 4149 aNavigationAPIMethodTracker)) { 4150 return NS_OK; 4151 } 4152 } 4153 4154 // The following steps are implemented by the remainder of ReloadNavigable. 4155 4156 // Send notifications to the HistoryListener if any, about the impending 4157 // reload 4158 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 4159 if (mozilla::SessionHistoryInParent()) { 4160 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this)); 4161 bool forceReload = IsForceReloadType(loadType); 4162 if (!XRE_IsParentProcess()) { 4163 ++mPendingReloadCount; 4164 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer); 4165 NS_ENSURE_STATE(viewer); 4166 4167 bool okToUnload = true; 4168 MOZ_TRY(viewer->PermitUnload(&okToUnload)); 4169 if (mIsBeingDestroyed) { 4170 // unload handler destroyed this docshell. 4171 return NS_ERROR_NOT_AVAILABLE; 4172 } 4173 if (!okToUnload) { 4174 return NS_OK; 4175 } 4176 4177 RefPtr<Document> doc(GetDocument()); 4178 RefPtr<BrowsingContext> browsingContext(mBrowsingContext); 4179 nsCOMPtr<nsIURI> currentURI(mCurrentURI); 4180 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo); 4181 RefPtr<StopDetector> stopDetector = new StopDetector(); 4182 nsCOMPtr<nsILoadGroup> loadGroup; 4183 GetLoadGroup(getter_AddRefs(loadGroup)); 4184 if (loadGroup) { 4185 // loadGroup may be null in theory. In that case stopDetector just 4186 // doesn't do anything. 4187 loadGroup->AddRequest(stopDetector, nullptr); 4188 } 4189 4190 ContentChild::GetSingleton()->SendNotifyOnHistoryReload( 4191 mBrowsingContext, forceReload, 4192 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo, 4193 loadGroup, stopDetector]( 4194 std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>, 4195 Maybe<bool>>&& aResult) { 4196 auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() { 4197 if (loadGroup) { 4198 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK); 4199 } 4200 }); 4201 4202 // Decrease mPendingReloadCount before any other early returns! 4203 if (--(docShell->mPendingReloadCount) > 0) { 4204 return; 4205 } 4206 4207 if (stopDetector->Canceled()) { 4208 return; 4209 } 4210 bool canReload; 4211 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState; 4212 Maybe<bool> reloadingActiveEntry; 4213 4214 std::tie(canReload, loadState, reloadingActiveEntry) = aResult; 4215 4216 if (!canReload) { 4217 return; 4218 } 4219 4220 if (loadState.isSome()) { 4221 MOZ_LOG( 4222 gSHLog, LogLevel::Debug, 4223 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get())); 4224 loadState.ref()->SetNotifiedBeforeUnloadListeners(true); 4225 docShell->LoadHistoryEntry(loadState.ref(), loadType, 4226 reloadingActiveEntry.ref()); 4227 } else { 4228 MOZ_LOG(gSHLog, LogLevel::Debug, 4229 ("nsDocShell %p ReloadDocument", docShell.get())); 4230 ReloadDocument(docShell, doc, loadType, browsingContext, 4231 currentURI, referrerInfo, 4232 /* aNotifiedBeforeUnloadListeners */ true); 4233 } 4234 }, 4235 [](mozilla::ipc::ResponseRejectReason) {}); 4236 } else { 4237 // Parent process 4238 bool canReload = false; 4239 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState; 4240 Maybe<bool> reloadingActiveEntry; 4241 if (!mBrowsingContext->IsDiscarded()) { 4242 mBrowsingContext->Canonical()->NotifyOnHistoryReload( 4243 forceReload, canReload, loadState, reloadingActiveEntry); 4244 } 4245 if (canReload) { 4246 if (loadState.isSome()) { 4247 MOZ_LOG(gSHLog, LogLevel::Debug, 4248 ("nsDocShell %p Reload - LoadHistoryEntry", this)); 4249 LoadHistoryEntry(loadState.ref(), loadType, 4250 reloadingActiveEntry.ref()); 4251 } else { 4252 MOZ_LOG(gSHLog, LogLevel::Debug, 4253 ("nsDocShell %p ReloadDocument", this)); 4254 RefPtr<Document> doc = GetDocument(); 4255 RefPtr<BrowsingContext> bc = mBrowsingContext; 4256 nsCOMPtr<nsIURI> currentURI = mCurrentURI; 4257 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo; 4258 ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo); 4259 } 4260 } 4261 } 4262 return NS_OK; 4263 } 4264 4265 bool canReload = true; 4266 if (rootSH) { 4267 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload); 4268 } 4269 4270 if (!canReload) { 4271 return NS_OK; 4272 } 4273 4274 /* If you change this part of code, make sure bug 45297 does not re-occur */ 4275 if (mOSHE) { 4276 nsCOMPtr<nsISHEntry> oshe = mOSHE; 4277 return LoadHistoryEntry( 4278 oshe, loadType, 4279 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION); 4280 } 4281 4282 if (mLSHE) { // In case a reload happened before the current load is done 4283 nsCOMPtr<nsISHEntry> lshe = mLSHE; 4284 return LoadHistoryEntry( 4285 lshe, loadType, 4286 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION); 4287 } 4288 4289 RefPtr<Document> doc = GetDocument(); 4290 RefPtr<BrowsingContext> bc = mBrowsingContext; 4291 nsCOMPtr<nsIURI> currentURI = mCurrentURI; 4292 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo; 4293 return ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo); 4294 } 4295 4296 void nsDocShell::DisplayRestrictedContentError() { 4297 bool didDisplayLoadError = false; 4298 RefPtr<mozilla::dom::Document> doc = GetDocument(); 4299 if (!doc) { 4300 return; 4301 } 4302 doc->TerminateParserAndDisableScripts(); 4303 DisplayLoadError(NS_ERROR_RESTRICTED_CONTENT, doc->GetDocumentURI(), nullptr, 4304 nullptr, &didDisplayLoadError); 4305 } 4306 4307 /* static */ 4308 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument, 4309 uint32_t aLoadType, 4310 BrowsingContext* aBrowsingContext, 4311 nsIURI* aCurrentURI, 4312 nsIReferrerInfo* aReferrerInfo, 4313 bool aNotifiedBeforeUnloadListeners) { 4314 if (!aDocument) { 4315 return NS_OK; 4316 } 4317 4318 // Do not inherit owner from document 4319 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE; 4320 nsAutoString srcdoc; 4321 nsIURI* baseURI = nullptr; 4322 nsCOMPtr<nsIURI> originalURI; 4323 nsCOMPtr<nsIURI> resultPrincipalURI; 4324 bool loadReplace = false; 4325 4326 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal(); 4327 nsCOMPtr<nsIPolicyContainer> policyContainer = 4328 aDocument->GetPolicyContainer(); 4329 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags(); 4330 uint64_t triggeringWindowId = aDocument->InnerWindowID(); 4331 bool triggeringStorageAccess = aDocument->UsingStorageAccess(); 4332 net::ClassificationFlags triggeringClassificationFlags = 4333 aDocument->GetScriptTrackingFlags(); 4334 4335 nsAutoString contentTypeHint; 4336 aDocument->GetContentType(contentTypeHint); 4337 4338 if (aDocument->IsSrcdocDocument()) { 4339 aDocument->GetSrcdocData(srcdoc); 4340 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC; 4341 baseURI = aDocument->GetBaseURI(); 4342 } else { 4343 srcdoc = VoidString(); 4344 } 4345 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel(); 4346 if (chan) { 4347 uint32_t loadFlags; 4348 chan->GetLoadFlags(&loadFlags); 4349 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; 4350 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan)); 4351 if (httpChan) { 4352 httpChan->GetOriginalURI(getter_AddRefs(originalURI)); 4353 } 4354 4355 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 4356 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)); 4357 } 4358 4359 if (!triggeringPrincipal) { 4360 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal"); 4361 return NS_ERROR_FAILURE; 4362 } 4363 4364 // Stack variables to ensure changes to the member variables don't affect to 4365 // the call. 4366 nsCOMPtr<nsIURI> currentURI = aCurrentURI; 4367 4368 // Reload always rewrites result principal URI. 4369 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI; 4370 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI)); 4371 4372 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext(); 4373 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI); 4374 loadState->SetReferrerInfo(aReferrerInfo); 4375 loadState->SetOriginalURI(originalURI); 4376 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI); 4377 loadState->SetLoadReplace(loadReplace); 4378 loadState->SetTriggeringPrincipal(triggeringPrincipal); 4379 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags); 4380 loadState->SetTriggeringWindowId(triggeringWindowId); 4381 loadState->SetTriggeringStorageAccess(triggeringStorageAccess); 4382 loadState->SetTriggeringClassificationFlags(triggeringClassificationFlags); 4383 loadState->SetPrincipalToInherit(triggeringPrincipal); 4384 loadState->SetPolicyContainer(policyContainer); 4385 loadState->SetInternalLoadFlags(flags); 4386 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint)); 4387 loadState->SetLoadType(aLoadType); 4388 loadState->SetFirstParty(true); 4389 loadState->SetSrcdocData(srcdoc); 4390 loadState->SetSourceBrowsingContext(aBrowsingContext); 4391 loadState->SetBaseURI(baseURI); 4392 loadState->SetHasValidUserGestureActivation( 4393 context && context->HasValidTransientUserGestureActivation()); 4394 4395 loadState->SetTextDirectiveUserActivation( 4396 aDocument->ConsumeTextDirectiveUserActivation() || 4397 loadState->HasValidUserGestureActivation()); 4398 4399 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners); 4400 return aDocShell->InternalLoad(loadState); 4401 } 4402 4403 // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) 4404 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP 4405 nsDocShell::Stop(uint32_t aStopFlags) { 4406 return StopInternal(aStopFlags, UnsetOngoingNavigation::Yes); 4407 } 4408 4409 nsresult nsDocShell::StopInternal( 4410 uint32_t aStopFlags, UnsetOngoingNavigation aUnsetOngoingNavigation) { 4411 RefPtr kungFuDeathGrip = this; 4412 if (RefPtr<Document> doc = GetExtantDocument(); 4413 aUnsetOngoingNavigation == UnsetOngoingNavigation::Yes && doc && 4414 !doc->ShouldIgnoreOpens() && 4415 mOngoingNavigation == Some(OngoingNavigation::NavigationID)) { 4416 SetOngoingNavigation(Nothing()); 4417 } 4418 4419 // Revoke any pending event related to content viewer restoration 4420 mRestorePresentationEvent.Revoke(); 4421 4422 if (mLoadType == LOAD_ERROR_PAGE) { 4423 if (mLSHE) { 4424 // Since error page loads never unset mLSHE, do so now 4425 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE)); 4426 } 4427 mActiveEntryIsLoadingFromSessionHistory = false; 4428 4429 mFailedChannel = nullptr; 4430 mFailedURI = nullptr; 4431 } 4432 4433 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) { 4434 // Stop the document loading and animations 4435 if (mDocumentViewer) { 4436 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer; 4437 viewer->Stop(); 4438 } 4439 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) { 4440 // Stop the document loading only 4441 if (mDocumentViewer) { 4442 RefPtr<Document> doc = mDocumentViewer->GetDocument(); 4443 if (doc) { 4444 doc->StopDocumentLoad(); 4445 } 4446 } 4447 } 4448 4449 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) { 4450 // Suspend any timers that were set for this loader. We'll clear 4451 // them out for good in CreateDocumentViewer. 4452 if (mRefreshURIList) { 4453 SuspendRefreshURIs(); 4454 mSavedRefreshURIList.swap(mRefreshURIList); 4455 mRefreshURIList = nullptr; 4456 } 4457 4458 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will 4459 // just call Stop() on us as an nsIDocumentLoader... We need fewer 4460 // redundant apis! 4461 Stop(); 4462 4463 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the 4464 // BFCache now, and the Stop above will have removed the DocumentChannel 4465 // from the loadgroup. 4466 mChannelToDisconnectOnPageHide = 0; 4467 } 4468 4469 for (auto* child : mChildList.ForwardRange()) { 4470 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child)); 4471 if (shellAsNav) { 4472 shellAsNav->Stop(aStopFlags); 4473 } 4474 } 4475 4476 return NS_OK; 4477 } 4478 4479 NS_IMETHODIMP 4480 nsDocShell::GetDocument(Document** aDocument) { 4481 NS_ENSURE_ARG_POINTER(aDocument); 4482 NS_ENSURE_TRUE(VerifyDocumentViewer(), NS_ERROR_FAILURE); 4483 4484 RefPtr<Document> doc = mDocumentViewer->GetDocument(); 4485 if (!doc) { 4486 return NS_ERROR_NOT_AVAILABLE; 4487 } 4488 4489 doc.forget(aDocument); 4490 return NS_OK; 4491 } 4492 4493 NS_IMETHODIMP 4494 nsDocShell::GetCurrentURI(nsIURI** aURI) { 4495 NS_ENSURE_ARG_POINTER(aURI); 4496 4497 nsCOMPtr<nsIURI> uri = mCurrentURI; 4498 uri.forget(aURI); 4499 return NS_OK; 4500 } 4501 4502 NS_IMETHODIMP 4503 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) { 4504 NS_ENSURE_ARG_POINTER(aSessionHistory); 4505 RefPtr<ChildSHistory> shistory = GetSessionHistory(); 4506 shistory.forget(aSessionHistory); 4507 return NS_OK; 4508 } 4509 4510 //***************************************************************************** 4511 // nsDocShell::nsIWebPageDescriptor 4512 //***************************************************************************** 4513 4514 NS_IMETHODIMP 4515 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell, 4516 const nsAString& aURI) { 4517 if (!aOtherDocShell) { 4518 return NS_ERROR_INVALID_POINTER; 4519 } 4520 nsCOMPtr<nsIURI> newURI; 4521 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI); 4522 if (NS_FAILED(rv)) { 4523 return rv; 4524 } 4525 4526 RefPtr<nsDocShellLoadState> loadState; 4527 uint32_t cacheKey; 4528 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell); 4529 if (mozilla::SessionHistoryInParent()) { 4530 loadState = new nsDocShellLoadState(newURI); 4531 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) { 4532 return NS_ERROR_INVALID_POINTER; 4533 } 4534 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0); 4535 } else { 4536 nsCOMPtr<nsISHEntry> entry; 4537 bool isOriginalSHE; 4538 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE); 4539 if (!entry) { 4540 return NS_ERROR_INVALID_POINTER; 4541 } 4542 rv = entry->CreateLoadInfo(getter_AddRefs(loadState)); 4543 NS_ENSURE_SUCCESS(rv, rv); 4544 entry->GetCacheKey(&cacheKey); 4545 loadState->SetURI(newURI); 4546 loadState->SetSHEntry(nullptr); 4547 } 4548 4549 // We're doing a load of the page, via an API that 4550 // is only exposed to system code. The triggering principal for this load 4551 // should be the system principal. 4552 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal()); 4553 loadState->SetOriginalURI(nullptr); 4554 loadState->SetResultPrincipalURI(nullptr); 4555 4556 // Initial about:blank handling is probably irrelevant, but newURI shouldn't 4557 // anyway be about:blank. Otherwise we should prohibit initial about blank 4558 // handling. 4559 MOZ_ASSERT(!NS_IsAboutBlankAllowQueryAndFragment(newURI), 4560 "We only expect view-source:// URIs"); 4561 4562 return InternalLoad(loadState, Some(cacheKey)); 4563 } 4564 4565 NS_IMETHODIMP 4566 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) { 4567 MOZ_ASSERT(aPageDescriptor, "Null out param?"); 4568 4569 *aPageDescriptor = nullptr; 4570 4571 nsISHEntry* src = mOSHE ? mOSHE : mLSHE; 4572 if (src) { 4573 nsCOMPtr<nsISHEntry> dest; 4574 4575 nsresult rv = src->Clone(getter_AddRefs(dest)); 4576 if (NS_FAILED(rv)) { 4577 return rv; 4578 } 4579 4580 // null out inappropriate cloned attributes... 4581 dest->SetParent(nullptr); 4582 dest->SetIsSubFrame(false); 4583 4584 return CallQueryInterface(dest, aPageDescriptor); 4585 } 4586 4587 return NS_ERROR_NOT_AVAILABLE; 4588 } 4589 4590 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry() 4591 const { 4592 nsCOMPtr<nsIInputStream> postData; 4593 if (mozilla::SessionHistoryInParent()) { 4594 if (mActiveEntry) { 4595 postData = mActiveEntry->GetPostData(); 4596 } else if (mLoadingEntry) { 4597 postData = mLoadingEntry->mInfo.GetPostData(); 4598 } 4599 } else { 4600 if (mOSHE) { 4601 postData = mOSHE->GetPostData(); 4602 } else if (mLSHE) { 4603 postData = mLSHE->GetPostData(); 4604 } 4605 } 4606 4607 return postData.forget(); 4608 } 4609 4610 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const { 4611 if (mozilla::SessionHistoryInParent()) { 4612 if (mActiveEntry) { 4613 return Some(mActiveEntry->GetCacheKey()); 4614 } 4615 4616 if (mLoadingEntry) { 4617 return Some(mLoadingEntry->mInfo.GetCacheKey()); 4618 } 4619 } else { 4620 if (mOSHE) { 4621 return Some(mOSHE->GetCacheKey()); 4622 } 4623 4624 if (mLSHE) { 4625 return Some(mLSHE->GetCacheKey()); 4626 } 4627 } 4628 4629 return Nothing(); 4630 } 4631 4632 bool nsDocShell::FillLoadStateFromCurrentEntry( 4633 nsDocShellLoadState& aLoadState) { 4634 if (mLoadingEntry) { 4635 mLoadingEntry->mInfo.FillLoadInfo(aLoadState); 4636 return true; 4637 } 4638 if (mActiveEntry) { 4639 mActiveEntry->FillLoadInfo(aLoadState); 4640 return true; 4641 } 4642 return false; 4643 } 4644 4645 //***************************************************************************** 4646 // nsDocShell::nsIBaseWindow 4647 //***************************************************************************** 4648 4649 NS_IMETHODIMP 4650 nsDocShell::Destroy() { 4651 // XXX: We allow this function to be called just once. If you are going to 4652 // reset new variables in this function, please make sure the variables will 4653 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed| 4654 // in the setter functions for the variables would be enough. 4655 if (mIsBeingDestroyed) { 4656 return NS_ERROR_DOCSHELL_DYING; 4657 } 4658 4659 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome, 4660 "Unexpected item type in docshell"); 4661 4662 nsCOMPtr<nsIObserverService> serv = services::GetObserverService(); 4663 if (serv) { 4664 const char* msg = mItemType == typeContent 4665 ? NS_WEBNAVIGATION_DESTROY 4666 : NS_CHROME_WEBNAVIGATION_DESTROY; 4667 serv->NotifyObservers(GetAsSupports(this), msg, nullptr); 4668 } 4669 4670 mIsBeingDestroyed = true; 4671 4672 // Brak the cycle with the initial client, if present. 4673 mInitialClientSource.reset(); 4674 4675 // Make sure to blow away our mLoadingURI just in case. No loads 4676 // from inside this pagehide. 4677 mLoadingURI = nullptr; 4678 4679 // Fire unload event before we blow anything away. 4680 (void)FirePageHideNotification(true); 4681 4682 // Clear pointers to any detached nsEditorData that's lying 4683 // around in shistory entries. Breaks cycle. See bug 430921. 4684 if (mOSHE) { 4685 mOSHE->SetEditorData(nullptr); 4686 } 4687 if (mLSHE) { 4688 mLSHE->SetEditorData(nullptr); 4689 } 4690 4691 // Note: mContentListener can be null if Init() failed and we're being 4692 // called from the destructor. 4693 if (mContentListener) { 4694 mContentListener->DropDocShellReference(); 4695 mContentListener->SetParentContentListener(nullptr); 4696 // Note that we do NOT set mContentListener to null here; that 4697 // way if someone tries to do a load in us after this point 4698 // the nsDSURIContentListener will block it. All of which 4699 // means that we should do this before calling Stop(), of 4700 // course. 4701 } 4702 4703 if (BrowsingContext* browsingContext = GetBrowsingContext(); 4704 browsingContext && !browsingContext->IsTop()) { 4705 InformNavigationAPIAboutChildNavigableDestruction(); 4706 } 4707 4708 // Stop any URLs that are currently being loaded... 4709 Stop(nsIWebNavigation::STOP_ALL); 4710 4711 mEditorData = nullptr; 4712 4713 // Save the state of the current document, before destroying the window. 4714 // This is needed to capture the state of a frameset when the new document 4715 // causes the frameset to be destroyed... 4716 PersistLayoutHistoryState(); 4717 4718 // Remove this docshell from its parent's child list 4719 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem = 4720 do_QueryInterface(GetAsSupports(mParent)); 4721 if (docShellParentAsItem) { 4722 docShellParentAsItem->RemoveChild(this); 4723 } 4724 4725 DestroyDocumentViewer(); 4726 4727 nsDocLoader::Destroy(); 4728 4729 mParentWidget = nullptr; 4730 SetCurrentURIInternal(nullptr); 4731 4732 if (mScriptGlobal) { 4733 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess); 4734 mScriptGlobal = nullptr; 4735 } 4736 4737 if (GetSessionHistory()) { 4738 // We want to destroy these content viewers now rather than 4739 // letting their destruction wait for the session history 4740 // entries to get garbage collected. (Bug 488394) 4741 GetSessionHistory()->EvictLocalDocumentViewers(); 4742 } 4743 4744 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) { 4745 mBrowsingContext->PrepareForProcessChange(); 4746 } 4747 4748 SetTreeOwner(nullptr); 4749 4750 mBrowserChild = nullptr; 4751 4752 mChromeEventHandler = nullptr; 4753 4754 mBCWebProgressStatusFilter = nullptr; 4755 4756 // Cancel any timers that were set for this docshell; this is needed 4757 // to break the cycle between us and the timers. 4758 CancelRefreshURITimers(); 4759 4760 return NS_OK; 4761 } 4762 4763 double nsDocShell::GetWidgetCSSToDeviceScale() { 4764 if (mParentWidget) { 4765 return mParentWidget->GetDefaultScale().scale; 4766 } 4767 if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) { 4768 return ownerWindow->GetWidgetCSSToDeviceScale(); 4769 } 4770 return 1.0; 4771 } 4772 4773 NS_IMETHODIMP 4774 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) { 4775 if (mParentWidget) { 4776 *aScale = mParentWidget->GetDesktopToDeviceScale().scale; 4777 return NS_OK; 4778 } 4779 4780 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner)); 4781 if (ownerWindow) { 4782 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale); 4783 } 4784 4785 *aScale = 1.0; 4786 return NS_OK; 4787 } 4788 4789 NS_IMETHODIMP 4790 nsDocShell::SetPosition(int32_t aX, int32_t aY) { 4791 mBounds.MoveTo(aX, aY); 4792 4793 if (mDocumentViewer) { 4794 NS_ENSURE_SUCCESS(mDocumentViewer->Move(aX, aY), NS_ERROR_FAILURE); 4795 } 4796 4797 return NS_OK; 4798 } 4799 4800 NS_IMETHODIMP 4801 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) { 4802 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner)); 4803 if (ownerWindow) { 4804 return ownerWindow->SetPositionDesktopPix(aX, aY); 4805 } 4806 4807 double scale = 1.0; 4808 GetDevicePixelsPerDesktopPixel(&scale); 4809 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale)); 4810 } 4811 4812 NS_IMETHODIMP 4813 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) { 4814 return GetPositionAndSize(aX, aY, nullptr, nullptr); 4815 } 4816 4817 NS_IMETHODIMP 4818 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) { 4819 int32_t x = 0, y = 0; 4820 GetPosition(&x, &y); 4821 return SetPositionAndSize(x, y, aWidth, aHeight, 4822 aRepaint ? nsIBaseWindow::eRepaint : 0); 4823 } 4824 4825 NS_IMETHODIMP 4826 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) { 4827 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight); 4828 } 4829 4830 NS_IMETHODIMP 4831 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth, 4832 int32_t aHeight, uint32_t aFlags) { 4833 mBounds.SetRect(aX, aY, aWidth, aHeight); 4834 4835 // Hold strong ref, since SetBounds can make us null out mDocumentViewer 4836 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer; 4837 if (viewer) { 4838 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize) 4839 ? nsIDocumentViewer::eDelayResize 4840 : 0; 4841 // XXX Border figured in here or is that handled elsewhere? 4842 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags); 4843 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); 4844 } 4845 4846 return NS_OK; 4847 } 4848 4849 NS_IMETHODIMP 4850 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth, 4851 int32_t* aHeight) { 4852 if (mParentWidget) { 4853 // ensure size is up-to-date if window has changed resolution 4854 LayoutDeviceIntRect r = mParentWidget->GetClientBounds(); 4855 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0); 4856 } 4857 4858 // We should really consider just getting this information from 4859 // our window instead of duplicating the storage and code... 4860 if (aWidth || aHeight) { 4861 // Caller wants to know our size; make sure to give them up to 4862 // date information. 4863 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent))); 4864 if (doc) { 4865 doc->FlushPendingNotifications(FlushType::Layout); 4866 } 4867 } 4868 4869 DoGetPositionAndSize(aX, aY, aWidth, aHeight); 4870 return NS_OK; 4871 } 4872 4873 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth, 4874 int32_t* aHeight) { 4875 if (aX) { 4876 *aX = mBounds.X(); 4877 } 4878 if (aY) { 4879 *aY = mBounds.Y(); 4880 } 4881 if (aWidth) { 4882 *aWidth = mBounds.Width(); 4883 } 4884 if (aHeight) { 4885 *aHeight = mBounds.Height(); 4886 } 4887 } 4888 4889 NS_IMETHODIMP 4890 nsDocShell::SetDimensions(DimensionRequest&& aRequest) { 4891 return NS_ERROR_NOT_IMPLEMENTED; 4892 } 4893 4894 NS_IMETHODIMP 4895 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, 4896 int32_t* aY, int32_t* aCX, int32_t* aCY) { 4897 return NS_ERROR_NOT_IMPLEMENTED; 4898 } 4899 4900 NS_IMETHODIMP 4901 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) { 4902 NS_ENSURE_ARG_POINTER(aParentWidget); 4903 4904 *aParentWidget = mParentWidget; 4905 NS_IF_ADDREF(*aParentWidget); 4906 4907 return NS_OK; 4908 } 4909 4910 NS_IMETHODIMP 4911 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) { 4912 MOZ_ASSERT(!mIsBeingDestroyed); 4913 mParentWidget = aParentWidget; 4914 4915 return NS_OK; 4916 } 4917 4918 NS_IMETHODIMP 4919 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) { 4920 // the nativeHandle should be accessed from nsIAppWindow 4921 return NS_ERROR_NOT_IMPLEMENTED; 4922 } 4923 4924 NS_IMETHODIMP 4925 nsDocShell::GetVisibility(bool* aVisibility) { 4926 NS_ENSURE_ARG_POINTER(aVisibility); 4927 4928 *aVisibility = false; 4929 4930 if (!mDocumentViewer) { 4931 return NS_OK; 4932 } 4933 4934 PresShell* presShell = GetPresShell(); 4935 if (!presShell || presShell->IsUnderHiddenEmbedderElement()) { 4936 // NOTE(emilio): IsUnderHiddenEmbedderElement() accounts for ancestors as 4937 // well. 4938 return NS_OK; 4939 } 4940 4941 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner)); 4942 if (!treeOwnerAsWin) { 4943 *aVisibility = true; 4944 return NS_OK; 4945 } 4946 4947 // Check with the tree owner as well to give embedders a chance to 4948 // expose visibility as well. 4949 nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility); 4950 if (rv == NS_ERROR_NOT_IMPLEMENTED) { 4951 // The tree owner had no opinion on our visibility. 4952 *aVisibility = true; 4953 return NS_OK; 4954 } 4955 return rv; 4956 } 4957 4958 void nsDocShell::ActivenessMaybeChanged() { 4959 const bool isActive = mBrowsingContext->IsActive(); 4960 if (RefPtr<PresShell> presShell = GetPresShell()) { 4961 presShell->ActivenessMaybeChanged(); 4962 } 4963 4964 // Tell the window about it 4965 if (mScriptGlobal) { 4966 mScriptGlobal->SetIsBackground(!isActive); 4967 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) { 4968 // Update orientation when the top-level browsing context becomes active. 4969 if (isActive && mBrowsingContext->IsTop() && 4970 !mBrowsingContext->Windowless()) { 4971 // We only care about the top-level browsing context. 4972 auto orientation = mBrowsingContext->GetOrientationLock(); 4973 ScreenOrientation::UpdateActiveOrientationLock(orientation); 4974 } 4975 4976 doc->PostVisibilityUpdateEvent(); 4977 } 4978 } 4979 4980 // Tell the nsDOMNavigationTiming about it 4981 RefPtr<nsDOMNavigationTiming> timing = mTiming; 4982 if (!timing && mDocumentViewer) { 4983 if (Document* doc = mDocumentViewer->GetDocument()) { 4984 timing = doc->GetNavigationTiming(); 4985 } 4986 } 4987 if (timing) { 4988 timing->NotifyDocShellStateChanged( 4989 isActive ? nsDOMNavigationTiming::DocShellState::eActive 4990 : nsDOMNavigationTiming::DocShellState::eInactive); 4991 } 4992 4993 // Restart or stop meta refresh timers if necessary 4994 if (mDisableMetaRefreshWhenInactive) { 4995 if (isActive) { 4996 ResumeRefreshURIs(); 4997 } else { 4998 SuspendRefreshURIs(); 4999 } 5000 } 5001 5002 if (InputTaskManager::CanSuspendInputEvent()) { 5003 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive); 5004 } 5005 } 5006 5007 NS_IMETHODIMP 5008 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) { 5009 if (!mWillChangeProcess) { 5010 // Intentionally ignoring handling discarded browsing contexts. 5011 (void)mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags); 5012 } else { 5013 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on 5014 // shutdown. Sorry DevTools, your DocShell is in another process. 5015 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell"); 5016 } 5017 return NS_OK; 5018 } 5019 5020 NS_IMETHODIMP 5021 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) { 5022 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags(); 5023 return NS_OK; 5024 } 5025 5026 NS_IMETHODIMP 5027 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) { 5028 NS_ENSURE_ARG_POINTER(aFailedChannel); 5029 Document* doc = GetDocument(); 5030 if (!doc) { 5031 *aFailedChannel = nullptr; 5032 return NS_OK; 5033 } 5034 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel()); 5035 return NS_OK; 5036 } 5037 5038 NS_IMETHODIMP 5039 nsDocShell::SetVisibility(bool aVisibility) { 5040 // Show()/Hide() may change mDocumentViewer. 5041 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer; 5042 if (!viewer) { 5043 return NS_OK; 5044 } 5045 if (aVisibility) { 5046 viewer->Show(); 5047 } else { 5048 viewer->Hide(); 5049 } 5050 5051 return NS_OK; 5052 } 5053 5054 NS_IMETHODIMP 5055 nsDocShell::GetEnabled(bool* aEnabled) { 5056 NS_ENSURE_ARG_POINTER(aEnabled); 5057 *aEnabled = true; 5058 return NS_ERROR_NOT_IMPLEMENTED; 5059 } 5060 5061 NS_IMETHODIMP 5062 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; } 5063 5064 NS_IMETHODIMP 5065 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) { 5066 // We don't create our own widget, so simply return the parent one. 5067 return GetParentWidget(aMainWidget); 5068 } 5069 5070 NS_IMETHODIMP 5071 nsDocShell::GetTitle(nsAString& aTitle) { 5072 aTitle = mTitle; 5073 return NS_OK; 5074 } 5075 5076 NS_IMETHODIMP 5077 nsDocShell::SetTitle(const nsAString& aTitle) { 5078 // Avoid unnecessary updates of the title if the URI and the title haven't 5079 // changed. 5080 if (mTitleValidForCurrentURI && mTitle == aTitle) { 5081 return NS_OK; 5082 } 5083 5084 // Store local title 5085 mTitle = aTitle; 5086 mTitleValidForCurrentURI = true; 5087 5088 // When title is set on the top object it should then be passed to the 5089 // tree owner. 5090 if (mBrowsingContext->IsTop()) { 5091 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner)); 5092 if (treeOwnerAsWin) { 5093 treeOwnerAsWin->SetTitle(aTitle); 5094 } 5095 } 5096 5097 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) { 5098 UpdateGlobalHistoryTitle(mCurrentURI); 5099 } 5100 5101 // Update SessionHistory with the document's title. 5102 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) { 5103 SetTitleOnHistoryEntry(true); 5104 } 5105 5106 return NS_OK; 5107 } 5108 5109 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) { 5110 if (mOSHE) { 5111 mOSHE->SetTitle(mTitle); 5112 } 5113 5114 if (mActiveEntry && mBrowsingContext) { 5115 mActiveEntry->SetTitle(mTitle); 5116 if (aUpdateEntryInSessionHistory) { 5117 if (XRE_IsParentProcess()) { 5118 SessionHistoryEntry* entry = 5119 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 5120 if (entry) { 5121 entry->SetTitle(mTitle); 5122 } 5123 } else { 5124 (void)ContentChild::GetSingleton()->SendSessionHistoryEntryTitle( 5125 mBrowsingContext, mTitle); 5126 } 5127 } 5128 } 5129 } 5130 5131 nsPoint nsDocShell::GetCurScrollPos() { 5132 nsPoint scrollPos; 5133 if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) { 5134 scrollPos = sf->GetVisualViewportOffset(); 5135 } 5136 return scrollPos; 5137 } 5138 5139 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos, 5140 int32_t aCurVerticalPos) { 5141 ScrollContainerFrame* sf = GetRootScrollContainerFrame(); 5142 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE); 5143 5144 ScrollMode scrollMode = sf->ScrollModeForScrollBehavior(); 5145 5146 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos); 5147 sf->ScrollTo(targetPos, scrollMode); 5148 5149 // Set the visual viewport offset as well. 5150 5151 RefPtr<PresShell> presShell = GetPresShell(); 5152 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); 5153 5154 nsPresContext* presContext = presShell->GetPresContext(); 5155 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE); 5156 5157 // Only the root content document can have a distinct visual viewport offset. 5158 if (!presContext->IsRootContentDocumentCrossProcess()) { 5159 return NS_OK; 5160 } 5161 5162 // Not on a platform with a distinct visual viewport - don't bother setting 5163 // the visual viewport offset. 5164 if (!presShell->IsVisualViewportSizeSet()) { 5165 return NS_OK; 5166 } 5167 5168 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread, 5169 scrollMode); 5170 5171 return NS_OK; 5172 } 5173 5174 void nsDocShell::RestoreScrollPositionFromTargetSessionHistoryInfo( 5175 SessionHistoryInfo* aTarget) { 5176 MOZ_DIAGNOSTIC_ASSERT(mozilla::SessionHistoryInParent()); 5177 nscoord bx = 0; 5178 nscoord by = 0; 5179 if (aTarget) { 5180 aTarget->GetScrollPosition(&bx, &by); 5181 } 5182 SetCurScrollPosEx(bx, by); 5183 } 5184 5185 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) { 5186 if (mScrollbarPref == aPref) { 5187 return; 5188 } 5189 mScrollbarPref = aPref; 5190 auto* ps = GetPresShell(); 5191 if (!ps) { 5192 return; 5193 } 5194 nsIFrame* rootScrollContainerFrame = ps->GetRootScrollContainerFrame(); 5195 if (!rootScrollContainerFrame) { 5196 return; 5197 } 5198 ps->FrameNeedsReflow(rootScrollContainerFrame, 5199 IntrinsicDirty::FrameAncestorsAndDescendants, 5200 NS_FRAME_IS_DIRTY); 5201 } 5202 5203 //***************************************************************************** 5204 // nsDocShell::nsIRefreshURI 5205 //***************************************************************************** 5206 5207 NS_IMETHODIMP 5208 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, 5209 uint32_t aDelay) { 5210 MOZ_ASSERT(!mIsBeingDestroyed); 5211 5212 NS_ENSURE_ARG(aURI); 5213 5214 /* Check if Meta refresh/redirects are permitted. Some 5215 * embedded applications may not want to do this. 5216 * Must do this before sending out NOTIFY_REFRESH events 5217 * because listeners may have side effects (e.g. displaying a 5218 * button to manually trigger the refresh later). 5219 */ 5220 bool allowRedirects = true; 5221 GetAllowMetaRedirects(&allowRedirects); 5222 if (!allowRedirects) { 5223 return NS_OK; 5224 } 5225 5226 // If any web progress listeners are listening for NOTIFY_REFRESH events, 5227 // give them a chance to block this refresh. 5228 bool sameURI; 5229 nsresult rv = aURI->Equals(mCurrentURI, &sameURI); 5230 if (NS_FAILED(rv)) { 5231 sameURI = false; 5232 } 5233 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) { 5234 return NS_OK; 5235 } 5236 5237 nsCOMPtr<nsITimerCallback> refreshTimer = 5238 new nsRefreshTimer(this, aURI, aPrincipal, aDelay); 5239 5240 BusyFlags busyFlags = GetBusyFlags(); 5241 5242 if (!mRefreshURIList) { 5243 mRefreshURIList = nsArray::Create(); 5244 } 5245 5246 if (busyFlags & BUSY_FLAGS_BUSY || 5247 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) { 5248 // We don't want to create the timer right now. Instead queue up the 5249 // request and trigger the timer in EndPageLoad() or whenever we become 5250 // active. 5251 mRefreshURIList->AppendElement(refreshTimer); 5252 } else { 5253 // There is no page loading going on right now. Create the 5254 // timer and fire it right away. 5255 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow(); 5256 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE); 5257 5258 nsCOMPtr<nsITimer> timer = MOZ_TRY( 5259 NS_NewTimerWithCallback(refreshTimer, aDelay, nsITimer::TYPE_ONE_SHOT)); 5260 5261 mRefreshURIList->AppendElement(timer); // owning timer ref 5262 } 5263 return NS_OK; 5264 } 5265 5266 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI, 5267 nsIPrincipal* aPrincipal, 5268 uint32_t aDelay, 5269 nsITimer* aTimer) { 5270 MOZ_ASSERT(aTimer, "Must have a timer here"); 5271 5272 // Remove aTimer from mRefreshURIList if needed 5273 if (mRefreshURIList) { 5274 uint32_t n = 0; 5275 mRefreshURIList->GetLength(&n); 5276 5277 for (uint32_t i = 0; i < n; ++i) { 5278 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i); 5279 if (timer == aTimer) { 5280 mRefreshURIList->RemoveElementAt(i); 5281 break; 5282 } 5283 } 5284 } 5285 5286 return ForceRefreshURI(aURI, aPrincipal, aDelay); 5287 } 5288 5289 NS_IMETHODIMP 5290 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal, 5291 uint32_t aDelay) { 5292 NS_ENSURE_ARG(aURI); 5293 5294 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI); 5295 loadState->SetOriginalURI(mCurrentURI); 5296 loadState->SetResultPrincipalURI(aURI); 5297 loadState->SetResultPrincipalURIIsSome(true); 5298 loadState->SetKeepResultPrincipalURIIfSet(true); 5299 loadState->SetIsMetaRefresh(true); 5300 5301 RefPtr<Document> doc = GetDocument(); 5302 NS_ENSURE_STATE(doc); 5303 5304 // Set the triggering pricipal to aPrincipal if available, or current 5305 // document's principal otherwise. 5306 nsCOMPtr<nsIPrincipal> principal = aPrincipal; 5307 if (!principal) { 5308 principal = doc->NodePrincipal(); 5309 } 5310 loadState->SetTriggeringPrincipal(principal); 5311 loadState->SetPolicyContainer(doc->GetPolicyContainer()); 5312 loadState->SetHasValidUserGestureActivation( 5313 doc->HasValidTransientUserGestureActivation()); 5314 5315 loadState->SetTextDirectiveUserActivation( 5316 doc->ConsumeTextDirectiveUserActivation() || 5317 loadState->HasValidUserGestureActivation()); 5318 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags()); 5319 loadState->SetTriggeringWindowId(doc->InnerWindowID()); 5320 loadState->SetTriggeringStorageAccess(doc->UsingStorageAccess()); 5321 loadState->SetTriggeringClassificationFlags(doc->GetScriptTrackingFlags()); 5322 5323 loadState->SetPrincipalIsExplicit(true); 5324 5325 /* Check if this META refresh causes a redirection 5326 * to another site. 5327 */ 5328 bool equalUri = false; 5329 nsresult rv = aURI->Equals(mCurrentURI, &equalUri); 5330 5331 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) { 5332 /* It is a META refresh based redirection within the threshold time 5333 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER). 5334 * Pass a REPLACE flag to LoadURI(). 5335 */ 5336 loadState->SetLoadType(LOAD_REFRESH_REPLACE); 5337 } else { 5338 loadState->SetLoadType(LOAD_REFRESH); 5339 } 5340 5341 const bool sendReferrer = StaticPrefs::network_http_referer_sendFromRefresh(); 5342 /* The document's referrer policy is needed instead of mReferrerInfo's 5343 * referrer policy. 5344 */ 5345 const nsCOMPtr<nsIReferrerInfo> referrerInfo = 5346 new ReferrerInfo(*doc, sendReferrer); 5347 /* We mimic HTTP, which passes the original referrer. See step 3 of 5348 * <https://html.spec.whatwg.org/multipage/browsing-the-web.html#create-navigation-params-by-fetching>. 5349 */ 5350 loadState->SetReferrerInfo(referrerInfo); 5351 5352 loadState->SetLoadFlags( 5353 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL); 5354 loadState->SetFirstParty(true); 5355 5356 /* 5357 * LoadURI(...) will cancel all refresh timers... This causes the 5358 * Timer and its refreshData instance to be released... 5359 */ 5360 LoadURI(loadState, false); 5361 5362 return NS_OK; 5363 } 5364 5365 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart, 5366 const char16_t* aEnd) { 5367 const char16_t* iter = aStart; 5368 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) { 5369 ++iter; 5370 } 5371 return iter; 5372 } 5373 5374 static std::tuple<const char16_t*, const char16_t*> ExtractURLString( 5375 const char16_t* aPosition, const char16_t* aEnd) { 5376 MOZ_ASSERT(aPosition != aEnd); 5377 5378 // 1. Let urlString be the substring of input from the code point at 5379 // position to the end of the string. 5380 const char16_t* urlStart = aPosition; 5381 const char16_t* urlEnd = aEnd; 5382 5383 // 2. If the code point in input pointed to by position is U+0055 (U) or 5384 // U+0075 (u), then advance position to the next code point. 5385 // Otherwise, jump to the step labeled skip quotes. 5386 if (*aPosition == 'U' || *aPosition == 'u') { 5387 ++aPosition; 5388 5389 // 3. If the code point in input pointed to by position is U+0052 (R) or 5390 // U+0072 (r), then advance position to the next code point. 5391 // Otherwise, jump to the step labeled parse. 5392 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) { 5393 return std::make_tuple(urlStart, urlEnd); 5394 } 5395 5396 ++aPosition; 5397 5398 // 4. If the code point in input pointed to by position is U+004C (L) or 5399 // U+006C (l), then advance position to the next code point. 5400 // Otherwise, jump to the step labeled parse. 5401 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) { 5402 return std::make_tuple(urlStart, urlEnd); 5403 } 5404 5405 ++aPosition; 5406 5407 // 5. Skip ASCII whitespace within input given position. 5408 aPosition = SkipASCIIWhitespace(aPosition, aEnd); 5409 5410 // 6. If the code point in input pointed to by position is U+003D (=), 5411 // then advance position to the next code point. Otherwise, jump to 5412 // the step labeled parse. 5413 if (aPosition == aEnd || *aPosition != '=') { 5414 return std::make_tuple(urlStart, urlEnd); 5415 } 5416 5417 ++aPosition; 5418 5419 // 7. Skip ASCII whitespace within input given position. 5420 aPosition = SkipASCIIWhitespace(aPosition, aEnd); 5421 } 5422 5423 // 8. Skip quotes: If the code point in input pointed to by position is 5424 // U+0027 (') or U+0022 ("), then let quote be that code point, and 5425 // advance position to the next code point. Otherwise, let quote be 5426 // the empty string. 5427 Maybe<char> quote; 5428 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) { 5429 quote.emplace(*aPosition); 5430 ++aPosition; 5431 } 5432 5433 // 9. Set urlString to the substring of input from the code point at 5434 // position to the end of the string. 5435 urlStart = aPosition; 5436 urlEnd = aEnd; 5437 5438 // 10. If quote is not the empty string, and there is a code point in 5439 // urlString equal to quote, then truncate urlString at that code 5440 // point, so that it and all subsequent code points are removed. 5441 const char16_t* quotePos; 5442 if (quote.isSome() && 5443 (quotePos = nsCharTraits<char16_t>::find( 5444 urlStart, std::distance(urlStart, aEnd), quote.value()))) { 5445 urlEnd = quotePos; 5446 } 5447 5448 return std::make_tuple(urlStart, urlEnd); 5449 } 5450 5451 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument, 5452 const nsAString& aHeader) { 5453 if (mIsBeingDestroyed) { 5454 return; 5455 } 5456 5457 const char16_t* position = aHeader.BeginReading(); 5458 const char16_t* end = aHeader.EndReading(); 5459 5460 // See https://html.spec.whatwg.org/#shared-declarative-refresh-steps. 5461 5462 // 3. Skip ASCII whitespace 5463 position = SkipASCIIWhitespace(position, end); 5464 5465 // 4. Let time be 0. 5466 CheckedInt<uint32_t> milliSeconds; 5467 5468 // 5. Collect a sequence of code points that are ASCII digits 5469 const char16_t* digitsStart = position; 5470 while (position != end && mozilla::IsAsciiDigit(*position)) { 5471 ++position; 5472 } 5473 5474 if (position == digitsStart) { 5475 // 6. If timeString is the empty string, then: 5476 // 1. If the code point in input pointed to by position is not U+002E 5477 // (.), then return. 5478 if (position == end || *position != '.') { 5479 return; 5480 } 5481 } else { 5482 // 7. Otherwise, set time to the result of parsing timeString using the 5483 // rules for parsing non-negative integers. 5484 nsContentUtils::ParseHTMLIntegerResultFlags result; 5485 uint32_t seconds = 5486 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result); 5487 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative)); 5488 if (result & nsContentUtils::eParseHTMLInteger_Error) { 5489 // The spec assumes no errors here (since we only pass ASCII digits in), 5490 // but we can still overflow, so this block should deal with that (and 5491 // only that). 5492 MOZ_ASSERT( 5493 !(result & ~(nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput | 5494 nsContentUtils::eParseHTMLInteger_Error | 5495 nsContentUtils::eParseHTMLInteger_ErrorOverflow))); 5496 return; 5497 } 5498 MOZ_ASSERT( 5499 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput)); 5500 5501 milliSeconds = seconds; 5502 milliSeconds *= 1000; 5503 if (!milliSeconds.isValid()) { 5504 return; 5505 } 5506 } 5507 5508 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL 5509 // STOP characters (.) from input given position. Ignore any collected 5510 // characters. 5511 while (position != end && 5512 (mozilla::IsAsciiDigit(*position) || *position == '.')) { 5513 ++position; 5514 } 5515 5516 // 9. Let urlRecord be document's URL. 5517 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI()); 5518 5519 // 10. If position is not past the end of input 5520 if (position != end) { 5521 // 1. If the code point in input pointed to by position is not U+003B (;), 5522 // U+002C (,), or ASCII whitespace, then return. 5523 if (*position != ';' && *position != ',' && 5524 !mozilla::IsAsciiWhitespace(*position)) { 5525 return; 5526 } 5527 5528 // 2. Skip ASCII whitespace within input given position. 5529 position = SkipASCIIWhitespace(position, end); 5530 5531 // 3. If the code point in input pointed to by position is U+003B (;) or 5532 // U+002C (,), then advance position to the next code point. 5533 if (position != end && (*position == ';' || *position == ',')) { 5534 ++position; 5535 5536 // 4. Skip ASCII whitespace within input given position. 5537 position = SkipASCIIWhitespace(position, end); 5538 } 5539 5540 // 11. If position is not past the end of input, then: 5541 if (position != end) { 5542 const char16_t* urlStart; 5543 const char16_t* urlEnd; 5544 5545 // 1-10. See ExtractURLString. 5546 std::tie(urlStart, urlEnd) = ExtractURLString(position, end); 5547 5548 // 11. Parse: Parse urlString relative to document. If that fails, return. 5549 // Otherwise, set urlRecord to the resulting URL record. 5550 nsresult rv = 5551 NS_NewURI(getter_AddRefs(urlRecord), 5552 Substring(urlStart, std::distance(urlStart, urlEnd)), 5553 /* charset = */ nullptr, aDocument->GetDocBaseURI()); 5554 NS_ENSURE_SUCCESS_VOID(rv); 5555 } 5556 } 5557 5558 nsIPrincipal* principal = aDocument->NodePrincipal(); 5559 nsCOMPtr<nsIScriptSecurityManager> securityManager = 5560 nsContentUtils::GetSecurityManager(); 5561 nsresult rv = securityManager->CheckLoadURIWithPrincipal( 5562 principal, urlRecord, 5563 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT, 5564 aDocument->InnerWindowID()); 5565 NS_ENSURE_SUCCESS_VOID(rv); 5566 5567 bool isjs = true; 5568 rv = NS_URIChainHasFlags( 5569 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs); 5570 NS_ENSURE_SUCCESS_VOID(rv); 5571 5572 if (isjs) { 5573 return; 5574 } 5575 5576 RefreshURI(urlRecord, principal, milliSeconds.value()); 5577 } 5578 5579 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) { 5580 if (!aTimerList) { 5581 return; 5582 } 5583 5584 uint32_t n = 0; 5585 aTimerList->GetLength(&n); 5586 5587 while (n) { 5588 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n)); 5589 5590 aTimerList->RemoveElementAt(n); // bye bye owning timer ref 5591 5592 if (timer) { 5593 timer->Cancel(); 5594 } 5595 } 5596 } 5597 5598 NS_IMETHODIMP 5599 nsDocShell::CancelRefreshURITimers() { 5600 DoCancelRefreshURITimers(mRefreshURIList); 5601 DoCancelRefreshURITimers(mSavedRefreshURIList); 5602 DoCancelRefreshURITimers(mBFCachedRefreshURIList); 5603 mRefreshURIList = nullptr; 5604 mSavedRefreshURIList = nullptr; 5605 mBFCachedRefreshURIList = nullptr; 5606 5607 return NS_OK; 5608 } 5609 5610 NS_IMETHODIMP 5611 nsDocShell::GetRefreshPending(bool* aResult) { 5612 if (!mRefreshURIList) { 5613 *aResult = false; 5614 return NS_OK; 5615 } 5616 5617 uint32_t count; 5618 nsresult rv = mRefreshURIList->GetLength(&count); 5619 if (NS_SUCCEEDED(rv)) { 5620 *aResult = (count != 0); 5621 } 5622 return rv; 5623 } 5624 5625 void nsDocShell::RefreshURIToQueue() { 5626 if (mRefreshURIList) { 5627 uint32_t n = 0; 5628 mRefreshURIList->GetLength(&n); 5629 5630 for (uint32_t i = 0; i < n; ++i) { 5631 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i); 5632 if (!timer) { 5633 continue; // this must be a nsRefreshURI already 5634 } 5635 5636 // Replace this timer object with a nsRefreshTimer object. 5637 nsCOMPtr<nsITimerCallback> callback; 5638 timer->GetCallback(getter_AddRefs(callback)); 5639 5640 timer->Cancel(); 5641 5642 mRefreshURIList->ReplaceElementAt(callback, i); 5643 } 5644 } 5645 } 5646 5647 NS_IMETHODIMP 5648 nsDocShell::SuspendRefreshURIs() { 5649 RefreshURIToQueue(); 5650 5651 // Suspend refresh URIs for our child shells as well. 5652 for (auto* child : mChildList.ForwardRange()) { 5653 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child); 5654 if (shell) { 5655 shell->SuspendRefreshURIs(); 5656 } 5657 } 5658 5659 return NS_OK; 5660 } 5661 5662 NS_IMETHODIMP 5663 nsDocShell::ResumeRefreshURIs() { 5664 RefreshURIFromQueue(); 5665 5666 // Resume refresh URIs for our child shells as well. 5667 for (auto* child : mChildList.ForwardRange()) { 5668 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child); 5669 if (shell) { 5670 shell->ResumeRefreshURIs(); 5671 } 5672 } 5673 5674 return NS_OK; 5675 } 5676 5677 nsresult nsDocShell::RefreshURIFromQueue() { 5678 if (!mRefreshURIList) { 5679 return NS_OK; 5680 } 5681 uint32_t n = 0; 5682 mRefreshURIList->GetLength(&n); 5683 5684 while (n) { 5685 nsCOMPtr<nsITimerCallback> refreshInfo = 5686 do_QueryElementAt(mRefreshURIList, --n); 5687 5688 if (refreshInfo) { 5689 // This is the nsRefreshTimer object, waiting to be 5690 // setup in a timer object and fired. 5691 // Create the timer and trigger it. 5692 uint32_t delay = static_cast<nsRefreshTimer*>( 5693 static_cast<nsITimerCallback*>(refreshInfo)) 5694 ->GetDelay(); 5695 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow(); 5696 if (win) { 5697 nsCOMPtr<nsITimer> timer; 5698 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay, 5699 nsITimer::TYPE_ONE_SHOT); 5700 5701 if (timer) { 5702 // Replace the nsRefreshTimer element in the queue with 5703 // its corresponding timer object, so that in case another 5704 // load comes through before the timer can go off, the timer will 5705 // get cancelled in CancelRefreshURITimer() 5706 mRefreshURIList->ReplaceElementAt(timer, n); 5707 } 5708 } 5709 } 5710 } 5711 5712 return NS_OK; 5713 } 5714 5715 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) { 5716 nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest); 5717 bool firstPart = false; 5718 return multiPartChannel && 5719 NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) && 5720 !firstPart; 5721 } 5722 5723 nsresult nsDocShell::Embed(nsIDocumentViewer* aDocumentViewer, 5724 WindowGlobalChild* aWindowActor, 5725 bool aIsTransientAboutBlank, nsIRequest* aRequest, 5726 nsIURI* aPreviousURI) { 5727 // Save the LayoutHistoryState of the previous document, before 5728 // setting up new document 5729 PersistLayoutHistoryState(); 5730 5731 nsresult rv = SetupNewViewer(aDocumentViewer, aWindowActor); 5732 NS_ENSURE_SUCCESS(rv, rv); 5733 5734 // XXX What if SetupNewViewer fails? 5735 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) { 5736 // Set history.state 5737 SetDocCurrentStateObj(mLSHE, 5738 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr); 5739 } 5740 5741 if (mLSHE) { 5742 // Restore the editing state, if it's stored in session history. 5743 if (mLSHE->HasDetachedEditor()) { 5744 ReattachEditorToWindow(mLSHE); 5745 } 5746 5747 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE)); 5748 } 5749 5750 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() && 5751 !IsFollowupPartOfMultipart(aRequest)) { 5752 bool expired = false; 5753 uint32_t cacheKey = 0; 5754 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest); 5755 if (cacheChannel) { 5756 // Check if the page has expired from cache 5757 uint32_t expTime = 0; 5758 cacheChannel->GetCacheTokenExpirationTime(&expTime); 5759 uint32_t now = PRTimeToSeconds(PR_Now()); 5760 if (expTime <= now) { 5761 expired = true; 5762 } 5763 5764 // The checks for updating cache key are similar to the old session 5765 // history in OnNewURI. Try to update the cache key if 5766 // - we should update session history and aren't doing a session 5767 // history load. 5768 // - we're doing a forced reload. 5769 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) && 5770 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) || 5771 IsForceReloadType(mLoadType)) { 5772 cacheChannel->GetCacheKey(&cacheKey); 5773 } 5774 } 5775 5776 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this)); 5777 MoveLoadingToActiveEntry(expired, cacheKey, aPreviousURI); 5778 } 5779 5780 bool updateHistory = true; 5781 5782 // Determine if this type of load should update history 5783 switch (mLoadType) { 5784 case LOAD_NORMAL_REPLACE: 5785 case LOAD_REFRESH_REPLACE: 5786 case LOAD_STOP_CONTENT_AND_REPLACE: 5787 case LOAD_RELOAD_BYPASS_CACHE: 5788 case LOAD_RELOAD_BYPASS_PROXY: 5789 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: 5790 case LOAD_REPLACE_BYPASS_CACHE: 5791 updateHistory = false; 5792 break; 5793 default: 5794 break; 5795 } 5796 5797 if (!updateHistory) { 5798 SetLayoutHistoryState(nullptr); 5799 } 5800 5801 return NS_OK; 5802 } 5803 5804 //***************************************************************************** 5805 // nsDocShell::nsIWebProgressListener 5806 //***************************************************************************** 5807 5808 NS_IMETHODIMP 5809 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest, 5810 int32_t aCurSelfProgress, int32_t aMaxSelfProgress, 5811 int32_t aCurTotalProgress, 5812 int32_t aMaxTotalProgress) { 5813 // Listeners in the parent process only care about aCurTotalProgress and 5814 // aMaxTotalProgress, which is internally managed by nsDocLoader. Because of 5815 // this, we don't send progress notifications except when they are recorded by 5816 // the toplevel context, and only report them on the toplevel context in the 5817 // parent process. 5818 // 5819 // FIXME: We should track progress for out-of-process iframes and manage total 5820 // progress in the parent process for more accurate notifications. 5821 MOZ_ASSERT( 5822 mBrowsingContext->IsTop(), 5823 "notification excluded in AddProgressListener(...) for non-toplevel BCs"); 5824 5825 if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) { 5826 listener->OnProgressChange(aProgress, aRequest, aCurSelfProgress, 5827 aMaxSelfProgress, aCurTotalProgress, 5828 aMaxTotalProgress); 5829 } 5830 5831 return NS_OK; 5832 } 5833 5834 NS_IMETHODIMP 5835 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest, 5836 uint32_t aStateFlags, nsresult aStatus) { 5837 // If we're receiving a notification on ourselves which has at least one of 5838 // the state change flags in kStateChangeFlagFilter, also notify WebProgress 5839 // on BrowsingContextWebProgress, potentially over IPC. 5840 // 5841 // NOTE: We don't notify for bubbled notifications (aProgress != this), as 5842 // BrowsingContextWebProgress independently handles event bubbling in the 5843 // parent process. 5844 // 5845 // NOTE: We don't filter notifications when registering our listener, as 5846 // `STATE_IS_REDIRECTED_DOCUMENT` cannot be filtered for at registration time. 5847 static constexpr uint32_t kStateChangeFlagFilter = 5848 STATE_IS_NETWORK | STATE_IS_DOCUMENT | STATE_IS_WINDOW | 5849 STATE_IS_REDIRECTED_DOCUMENT; 5850 if (aProgress == this && (aStateFlags & kStateChangeFlagFilter) != 0) { 5851 if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) { 5852 listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus); 5853 } 5854 } 5855 5856 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) { 5857 // Save timing statistics. 5858 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); 5859 nsCOMPtr<nsIURI> uri; 5860 channel->GetURI(getter_AddRefs(uri)); 5861 nsAutoCString aURI; 5862 uri->GetAsciiSpec(aURI); 5863 5864 if (this == aProgress) { 5865 (void)MaybeInitTiming(); 5866 mTiming->NotifyFetchStart(uri, 5867 ConvertLoadTypeToNavigationType(mLoadType)); 5868 // If we are starting a DocumentChannel, we need to pass the timing 5869 // statistics so that should a process switch occur, the starting type can 5870 // be passed to the new DocShell running in the other content process. 5871 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) { 5872 docChannel->SetNavigationTiming(mTiming); 5873 } 5874 } 5875 5876 // Page has begun to load 5877 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD); 5878 5879 if ((aStateFlags & STATE_RESTORING) == 0) { 5880 if (SessionStorePlatformCollection()) { 5881 if (IsForceReloadType(mLoadType)) { 5882 if (WindowContext* windowContext = 5883 mBrowsingContext->GetCurrentWindowContext()) { 5884 SessionStoreChild::From(windowContext->GetWindowGlobalChild()) 5885 ->ResetSessionStore(mBrowsingContext, 5886 mBrowsingContext->GetSessionStoreEpoch()); 5887 } 5888 } 5889 } 5890 } 5891 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) { 5892 // Page is loading 5893 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING); 5894 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) { 5895 // Page has finished loading 5896 mBusyFlags = BUSY_FLAGS_NONE; 5897 } 5898 5899 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) { 5900 nsCOMPtr<nsIWebProgress> webProgress = 5901 do_QueryInterface(GetAsSupports(this)); 5902 // Is the document stop notification for this document? 5903 if (aProgress == webProgress.get()) { 5904 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest)); 5905 EndPageLoad(aProgress, channel, aStatus); 5906 } 5907 } 5908 // note that redirect state changes will go through here as well, but it 5909 // is better to handle those in OnRedirectStateChange where more 5910 // information is available. 5911 return NS_OK; 5912 } 5913 5914 NS_IMETHODIMP 5915 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest, 5916 nsIURI* aURI, uint32_t aFlags) { 5917 // If we're receiving a notification on ourselves, also notify WebProgress on 5918 // BrowsingContextWebProgress, potentially over IPC. 5919 // 5920 // NOTE: We don't notify for bubbled notifications (aProgress != this), as 5921 // BrowsingContextWebProgress independently handles event bubbling in the 5922 // parent process. 5923 // 5924 // NOTE: Tests depend on this happening before UpdateSecurityState. 5925 if (aProgress == this) { 5926 if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) { 5927 listener->OnLocationChange(aProgress, aRequest, aURI, aFlags); 5928 } 5929 } 5930 5931 // Since we've now changed Documents, notify the BrowsingContext that we've 5932 // changed. Ideally we'd just let the BrowsingContext do this when it 5933 // changes the current window global, but that happens before this and we 5934 // have a lot of tests that depend on the specific ordering of messages. 5935 bool isTopLevel = false; 5936 if (XRE_IsParentProcess() && 5937 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) && 5938 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) { 5939 GetBrowsingContext()->Canonical()->UpdateSecurityState(); 5940 } 5941 return NS_OK; 5942 } 5943 5944 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel, 5945 nsIChannel* aNewChannel, 5946 uint32_t aRedirectFlags, 5947 uint32_t aStateFlags) { 5948 NS_ASSERTION(aStateFlags & STATE_REDIRECTING, 5949 "Calling OnRedirectStateChange when there is no redirect"); 5950 5951 if (!(aStateFlags & STATE_IS_DOCUMENT)) { 5952 return; // not a toplevel document 5953 } 5954 5955 nsCOMPtr<nsIURI> oldURI, newURI; 5956 aOldChannel->GetURI(getter_AddRefs(oldURI)); 5957 aNewChannel->GetURI(getter_AddRefs(newURI)); 5958 if (!oldURI || !newURI) { 5959 return; 5960 } 5961 5962 // DocumentChannel adds redirect chain to global history in the parent 5963 // process. The redirect chain can't be queried from the content process, so 5964 // there's no need to update global history here. 5965 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel); 5966 if (!docChannel) { 5967 // Below a URI visit is saved (see AddURIVisit method doc). 5968 // The visit chain looks something like: 5969 // ... 5970 // Site N - 1 5971 // => Site N 5972 // (redirect to =>) Site N + 1 (we are here!) 5973 5974 // Get N - 1 and transition type 5975 nsCOMPtr<nsIURI> previousURI; 5976 uint32_t previousFlags = 0; 5977 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags); 5978 5979 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL || 5980 net::ChannelIsPost(aOldChannel)) { 5981 // 1. Internal redirects are ignored because they are specific to the 5982 // channel implementation. 5983 // 2. POSTs are not saved by global history. 5984 // 5985 // Regardless, we need to propagate the previous visit to the new 5986 // channel. 5987 SaveLastVisit(aNewChannel, previousURI, previousFlags); 5988 } else { 5989 // Get the HTTP response code, if available. 5990 uint32_t responseStatus = 0; 5991 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel); 5992 if (httpChannel) { 5993 (void)httpChannel->GetResponseStatus(&responseStatus); 5994 } 5995 5996 // Add visit N -1 => N 5997 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus); 5998 5999 // Since N + 1 could be the final destination, we will not save N => N + 1 6000 // here. OnNewURI will do that, so we will cache it. 6001 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags); 6002 } 6003 } 6004 6005 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) && 6006 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) { 6007 mLoadType = LOAD_NORMAL_REPLACE; 6008 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing()); 6009 } 6010 } 6011 6012 NS_IMETHODIMP 6013 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 6014 nsresult aStatus, const char16_t* aMessage) { 6015 // If we're receiving a notification on ourselves, also notify WebProgress on 6016 // BrowsingContextWebProgress, potentially over IPC. 6017 // 6018 // NOTE: We don't notify for bubbled notifications (aWebProgress != this), as 6019 // BrowsingContextWebProgress independently handles event bubbling in the 6020 // parent process. 6021 if (aWebProgress == this) { 6022 if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) { 6023 listener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage); 6024 } 6025 } 6026 6027 return NS_OK; 6028 } 6029 6030 NS_IMETHODIMP 6031 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest, 6032 uint32_t aState) { 6033 // If we're receiving a notification on ourselves, also notify WebProgress on 6034 // BrowsingContextWebProgress, potentially over IPC. 6035 // 6036 // NOTE: We don't notify for bubbled notifications (aWebProgress != this), as 6037 // BrowsingContextWebProgress independently handles event bubbling in the 6038 // parent process. 6039 if (aWebProgress == this) { 6040 if (nsCOMPtr<nsIWebProgressListener> listener = BCWebProgressListener()) { 6041 listener->OnSecurityChange(aWebProgress, aRequest, aState); 6042 } 6043 } 6044 6045 return NS_OK; 6046 } 6047 6048 NS_IMETHODIMP 6049 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress, 6050 nsIRequest* aRequest, uint32_t aEvent) { 6051 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)"); 6052 return NS_OK; 6053 } 6054 6055 already_AddRefed<nsIWebProgressListener> nsDocShell::BCWebProgressListener() { 6056 // If this BrowsingContext has been replaced, we should discard any 6057 // notifications which would otherwise be delivered in-process. 6058 if (XRE_IsParentProcess() && mBrowsingContext->Canonical()->IsReplaced()) { 6059 return nullptr; 6060 } 6061 6062 // Create a nsBrowserStatusFilter to perform some throttling of 6063 // OnProgressChange and OnStatusChange notifications which are delivered to 6064 // our BCWebProgress listener. This reduces the amount of IPC traffic. 6065 if (!mBCWebProgressStatusFilter && !mIsBeingDestroyed) { 6066 nsCOMPtr<nsIWebProgressListener> innerListener; 6067 if (XRE_IsParentProcess()) { 6068 innerListener = mBrowsingContext->Canonical()->GetWebProgress(); 6069 } else { 6070 innerListener = do_QueryReferent(mBrowserChild); 6071 } 6072 if (innerListener) { 6073 // NOTE: We need to disable filtering of StateChange events here, as 6074 // listeners on BrowsingContextWebProgress may depend on state change 6075 // notifications are otherwise filtered. 6076 // NOTE: Unlike other nsIWebProgress types, nsBrowserStatusFilter holds a 6077 // strong cycle-collected reference to the inner listener. 6078 mBCWebProgressStatusFilter = 6079 new nsBrowserStatusFilter(/* aDisableStateChangeFilters */ true); 6080 mBCWebProgressStatusFilter->AddProgressListener( 6081 innerListener, nsIWebProgress::NOTIFY_ALL); 6082 } 6083 } 6084 6085 return do_AddRef(mBCWebProgressStatusFilter); 6086 } 6087 6088 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI( 6089 const nsACString& aKeyword, bool aIsPrivateContext) { 6090 nsCOMPtr<nsIURIFixupInfo> info; 6091 if (!XRE_IsContentProcess()) { 6092 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service(); 6093 if (uriFixup) { 6094 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info)); 6095 } 6096 } 6097 return info.forget(); 6098 } 6099 6100 /* static */ 6101 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI( 6102 nsIChannel* aChannel, nsIURI* aUrl) { 6103 if (!aChannel) { 6104 return nullptr; 6105 } 6106 6107 nsresult rv = NS_OK; 6108 nsAutoCString host; 6109 rv = aUrl->GetAsciiHost(host); 6110 if (NS_WARN_IF(NS_FAILED(rv))) { 6111 return nullptr; 6112 } 6113 6114 // Return if fixup enable pref is turned off. 6115 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) { 6116 return nullptr; 6117 } 6118 6119 // Return if scheme is not HTTPS. 6120 if (!aUrl->SchemeIs("https")) { 6121 return nullptr; 6122 } 6123 6124 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo(); 6125 if (!info) { 6126 return nullptr; 6127 } 6128 6129 // Skip doing the fixup if our channel was redirected, because we 6130 // shouldn't be guessing things about the post-redirect URI. 6131 if (!info->RedirectChain().IsEmpty()) { 6132 return nullptr; 6133 } 6134 6135 int32_t port = 0; 6136 rv = aUrl->GetPort(&port); 6137 if (NS_WARN_IF(NS_FAILED(rv))) { 6138 return nullptr; 6139 } 6140 6141 // Don't fix up hosts with ports. 6142 if (port != -1) { 6143 return nullptr; 6144 } 6145 6146 // Don't fix up localhost url. 6147 if (host == "localhost") { 6148 return nullptr; 6149 } 6150 6151 // Don't fix up hostnames with IP address. 6152 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) { 6153 return nullptr; 6154 } 6155 6156 nsAutoCString userPass; 6157 rv = aUrl->GetUserPass(userPass); 6158 if (NS_WARN_IF(NS_FAILED(rv))) { 6159 return nullptr; 6160 } 6161 6162 // Security - URLs with user / password info should NOT be modified. 6163 if (!userPass.IsEmpty()) { 6164 return nullptr; 6165 } 6166 6167 nsCOMPtr<nsITransportSecurityInfo> tsi; 6168 rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi)); 6169 if (NS_WARN_IF(NS_FAILED(rv))) { 6170 return nullptr; 6171 } 6172 6173 if (NS_WARN_IF(!tsi)) { 6174 return nullptr; 6175 } 6176 6177 nsCOMPtr<nsIX509Cert> cert; 6178 rv = tsi->GetServerCert(getter_AddRefs(cert)); 6179 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) { 6180 return nullptr; 6181 } 6182 6183 nsTArray<uint8_t> certBytes; 6184 rv = cert->GetRawDER(certBytes); 6185 if (NS_FAILED(rv)) { 6186 return nullptr; 6187 } 6188 6189 mozilla::pkix::Input serverCertInput; 6190 mozilla::pkix::Result result = 6191 serverCertInput.Init(certBytes.Elements(), certBytes.Length()); 6192 if (result != mozilla::pkix::Success) { 6193 return nullptr; 6194 } 6195 6196 constexpr auto wwwPrefix = "www."_ns; 6197 nsAutoCString newHost; 6198 if (StringBeginsWith(host, wwwPrefix)) { 6199 // Try www.example.com -> example.com 6200 newHost.Assign(Substring(host, wwwPrefix.Length())); 6201 } else { 6202 // Try example.com -> www.example.com 6203 newHost.Assign(wwwPrefix); 6204 newHost.Append(host); 6205 } 6206 6207 mozilla::pkix::Input newHostInput; 6208 result = newHostInput.Init( 6209 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()), 6210 newHost.Length()); 6211 if (result != mozilla::pkix::Success) { 6212 return nullptr; 6213 } 6214 6215 // Because certificate verification returned Result::ERROR_BAD_CERT_DOMAIN / 6216 // SSL_ERROR_BAD_CERT_DOMAIN, a chain was built and we know whether or not 6217 // the root was a built-in. 6218 bool rootIsBuiltIn; 6219 if (NS_FAILED(tsi->GetIsBuiltCertChainRootBuiltInRoot(&rootIsBuiltIn))) { 6220 return nullptr; 6221 } 6222 mozilla::psm::SkipInvalidSANsForNonBuiltInRootsPolicy nameMatchingPolicy( 6223 rootIsBuiltIn); 6224 6225 // Check if the certificate is valid for the new hostname. 6226 result = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput, 6227 nameMatchingPolicy); 6228 if (result != mozilla::pkix::Success) { 6229 return nullptr; 6230 } 6231 6232 nsCOMPtr<nsIURI> newURI; 6233 (void)NS_MutateURI(aUrl).SetHost(newHost).Finalize(getter_AddRefs(newURI)); 6234 6235 return newURI.forget(); 6236 } 6237 6238 /* static */ 6239 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup( 6240 nsIChannel* aChannel, nsresult aStatus, 6241 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType, 6242 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing, 6243 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData, 6244 nsILoadInfo::SchemelessInputType* outSchemelessInput) { 6245 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET && 6246 aStatus != NS_ERROR_CONNECTION_REFUSED && 6247 aStatus != 6248 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) { 6249 return nullptr; 6250 } 6251 6252 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) { 6253 return nullptr; 6254 } 6255 6256 nsCOMPtr<nsIURI> url; 6257 nsresult rv = aChannel->GetURI(getter_AddRefs(url)); 6258 if (NS_FAILED(rv)) { 6259 return nullptr; 6260 } 6261 6262 // 6263 // Try and make an alternative URI from the old one 6264 // 6265 nsCOMPtr<nsIURI> newURI; 6266 nsCOMPtr<nsIInputStream> newPostData; 6267 6268 nsAutoCString oldSpec; 6269 url->GetSpec(oldSpec); 6270 6271 // 6272 // First try keyword fixup 6273 // 6274 nsAutoString keywordProviderName, keywordAsSent; 6275 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) { 6276 // we should only perform a keyword search under the following 6277 // conditions: 6278 // (0) Pref keyword.enabled is true 6279 // (1) the url scheme is http (or https) 6280 // (2) the url does not have a protocol scheme 6281 // If we don't enforce such a policy, then we end up doing 6282 // keyword searchs on urls we don't intend like imap, file, 6283 // mailbox, etc. This could lead to a security problem where we 6284 // send data to the keyword server that we shouldn't be. 6285 // Someone needs to clean up keywords in general so we can 6286 // determine on a per url basis if we want keywords 6287 // enabled...this is just a bandaid... 6288 if (Preferences::GetBool("keyword.enabled", false) && 6289 net::SchemeIsHttpOrHttps(url)) { 6290 bool attemptFixup = false; 6291 nsAutoCString host; 6292 (void)url->GetHost(host); 6293 if (host.FindChar('.') == kNotFound) { 6294 attemptFixup = true; 6295 } else { 6296 // For domains with dots, we check the public suffix validity. 6297 nsCOMPtr<nsIEffectiveTLDService> tldService = 6298 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); 6299 if (tldService) { 6300 nsAutoCString suffix; 6301 attemptFixup = 6302 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) && 6303 suffix.IsEmpty(); 6304 } 6305 } 6306 if (attemptFixup) { 6307 nsCOMPtr<nsIURIFixupInfo> info; 6308 // only send non-qualified hosts to the keyword server 6309 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) { 6310 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing); 6311 } else { 6312 // 6313 // If this string was passed through nsStandardURL by 6314 // chance, then it may have been converted from UTF-8 to 6315 // Punycode, which would result in a completely bogus keyword 6316 // query. Here we try to recover the original Unicode 6317 // value, but this is not 100% correct since the value may 6318 // have been normalized per the IDN normalization rules. 6319 // 6320 // Since we don't have access to the exact original string 6321 // that was entered by the user, this will just have to do. 6322 nsAutoCString utf8Host; 6323 mozilla_net_recover_keyword_from_punycode(&host, &utf8Host); 6324 info = KeywordToURI(utf8Host, aUsePrivateBrowsing); 6325 } 6326 if (info) { 6327 info->GetPreferredURI(getter_AddRefs(newURI)); 6328 info->GetSchemelessInput(outSchemelessInput); 6329 if (newURI) { 6330 info->GetKeywordAsSent(keywordAsSent); 6331 info->GetKeywordProviderName(keywordProviderName); 6332 info->GetPostData(getter_AddRefs(newPostData)); 6333 } 6334 } 6335 } 6336 } 6337 } 6338 6339 // 6340 // Now try change the address, e.g. turn http://foo into 6341 // http://www.foo.com, and if that doesn't work try https with 6342 // https://foo and https://www.foo.com. 6343 // 6344 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) { 6345 // Skip fixup for anything except a normal document load 6346 // operation on the topframe. 6347 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame; 6348 6349 if (doCreateAlternate) { 6350 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 6351 nsIPrincipal* principal = loadInfo->TriggeringPrincipal(); 6352 // Only do this if our channel was loaded directly by the user from the 6353 // URL bar or similar (system principal) and not redirected, because we 6354 // shouldn't be guessing things about links from other sites, or a 6355 // post-redirect URI. 6356 doCreateAlternate = principal && principal->IsSystemPrincipal() && 6357 loadInfo->RedirectChain().IsEmpty(); 6358 } 6359 // Test if keyword lookup produced a new URI or not 6360 if (doCreateAlternate && newURI) { 6361 bool sameURI = false; 6362 url->Equals(newURI, &sameURI); 6363 if (!sameURI) { 6364 // Keyword lookup made a new URI so no need to try 6365 // an alternate one. 6366 doCreateAlternate = false; 6367 } 6368 } 6369 if (doCreateAlternate) { 6370 newURI = nullptr; 6371 newPostData = nullptr; 6372 keywordProviderName.Truncate(); 6373 keywordAsSent.Truncate(); 6374 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service(); 6375 if (uriFixup) { 6376 nsCOMPtr<nsIURIFixupInfo> fixupInfo; 6377 uriFixup->GetFixupURIInfo(oldSpec, nsIURIFixup::FIXUP_FLAG_NONE, 6378 getter_AddRefs(fixupInfo)); 6379 if (fixupInfo) { 6380 fixupInfo->GetPreferredURI(getter_AddRefs(newURI)); 6381 } 6382 } 6383 } 6384 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED && 6385 Preferences::GetBool("browser.fixup.fallback-to-https", false)) { 6386 // Try HTTPS, since http didn't work 6387 if (url->SchemeIs("http")) { 6388 int32_t port = 0; 6389 url->GetPort(&port); 6390 6391 // Fall back to HTTPS only if port is default 6392 if (port == -1) { 6393 newURI = nullptr; 6394 newPostData = nullptr; 6395 (void)NS_MutateURI(url) 6396 .SetScheme("https"_ns) 6397 .Finalize(getter_AddRefs(newURI)); 6398 } 6399 } 6400 } 6401 6402 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try adding or removing 6403 // "www." to/from the beginning of the domain name to see if we can avoid 6404 // showing the cert error page. For example, https://example.com -> 6405 // https://www.example.com or https://www.example.com -> https://example.com. 6406 if (aStatus == 6407 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) { 6408 newPostData = nullptr; 6409 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url); 6410 } 6411 6412 // Did we make a new URI that is different to the old one? If so 6413 // load it. 6414 // 6415 if (newURI) { 6416 // Make sure the new URI is different from the old one, 6417 // otherwise there's little point trying to load it again. 6418 bool sameURI = false; 6419 url->Equals(newURI, &sameURI); 6420 if (!sameURI) { 6421 if (aNewPostData) { 6422 newPostData.forget(aNewPostData); 6423 } 6424 if (aNotifyKeywordSearchLoading) { 6425 // This notification is meant for Firefox Health Report so it 6426 // can increment counts from the search engine 6427 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent); 6428 } 6429 return newURI.forget(); 6430 } 6431 } 6432 6433 return nullptr; 6434 } 6435 6436 nsresult nsDocShell::FilterStatusForErrorPage( 6437 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType, 6438 bool aIsTopFrame, bool aUseErrorPages, 6439 bool* aSkippedUnknownProtocolNavigation) { 6440 // Errors to be shown only on top-level frames 6441 if ((aStatus == NS_ERROR_UNKNOWN_HOST || 6442 aStatus == NS_ERROR_CONNECTION_REFUSED || 6443 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST || 6444 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED || 6445 aStatus == NS_ERROR_PROXY_FORBIDDEN || 6446 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED || 6447 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED || 6448 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS || 6449 aStatus == NS_ERROR_MALFORMED_URI || 6450 aStatus == NS_ERROR_HARMFULADDON_URI || 6451 aStatus == NS_ERROR_BLOCKED_BY_POLICY || 6452 aStatus == NS_ERROR_DOM_COOP_FAILED || 6453 aStatus == NS_ERROR_DOM_COEP_FAILED || 6454 aStatus == NS_ERROR_DOM_INVALID_HEADER_VALUE) && 6455 (aIsTopFrame || aUseErrorPages)) { 6456 return aStatus; 6457 } 6458 6459 if (aStatus == NS_ERROR_NET_TIMEOUT || 6460 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL || 6461 aStatus == NS_ERROR_NET_EMPTY_RESPONSE || 6462 aStatus == NS_ERROR_NET_ERROR_RESPONSE || 6463 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT || 6464 aStatus == NS_ERROR_REDIRECT_LOOP || 6465 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE || 6466 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET || 6467 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE || 6468 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI || 6469 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI || 6470 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE || 6471 aStatus == NS_ERROR_INTERCEPTION_FAILED || 6472 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY || 6473 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY || 6474 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR || 6475 aStatus == NS_ERROR_BASIC_HTTP_AUTH_DISABLED || 6476 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND || 6477 aStatus == NS_ERROR_FILE_ACCESS_DENIED || 6478 aStatus == NS_ERROR_CORRUPTED_CONTENT || 6479 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING || 6480 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_TOR || 6481 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) { 6482 // Errors to be shown for any frame 6483 return aStatus; 6484 } 6485 6486 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) { 6487 // For unknown protocols we only display an error if the load is triggered 6488 // by the browser itself. Showing the error for page-triggered navigations 6489 // causes annoying behavior for users when a page tries to open an external 6490 // app which has not been installed, see bug 1528305. A missing WebExtension 6491 // protocol handlers will however always load the error page, as it is not 6492 // expected to be opened externally, see bug 1921426. 6493 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo(); 6494 if (!info->TriggeringPrincipal()->IsSystemPrincipal() && 6495 !BasePrincipal::Cast(info->TriggeringPrincipal())->AddonPolicy()) { 6496 if (aSkippedUnknownProtocolNavigation) { 6497 *aSkippedUnknownProtocolNavigation = true; 6498 } 6499 return NS_OK; 6500 } 6501 return aStatus; 6502 } 6503 6504 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) { 6505 // Non-caching channels will simply return NS_ERROR_OFFLINE. 6506 // Caching channels would have to look at their flags to work 6507 // out which error to return. Or we can fix up the error here. 6508 if (!(aLoadType & LOAD_CMD_HISTORY)) { 6509 return NS_ERROR_OFFLINE; 6510 } 6511 return aStatus; 6512 } 6513 6514 return NS_OK; 6515 } 6516 6517 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress, 6518 nsIChannel* aChannel, nsresult aStatus) { 6519 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, 6520 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this, 6521 static_cast<uint32_t>(aStatus))); 6522 if (!aChannel) { 6523 return NS_ERROR_NULL_POINTER; 6524 } 6525 6526 // Make sure to discard the initial client if we never created the initial 6527 // about:blank document. Do this before possibly returning from the method 6528 // due to an error. 6529 mInitialClientSource.reset(); 6530 6531 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel); 6532 if (reporter) { 6533 nsCOMPtr<nsILoadGroup> loadGroup; 6534 aChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 6535 if (loadGroup) { 6536 reporter->FlushConsoleReports(loadGroup); 6537 } else { 6538 reporter->FlushConsoleReports(GetDocument()); 6539 } 6540 } 6541 6542 nsCOMPtr<nsIURI> url; 6543 nsresult rv = aChannel->GetURI(getter_AddRefs(url)); 6544 if (NS_FAILED(rv)) { 6545 return rv; 6546 } 6547 6548 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel); 6549 if (timingChannel) { 6550 TimeStamp channelCreationTime; 6551 rv = timingChannel->GetChannelCreation(&channelCreationTime); 6552 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) { 6553 glean::performance_page::total_content_page_load.AccumulateRawDuration( 6554 TimeStamp::Now() - channelCreationTime); 6555 } 6556 } 6557 6558 // Timing is picked up by the window, we don't need it anymore 6559 mTiming = nullptr; 6560 6561 // clean up reload state for meta charset 6562 if (eCharsetReloadRequested == mCharsetReloadState) { 6563 mCharsetReloadState = eCharsetReloadStopOrigional; 6564 } else { 6565 mCharsetReloadState = eCharsetReloadInit; 6566 } 6567 6568 // Save a pointer to the currently-loading history entry. 6569 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history 6570 // entry further down in this method. 6571 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE; 6572 (void)loadingSHE; 6573 // 6574 // one of many safeguards that prevent death and destruction if 6575 // someone is so very very rude as to bring this window down 6576 // during this load handler. 6577 // 6578 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this); 6579 6580 // Notify the DocumentViewer that the Document has finished loading. This 6581 // will cause any OnLoad(...) and PopState(...) handlers to fire. 6582 if (!mEODForCurrentDocument && mDocumentViewer) { 6583 mIsExecutingOnLoadHandler = true; 6584 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer; 6585 viewer->LoadComplete(aStatus); 6586 mIsExecutingOnLoadHandler = false; 6587 6588 mEODForCurrentDocument = true; 6589 } 6590 /* Check if the httpChannel has any cache-control related response headers, 6591 * like no-store, no-cache. If so, update SHEntry so that 6592 * when a user goes back/forward to this page, we appropriately do 6593 * form value restoration or load from server. 6594 */ 6595 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel)); 6596 if (!httpChannel) { 6597 // HttpChannel could be hiding underneath a Multipart channel. 6598 GetHttpChannel(aChannel, getter_AddRefs(httpChannel)); 6599 } 6600 6601 if (httpChannel) { 6602 // figure out if SH should be saving layout state. 6603 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel); 6604 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) && 6605 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) { 6606 mLSHE->SetSaveLayoutStateFlag(false); 6607 } 6608 } 6609 6610 // Clear mLSHE after calling the onLoadHandlers. This way, if the 6611 // onLoadHandler tries to load something different in 6612 // itself or one of its children, we can deal with it appropriately. 6613 if (mLSHE) { 6614 mLSHE->SetLoadType(LOAD_HISTORY); 6615 6616 // Clear the mLSHE reference to indicate document loading is done one 6617 // way or another. 6618 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing()); 6619 } 6620 mActiveEntryIsLoadingFromSessionHistory = false; 6621 6622 // if there's a refresh header in the channel, this method 6623 // will set it up for us. 6624 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive) 6625 RefreshURIFromQueue(); 6626 6627 // Test whether this is the top frame or a subframe 6628 bool isTopFrame = mBrowsingContext->IsTop(); 6629 6630 bool hadErrorStatus = false; 6631 // If status code indicates an error it means that DocumentChannel already 6632 // tried to fixup the uri and failed. Throw an error dialog box here. 6633 if (NS_FAILED(aStatus)) { 6634 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire 6635 // the error event to our embedder, since tests are relying on this. 6636 // The error event is usually fired by the caller of InternalLoad, but 6637 // this particular error can happen asynchronously. 6638 // Bug 1629201 is filed for having much clearer decision making around 6639 // which cases need error events. 6640 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT || 6641 aStatus == NS_ERROR_CONTENT_BLOCKED); 6642 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent); 6643 6644 bool skippedUnknownProtocolNavigation = false; 6645 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame, 6646 mBrowsingContext->GetUseErrorPages(), 6647 &skippedUnknownProtocolNavigation); 6648 hadErrorStatus = true; 6649 if (NS_FAILED(aStatus)) { 6650 if (!mIsBeingDestroyed) { 6651 DisplayLoadError(aStatus, url, nullptr, aChannel); 6652 } 6653 } else if (skippedUnknownProtocolNavigation) { 6654 nsAutoCString sanitized; 6655 nsTArray<nsString> params; 6656 if (NS_SUCCEEDED(NS_GetSanitizedURIStringFromURI(url, sanitized))) { 6657 params.AppendElement(NS_ConvertUTF8toUTF16(sanitized)); 6658 } else { 6659 params.AppendElement(u"(unknown uri)"_ns); 6660 } 6661 nsContentUtils::ReportToConsole( 6662 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(), 6663 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented", 6664 params); 6665 } 6666 } 6667 6668 if (hadErrorStatus) { 6669 // Don't send session store updates if the reason EndPageLoad was called is 6670 // because we are process switching. Sometimes the update takes too long and 6671 // incorrectly overrides session store data from the following load. 6672 return NS_OK; 6673 } 6674 if (SessionStorePlatformCollection()) { 6675 if (WindowContext* windowContext = 6676 mBrowsingContext->GetCurrentWindowContext()) { 6677 using Change = SessionStoreChangeListener::Change; 6678 6679 // We've finished loading the page and now we want to collect all the 6680 // session store state that the page is initialized with. 6681 SessionStoreChangeListener::CollectSessionStoreData( 6682 windowContext, 6683 EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory, 6684 Change::WireFrame)); 6685 } 6686 } 6687 6688 return NS_OK; 6689 } 6690 6691 //***************************************************************************** 6692 // nsDocShell: Content Viewer Management 6693 //***************************************************************************** 6694 6695 bool nsDocShell::VerifyDocumentViewer() { 6696 if (mDocumentViewer) { 6697 return true; 6698 } 6699 if (mIsBeingDestroyed) { 6700 return false; 6701 } 6702 if (!mInitialized) { 6703 // The viewer should be created during docshell initialization. If something 6704 // wants a viewer or document, it has to initialize the docshell first. 6705 MOZ_ASSERT_UNREACHABLE( 6706 "The docshell should be initialized to get a viewer."); 6707 } else { 6708 NS_WARNING("No document viewer, docshell failed to initialize."); 6709 } 6710 return false; 6711 } 6712 6713 nsresult nsDocShell::CreateInitialDocumentViewer( 6714 nsIOpenWindowInfo* aOpenWindowInfo, 6715 mozilla::dom::WindowGlobalChild* aWindowActor) { 6716 if (mIsBeingDestroyed) { 6717 return NS_ERROR_FAILURE; 6718 } 6719 MOZ_ASSERT(!mDocumentViewer); 6720 MOZ_ASSERT(aOpenWindowInfo, "Why don't we have openwindowinfo?"); 6721 6722 // Previously, CreateDocumentViewerForActor would've used the actor's 6723 // principal. 6724 MOZ_ASSERT_IF(aWindowActor, 6725 aWindowActor->DocumentPrincipal() == 6726 aOpenWindowInfo->PrincipalToInheritForAboutBlank()); 6727 MOZ_ASSERT_IF( 6728 aWindowActor, 6729 aWindowActor->DocumentPrincipal() == 6730 aOpenWindowInfo->PartitionedPrincipalToInheritForAboutBlank()); 6731 6732 nsresult rv = CreateAboutBlankDocumentViewer( 6733 aOpenWindowInfo->PrincipalToInheritForAboutBlank(), 6734 aOpenWindowInfo->PartitionedPrincipalToInheritForAboutBlank(), 6735 aOpenWindowInfo->PolicyContainerToInheritForAboutBlank(), 6736 aOpenWindowInfo->BaseUriToInheritForAboutBlank(), 6737 /* aIsInitialDocument */ true, 6738 aOpenWindowInfo->CoepToInheritForAboutBlank(), 6739 /* aTryToSaveOldPresentation */ true, 6740 /* aCheckPermitUnload */ true, aWindowActor); 6741 6742 NS_ENSURE_STATE(mDocumentViewer); 6743 6744 if (NS_SUCCEEDED(rv)) { 6745 RefPtr<Document> doc(GetDocument()); 6746 MOZ_ASSERT(doc, 6747 "Should have doc if CreateAboutBlankDocumentViewer " 6748 "succeeded!"); 6749 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document"); 6750 6751 // Documents created using CreateInitialDocumentViewer may be transient 6752 // placeholders created by framescripts before content has a 6753 // chance to load. In some cases, window.open(..., "noopener") 6754 // will create such a document and then synchronously tear it 6755 // down, firing a "pagehide" event. Doing so violates our 6756 // assertions about DocGroups. It's easier to silence the 6757 // assertion here than to avoid creating the extra document. 6758 doc->IgnoreDocGroupMismatches(); 6759 } 6760 6761 return rv; 6762 } 6763 6764 // Location.ancestorOrigins for about:blank, need special case handling. 6765 // Particularly in the case for initial about:blank, which does not go through 6766 // normal code that happen with navigations. 6767 static void CreateAboutBlankAncestorOriginsForNonTopLevel(Document* aDoc) { 6768 BrowsingContext* bc = aDoc->GetBrowsingContext(); 6769 MOZ_ASSERT(bc && !bc->IsDiscarded() && bc->GetEmbedderElement()); 6770 // We're not even going to attempt to deal with location.ancestorOrigins stuff 6771 // in the parent process. 6772 if (!XRE_IsContentProcess()) { 6773 return; 6774 } 6775 6776 const auto* frame = bc->GetEmbedderElement(); 6777 const auto referrerPolicy = frame->GetReferrerPolicyAsEnum(); 6778 // Inform the parent process that it needs to create an internal ancestor 6779 // origins list for this browsing context `bc` 6780 (void)ContentChild::GetSingleton()->SendUpdateAncestorOriginsList(bc); 6781 6782 const bool masked = referrerPolicy == ReferrerPolicy::No_referrer; 6783 BrowsingContext* parent = bc->GetParent(); 6784 MOZ_DIAGNOSTIC_ASSERT(parent && parent->IsInProcess() && 6785 parent->GetExtantDocument()); 6786 6787 nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals; 6788 constexpr auto getPrincipal = 6789 [](const BrowsingContext* ctx) -> nsIPrincipal* { 6790 if (!ctx) { 6791 return nullptr; 6792 } 6793 auto* doc = ctx->GetExtantDocument(); 6794 return doc ? doc->GetPrincipal() : nullptr; 6795 }; 6796 BrowsingContext* ancestorContextToCopyAncestorListFrom = parent; 6797 6798 // about:blank is different from normal docs. 6799 // We only care about in-process, same-origin ancestors. 6800 // Therefore run the algorithm all the way up to the last same-origin doc 6801 // add that origin to the list, and then append that origin's ancestor origins 6802 // list 6803 if (masked) { 6804 ancestorPrincipals.AppendElement(nullptr); 6805 // 16.1.1. If ancestorOrigin is same origin with parentDoc's origin, then 6806 // append a new opaque origin to output. 6807 auto* parentDocPrincipal = getPrincipal(parent); 6808 for (auto* ancestor = parent->GetParent(); ancestor; 6809 ancestor = ancestor->GetParent()) { 6810 auto* principal = getPrincipal(ancestor); 6811 if (principal && principal->Equals(parentDocPrincipal)) { 6812 // same principal, same process, adding nullptr for 6813 // parentContextToCopyAncestorListFrom 6814 ancestorContextToCopyAncestorListFrom = ancestor; 6815 ancestorPrincipals.AppendElement(nullptr); 6816 } else { 6817 // 16.1.2 Otherwise, append ancestorOrigin to output and set masked to 6818 // false. 6819 // Note: But in the case of about:blank, ancestorOrigin can 6820 // potentially live in another process. So we stop right before it, and 6821 // just copy `ancestorContextToCopyAncestorListFrom` list, since the 6822 // masking steps should finish here. 6823 break; 6824 } 6825 } 6826 } else { 6827 ancestorPrincipals.AppendElement(getPrincipal(parent)); 6828 } 6829 6830 nsTArray<nsString> list = ProduceAncestorOriginsList(ancestorPrincipals); 6831 Document* ancestorDoc = 6832 ancestorContextToCopyAncestorListFrom->GetExtantDocument(); 6833 MOZ_DIAGNOSTIC_ASSERT(ancestorDoc); 6834 list.AppendElements(ancestorDoc->GetAncestorOriginsList()); 6835 aDoc->SetAncestorOriginsList(std::move(list)); 6836 } 6837 6838 nsresult nsDocShell::CreateAboutBlankDocumentViewer( 6839 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal, 6840 nsIPolicyContainer* aPolicyContainer, nsIURI* aBaseURI, 6841 bool aIsInitialDocument, 6842 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP, 6843 bool aTryToSaveOldPresentation, bool aCheckPermitUnload, 6844 WindowGlobalChild* aActor) { 6845 RefPtr<Document> blankDoc; 6846 nsCOMPtr<nsIDocumentViewer> viewer; 6847 nsresult rv = NS_ERROR_FAILURE; 6848 6849 PROFILER_MARKER_UNTYPED("CreateAboutBlankDocumentViewer", DOM, 6850 MarkerStack::Capture()); 6851 6852 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal); 6853 6854 /* mCreatingDocument should never be true at this point. However, it's 6855 a theoretical possibility. We want to know about it and make it stop, 6856 and this sounds like a job for an assertion. */ 6857 NS_ASSERTION(!mCreatingDocument, 6858 "infinite(?) loop creating document averted"); 6859 if (mCreatingDocument) { 6860 return NS_ERROR_FAILURE; 6861 } 6862 6863 if (!mBrowsingContext->AncestorsAreCurrent() || 6864 (mozilla::SessionHistoryInParent() && mBrowsingContext->IsInBFCache())) { 6865 mBrowsingContext->RemoveRootFromBFCacheSync(); 6866 return NS_ERROR_NOT_AVAILABLE; 6867 } 6868 6869 // mDocumentViewer->PermitUnload may release |this| docshell. 6870 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this); 6871 6872 // Ensure that UsesOriginAgentCluster has been initialized for this 6873 // BrowsingContextGroup/principal pair before creating the document. 6874 if (aPrincipal) { 6875 mBrowsingContext->Group()->EnsureUsesOriginAgentClusterInitialized( 6876 aPrincipal); 6877 } 6878 6879 AutoRestore<bool> creatingDocument(mCreatingDocument); 6880 mCreatingDocument = true; 6881 6882 if (aPrincipal && !aPrincipal->IsSystemPrincipal() && 6883 mItemType != typeChrome) { 6884 if (GetIsTopLevelContentDocShell()) { 6885 // Bug 1948216 tracks having a FPD for top-level initial about:blank 6886 MOZ_ASSERT(aPrincipal->OriginAttributesRef().EqualsIgnoringFPD( 6887 mBrowsingContext->OriginAttributesRef())); 6888 } else { 6889 MOZ_ASSERT(aPrincipal->OriginAttributesRef() == 6890 mBrowsingContext->OriginAttributesRef()); 6891 } 6892 } 6893 6894 // Make sure timing is created. But first record whether we had it 6895 // already, so we don't clobber the timing for an in-progress load. 6896 bool hadTiming = mTiming; 6897 bool toBeReset = MaybeInitTiming(); 6898 if (mDocumentViewer) { 6899 if (aCheckPermitUnload) { 6900 // We've got a content viewer already. Make sure the user 6901 // permits us to discard the current document and replace it 6902 // with about:blank. And also ensure we fire the unload events 6903 // in the current document. 6904 6905 // Unload gets fired first for 6906 // document loaded from the session history. 6907 mTiming->NotifyBeforeUnload(); 6908 6909 bool okToUnload; 6910 rv = mDocumentViewer->PermitUnload(&okToUnload); 6911 if (mIsBeingDestroyed) { 6912 // unload handler destroyed this docshell. 6913 return NS_ERROR_NOT_AVAILABLE; 6914 } 6915 if (NS_SUCCEEDED(rv) && !okToUnload) { 6916 // The user chose not to unload the page, interrupt the load. 6917 MaybeResetInitTiming(toBeReset); 6918 return NS_ERROR_FAILURE; 6919 } 6920 if (mTiming) { 6921 mTiming->NotifyUnloadAccepted(mCurrentURI); 6922 } 6923 } 6924 6925 mSavingOldViewer = 6926 aTryToSaveOldPresentation && 6927 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr, 6928 /* aReportBFCacheComboTelemetry */ true); 6929 6930 // Make sure to blow away our mLoadingURI just in case. No loads 6931 // from inside this pagehide. 6932 mLoadingURI = nullptr; 6933 6934 // Stop any in-progress loading, so that we don't accidentally trigger any 6935 // PageShow notifications from Embed() interrupting our loading below. 6936 Stop(); 6937 6938 // Notify the current document that it is about to be unloaded!! 6939 // 6940 // It is important to fire the unload() notification *before* any state 6941 // is changed within the DocShell - otherwise, javascript will get the 6942 // wrong information :-( 6943 // 6944 (void)FirePageHideNotification(!mSavingOldViewer); 6945 // pagehide notification might destroy this docshell. 6946 if (mIsBeingDestroyed) { 6947 return NS_ERROR_DOCSHELL_DYING; 6948 } 6949 } 6950 6951 // Now make sure we don't think we're in the middle of firing unload after 6952 // this point. This will make us fire unload when the about:blank document 6953 // unloads... but that's ok, more or less. Would be nice if it fired load 6954 // too, of course. 6955 mFiredUnloadEvent = false; 6956 6957 nsCOMPtr<nsIDocumentLoaderFactory> docFactory = 6958 nsContentUtils::FindInternalDocumentViewer("text/html"_ns); 6959 6960 if (docFactory) { 6961 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal; 6962 const uint32_t sandboxFlags = 6963 mBrowsingContext->GetHasLoadedNonInitialDocument() 6964 ? mBrowsingContext->GetSandboxFlags() 6965 : mBrowsingContext->GetInitialSandboxFlags(); 6966 // If we're sandboxed, then create a new null principal. We skip 6967 // this if we're being created from WindowGlobalChild, since in 6968 // that case we already have a null principal if required. 6969 // We can't compare againt the BrowsingContext sandbox flag, since 6970 // the value was taken when the load initiated and may have since 6971 // changed. 6972 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) { 6973 if (aPrincipal) { 6974 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal); 6975 } else { 6976 principal = NullPrincipal::Create(GetOriginAttributes()); 6977 } 6978 partitionedPrincipal = principal; 6979 } else { 6980 principal = aPrincipal; 6981 partitionedPrincipal = aPartitionedPrincipal; 6982 } 6983 6984 // We cannot get the foreign partitioned principal for the initial 6985 // about:blank page. So, we change to check if we need to use the 6986 // partitioned principal for the service worker here. 6987 MaybeCreateInitialClientSource( 6988 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker( 6989 this) 6990 ? partitionedPrincipal 6991 : principal); 6992 6993 // generate (about:blank) document to load 6994 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal, 6995 partitionedPrincipal, this); 6996 if (blankDoc) { 6997 // Hack: manually set the policyContainer for the new document 6998 // Please create an actual copy of the policyContainer (do not share the 6999 // same reference) otherwise modifying the new container (such as 7000 // appending a new policy to CSP) within the new document will be 7001 // incorrectly propagated to the opening doc. 7002 if (aPolicyContainer) { 7003 RefPtr<PolicyContainer> policyContainerToInherit = 7004 new PolicyContainer(); 7005 policyContainerToInherit->InitFromOther( 7006 PolicyContainer::Cast(aPolicyContainer)); 7007 blankDoc->SetPolicyContainer(policyContainerToInherit); 7008 nsIContentSecurityPolicy* csp = 7009 PolicyContainer::GetCSP(policyContainerToInherit); 7010 if (!csp) { 7011 csp = new nsCSPContext(); 7012 policyContainerToInherit->SetCSP(csp); 7013 }; 7014 nsresult rv = csp->SetRequestContextWithDocument(blankDoc); 7015 if (NS_WARN_IF(NS_FAILED(rv))) { 7016 return rv; 7017 } 7018 } 7019 7020 blankDoc->SetInitialStatus( 7021 aIsInitialDocument ? Document::InitialStatus::IsInitialUncommitted 7022 : Document::InitialStatus::NeverInitial); 7023 7024 blankDoc->SetEmbedderPolicy(aCOEP); 7025 7026 // Hack: set the base URI manually, since this document never 7027 // got Reset() with a channel. 7028 blankDoc->SetBaseURI(aBaseURI); 7029 7030 // Copy our sandbox flags to the document. These are immutable 7031 // after being set here. 7032 blankDoc->SetSandboxFlags(sandboxFlags); 7033 7034 // We inherit the classification flags from the parent document if the 7035 // principal matches. 7036 nsCOMPtr<nsIDocShellTreeItem> parentItem; 7037 GetInProcessSameTypeParent(getter_AddRefs(parentItem)); 7038 if (parentItem) { 7039 RefPtr<Document> parentDocument = parentItem->GetDocument(); 7040 if (parentDocument && principal && 7041 principal->Equals(parentDocument->NodePrincipal())) { 7042 blankDoc->SetClassificationFlags( 7043 parentDocument->GetClassificationFlags()); 7044 } 7045 } 7046 7047 // create a content viewer for us and the new document 7048 docFactory->CreateInstanceForDocument( 7049 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view", 7050 getter_AddRefs(viewer)); 7051 7052 // hook 'em up 7053 if (viewer) { 7054 viewer->SetContainer(this); 7055 if (mLoadingEntry && mBrowsingContext->IsTop()) { 7056 mLoadingEntry->mInfo.SetTransient(); 7057 } 7058 rv = Embed(viewer, aActor, true, nullptr, mCurrentURI); 7059 NS_ENSURE_SUCCESS(rv, rv); 7060 7061 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr, 7062 /* aFireLocationChange */ true, 7063 /* aIsInitialAboutBlank */ aIsInitialDocument, 7064 /* aLocationFlags */ 0); 7065 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK; 7066 } 7067 7068 if (Element* embedderElement = blankDoc->GetEmbedderElement()) { 7069 blankDoc->InitFeaturePolicy(AsVariant(embedderElement)); 7070 } else { 7071 blankDoc->InitFeaturePolicy(AsVariant(Nothing{})); 7072 } 7073 7074 // Perform redacted location.ancestorOrigins algorithm for about:blank 7075 if (BrowsingContext* bc = GetBrowsingContext(); 7076 bc && bc->GetEmbedderElement()) { 7077 CreateAboutBlankAncestorOriginsForNonTopLevel(blankDoc); 7078 } 7079 } 7080 } 7081 7082 // The transient about:blank viewer doesn't have a session history entry. 7083 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr)); 7084 7085 // Clear out our mTiming like we would in EndPageLoad, if we didn't 7086 // have one before entering this function. 7087 if (!hadTiming) { 7088 mTiming = nullptr; 7089 mBlankTiming = true; 7090 } 7091 7092 return rv; 7093 } 7094 7095 NS_IMETHODIMP 7096 nsDocShell::CreateAboutBlankDocumentViewer( 7097 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal, 7098 nsIPolicyContainer* aPolicyContainer) { 7099 return CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal, 7100 aPolicyContainer, nullptr, 7101 /* aIsInitialDocument */ false); 7102 } 7103 7104 bool nsDocShell::CanSavePresentation(uint32_t aLoadType, 7105 nsIRequest* aNewRequest, 7106 Document* aNewDocument, 7107 bool aReportBFCacheComboTelemetry) { 7108 if (!mOSHE) { 7109 return false; // no entry to save into 7110 } 7111 7112 MOZ_ASSERT(!mozilla::SessionHistoryInParent(), 7113 "mOSHE cannot be non-null with SHIP"); 7114 nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer(); 7115 if (viewer) { 7116 NS_WARNING("mOSHE already has a content viewer!"); 7117 return false; 7118 } 7119 7120 // Only save presentation for "normal" loads and link loads. Anything else 7121 // probably wants to refetch the page, so caching the old presentation 7122 // would be incorrect. 7123 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY && 7124 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT && 7125 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE && 7126 aLoadType != LOAD_ERROR_PAGE) { 7127 return false; 7128 } 7129 7130 // If the session history entry has the saveLayoutState flag set to false, 7131 // then we should not cache the presentation. 7132 if (!mOSHE->GetSaveLayoutStateFlag()) { 7133 return false; 7134 } 7135 7136 // If the document is not done loading, don't cache it. 7137 if (!mScriptGlobal || mScriptGlobal->IsLoading()) { 7138 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose, 7139 ("Blocked due to document still loading")); 7140 return false; 7141 } 7142 7143 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) { 7144 return false; 7145 } 7146 7147 // Avoid doing the work of saving the presentation state in the case where 7148 // the content viewer cache is disabled. 7149 if (nsSHistory::GetMaxTotalViewers() == 0) { 7150 return false; 7151 } 7152 7153 // Don't cache the content viewer if we're in a subframe. 7154 if (mBrowsingContext->GetParent()) { 7155 return false; // this is a subframe load 7156 } 7157 7158 // If the document does not want its presentation cached, then don't. 7159 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc(); 7160 7161 uint32_t bfCacheCombo = 0; 7162 bool canSavePresentation = 7163 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true); 7164 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0); 7165 if (canSavePresentation && doc->IsTopLevelContentDocument()) { 7166 auto* browsingContextGroup = mBrowsingContext->Group(); 7167 nsTArray<RefPtr<BrowsingContext>>& topLevelContext = 7168 browsingContextGroup->Toplevels(); 7169 7170 for (const auto& browsingContext : topLevelContext) { 7171 if (browsingContext != mBrowsingContext) { 7172 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) { 7173 canSavePresentation = false; 7174 } 7175 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG; 7176 break; 7177 } 7178 } 7179 } 7180 7181 if (aReportBFCacheComboTelemetry) { 7182 ReportBFCacheComboTelemetry(bfCacheCombo); 7183 } 7184 return doc && canSavePresentation; 7185 } 7186 7187 /* static */ 7188 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) { 7189 // There are 11 possible reasons to make a request fails to use BFCache 7190 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record 7191 // the common combinations for reasons which make requests fail to use 7192 // BFCache. These combinations are generated based on some local browsings, 7193 // we need to adjust them when necessary. 7194 enum BFCacheStatusCombo : uint32_t { 7195 BFCACHE_SUCCESS, 7196 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG, 7197 // If both unload and beforeunload listeners are presented, it'll be 7198 // recorded as unload 7199 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER, 7200 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | 7201 mozilla::dom::BFCacheStatus::REQUEST, 7202 REQUEST = mozilla::dom::BFCacheStatus::REQUEST, 7203 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | 7204 mozilla::dom::BFCacheStatus::REQUEST | 7205 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION, 7206 UNLOAD_REQUEST_PEER_MSE = 7207 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | 7208 mozilla::dom::BFCacheStatus::REQUEST | 7209 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION | 7210 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT, 7211 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | 7212 mozilla::dom::BFCacheStatus::REQUEST | 7213 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT, 7214 SUSPENDED_UNLOAD_REQUEST_PEER = 7215 mozilla::dom::BFCacheStatus::SUSPENDED | 7216 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER | 7217 mozilla::dom::BFCacheStatus::REQUEST | 7218 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION, 7219 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES, 7220 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER, 7221 }; 7222 7223 // Beforeunload is recorded as a blocker only if it is the only one to block 7224 // bfcache. 7225 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) { 7226 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER; 7227 } 7228 switch (aCombo) { 7229 case BFCACHE_SUCCESS: 7230 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eBfcacheSuccess) 7231 .Add(); 7232 break; 7233 case NOT_ONLY_TOPLEVEL: 7234 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) { 7235 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eOther).Add(); 7236 break; 7237 } 7238 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eBfcacheSuccess) 7239 .Add(); 7240 glean::bfcache::combo 7241 .EnumGet(glean::bfcache::ComboLabel::eSuccessNotToplevel) 7242 .Add(); 7243 break; 7244 case UNLOAD: 7245 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnload).Add(); 7246 break; 7247 case BEFOREUNLOAD: 7248 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eBeforeunload) 7249 .Add(); 7250 break; 7251 case UNLOAD_REQUEST: 7252 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnloadReq) 7253 .Add(); 7254 break; 7255 case REQUEST: 7256 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eReq).Add(); 7257 break; 7258 case UNLOAD_REQUEST_PEER: 7259 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnloadReqPeer) 7260 .Add(); 7261 break; 7262 case UNLOAD_REQUEST_PEER_MSE: 7263 glean::bfcache::combo 7264 .EnumGet(glean::bfcache::ComboLabel::eUnloadReqPeerMse) 7265 .Add(); 7266 break; 7267 case UNLOAD_REQUEST_MSE: 7268 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eUnloadReqMse) 7269 .Add(); 7270 break; 7271 case SUSPENDED_UNLOAD_REQUEST_PEER: 7272 glean::bfcache::combo 7273 .EnumGet(glean::bfcache::ComboLabel::eSpdUnloadReqPeer) 7274 .Add(); 7275 break; 7276 case REMOTE_SUBFRAMES: 7277 glean::bfcache::combo 7278 .EnumGet(glean::bfcache::ComboLabel::eRemoteSubframes) 7279 .Add(); 7280 break; 7281 default: 7282 glean::bfcache::combo.EnumGet(glean::bfcache::ComboLabel::eOther).Add(); 7283 break; 7284 } 7285 }; 7286 7287 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) { 7288 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7289 MOZ_ASSERT(!mIsBeingDestroyed); 7290 7291 NS_ASSERTION(!mEditorData, 7292 "Why reattach an editor when we already have one?"); 7293 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(), 7294 "Reattaching when there's not a detached editor."); 7295 7296 if (mEditorData || !aSHEntry) { 7297 return; 7298 } 7299 7300 mEditorData = WrapUnique(aSHEntry->ForgetEditorData()); 7301 if (mEditorData) { 7302 #ifdef DEBUG 7303 nsresult rv = 7304 #endif 7305 mEditorData->ReattachToWindow(this); 7306 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session"); 7307 } 7308 } 7309 7310 void nsDocShell::DetachEditorFromWindow() { 7311 if (!mEditorData || mEditorData->WaitingForLoad()) { 7312 // If there's nothing to detach, or if the editor data is actually set 7313 // up for the _new_ page that's coming in, don't detach. 7314 return; 7315 } 7316 7317 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(), 7318 "Detaching editor when it's already detached."); 7319 7320 nsresult res = mEditorData->DetachFromWindow(); 7321 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor"); 7322 7323 if (NS_SUCCEEDED(res)) { 7324 // Make mOSHE hold the owning ref to the editor data. 7325 if (mOSHE) { 7326 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(), 7327 "We should not set the editor data again once after we " 7328 "detached the editor data during destroying this docshell"); 7329 mOSHE->SetEditorData(mEditorData.release()); 7330 } else { 7331 mEditorData = nullptr; 7332 } 7333 } 7334 7335 #ifdef DEBUG 7336 { 7337 bool isEditable; 7338 GetEditable(&isEditable); 7339 NS_ASSERTION(!isEditable, 7340 "Window is still editable after detaching editor."); 7341 } 7342 #endif // DEBUG 7343 } 7344 7345 nsresult nsDocShell::CaptureState() { 7346 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7347 7348 if (!mOSHE || mOSHE == mLSHE) { 7349 // No entry to save into, or we're replacing the existing entry. 7350 return NS_ERROR_FAILURE; 7351 } 7352 7353 if (!mScriptGlobal) { 7354 return NS_ERROR_FAILURE; 7355 } 7356 7357 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState(); 7358 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE); 7359 7360 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) { 7361 nsAutoCString spec; 7362 nsCOMPtr<nsIURI> uri = mOSHE->GetURI(); 7363 if (uri) { 7364 uri->GetSpec(spec); 7365 } 7366 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7367 ("Saving presentation into session history, URI: %s", spec.get())); 7368 } 7369 7370 mOSHE->SetWindowState(windowState); 7371 7372 // Suspend refresh URIs and save off the timer queue 7373 mOSHE->SetRefreshURIList(mSavedRefreshURIList); 7374 7375 // Capture the current content viewer bounds. 7376 if (mDocumentViewer) { 7377 LayoutDeviceIntRect bounds; 7378 mDocumentViewer->GetBounds(bounds); 7379 mOSHE->SetViewerBounds(bounds.ToUnknownRect()); 7380 } 7381 7382 // Capture the docshell hierarchy. 7383 mOSHE->ClearChildShells(); 7384 7385 uint32_t childCount = mChildList.Length(); 7386 for (uint32_t i = 0; i < childCount; ++i) { 7387 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i)); 7388 NS_ASSERTION(childShell, "null child shell"); 7389 7390 mOSHE->AddChildShell(childShell); 7391 } 7392 7393 return NS_OK; 7394 } 7395 7396 NS_IMETHODIMP 7397 nsDocShell::RestorePresentationEvent::Run() { 7398 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7399 7400 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) { 7401 NS_WARNING("RestoreFromHistory failed"); 7402 } 7403 return NS_OK; 7404 } 7405 7406 NS_IMETHODIMP 7407 nsDocShell::BeginRestore(nsIDocumentViewer* aDocumentViewer, bool aTop) { 7408 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7409 7410 nsresult rv; 7411 if (!aDocumentViewer) { 7412 NS_ENSURE_TRUE(VerifyDocumentViewer(), NS_ERROR_FAILURE); 7413 7414 aDocumentViewer = mDocumentViewer; 7415 } 7416 7417 // Dispatch events for restoring the presentation. We try to simulate 7418 // the progress notifications loading the document would cause, so we add 7419 // the document's channel to the loadgroup to initiate stateChange 7420 // notifications. 7421 7422 RefPtr<Document> doc = aDocumentViewer->GetDocument(); 7423 if (doc) { 7424 nsIChannel* channel = doc->GetChannel(); 7425 if (channel) { 7426 mEODForCurrentDocument = false; 7427 mIsRestoringDocument = true; 7428 mLoadGroup->AddRequest(channel, nullptr); 7429 mIsRestoringDocument = false; 7430 } 7431 } 7432 7433 if (!aTop) { 7434 // This point corresponds to us having gotten OnStartRequest or 7435 // STATE_START, so do the same thing that CreateDocumentViewer does at 7436 // this point to ensure that unload/pagehide events for this document 7437 // will fire when it's unloaded again. 7438 mFiredUnloadEvent = false; 7439 7440 // For non-top frames, there is no notion of making sure that the 7441 // previous document is in the domwindow when STATE_START notifications 7442 // happen. We can just call BeginRestore for all of the child shells 7443 // now. 7444 rv = BeginRestoreChildren(); 7445 NS_ENSURE_SUCCESS(rv, rv); 7446 } 7447 7448 return NS_OK; 7449 } 7450 7451 nsresult nsDocShell::BeginRestoreChildren() { 7452 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7453 7454 for (auto* childDocLoader : mChildList.ForwardRange()) { 7455 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader); 7456 if (child) { 7457 nsresult rv = child->BeginRestore(nullptr, false); 7458 NS_ENSURE_SUCCESS(rv, rv); 7459 } 7460 } 7461 return NS_OK; 7462 } 7463 7464 NS_IMETHODIMP 7465 nsDocShell::FinishRestore() { 7466 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7467 7468 // First we call finishRestore() on our children. In the simulated load, 7469 // all of the child frames finish loading before the main document. 7470 7471 for (auto* childDocLoader : mChildList.ForwardRange()) { 7472 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader); 7473 if (child) { 7474 child->FinishRestore(); 7475 } 7476 } 7477 7478 if (mOSHE && mOSHE->HasDetachedEditor()) { 7479 ReattachEditorToWindow(mOSHE); 7480 } 7481 7482 RefPtr<Document> doc = GetDocument(); 7483 if (doc) { 7484 // Finally, we remove the request from the loadgroup. This will 7485 // cause onStateChange(STATE_STOP) to fire, which will fire the 7486 // pageshow event to the chrome. 7487 7488 nsIChannel* channel = doc->GetChannel(); 7489 if (channel) { 7490 mIsRestoringDocument = true; 7491 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK); 7492 mIsRestoringDocument = false; 7493 } 7494 } 7495 7496 return NS_OK; 7497 } 7498 7499 NS_IMETHODIMP 7500 nsDocShell::GetRestoringDocument(bool* aRestoring) { 7501 *aRestoring = mIsRestoringDocument; 7502 return NS_OK; 7503 } 7504 7505 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry, 7506 bool* aRestoring) { 7507 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7508 MOZ_ASSERT(!mIsBeingDestroyed); 7509 7510 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY, 7511 "RestorePresentation should only be called for history loads"); 7512 7513 nsCOMPtr<nsIDocumentViewer> viewer = aSHEntry->GetDocumentViewer(); 7514 7515 nsAutoCString spec; 7516 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) { 7517 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI(); 7518 if (uri) { 7519 uri->GetSpec(spec); 7520 } 7521 } 7522 7523 *aRestoring = false; 7524 7525 if (!viewer) { 7526 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7527 ("no saved presentation for uri: %s", spec.get())); 7528 return NS_OK; 7529 } 7530 7531 // We need to make sure the content viewer's container is this docshell. 7532 // In subframe navigation, it's possible for the docshell that the 7533 // content viewer was originally loaded into to be replaced with a 7534 // different one. We don't currently support restoring the presentation 7535 // in that case. 7536 7537 nsCOMPtr<nsIDocShell> container; 7538 viewer->GetContainer(getter_AddRefs(container)); 7539 if (!::SameCOMIdentity(container, GetAsSupports(this))) { 7540 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7541 ("No valid container, clearing presentation")); 7542 aSHEntry->SetDocumentViewer(nullptr); 7543 return NS_ERROR_FAILURE; 7544 } 7545 7546 NS_ASSERTION(mDocumentViewer != viewer, "Restoring existing presentation"); 7547 7548 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7549 ("restoring presentation from session history: %s", spec.get())); 7550 7551 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing()); 7552 7553 // Post an event that will remove the request after we've returned 7554 // to the event loop. This mimics the way it is called by nsIChannel 7555 // implementations. 7556 7557 // Revoke any pending restore (just in case). 7558 NS_ASSERTION(!mRestorePresentationEvent.IsPending(), 7559 "should only have one RestorePresentationEvent"); 7560 mRestorePresentationEvent.Revoke(); 7561 7562 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this); 7563 nsresult rv = Dispatch(do_AddRef(evt)); 7564 if (NS_SUCCEEDED(rv)) { 7565 mRestorePresentationEvent = evt.get(); 7566 // The rest of the restore processing will happen on our event 7567 // callback. 7568 *aRestoring = true; 7569 } 7570 7571 return rv; 7572 } 7573 7574 namespace { 7575 class MOZ_STACK_CLASS PresentationEventForgetter { 7576 public: 7577 explicit PresentationEventForgetter( 7578 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>& 7579 aRestorePresentationEvent) 7580 : mRestorePresentationEvent(aRestorePresentationEvent), 7581 mEvent(aRestorePresentationEvent.get()) {} 7582 7583 ~PresentationEventForgetter() { Forget(); } 7584 7585 void Forget() { 7586 if (mRestorePresentationEvent.get() == mEvent) { 7587 mRestorePresentationEvent.Forget(); 7588 mEvent = nullptr; 7589 } 7590 } 7591 7592 private: 7593 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>& 7594 mRestorePresentationEvent; 7595 RefPtr<nsDocShell::RestorePresentationEvent> mEvent; 7596 }; 7597 7598 } // namespace 7599 7600 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) { 7601 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0; 7602 } 7603 7604 nsresult nsDocShell::RestoreFromHistory() { 7605 MOZ_ASSERT(!mozilla::SessionHistoryInParent()); 7606 MOZ_ASSERT(mRestorePresentationEvent.IsPending()); 7607 PresentationEventForgetter forgetter(mRestorePresentationEvent); 7608 7609 // This section of code follows the same ordering as CreateDocumentViewer. 7610 if (!mLSHE) { 7611 return NS_ERROR_FAILURE; 7612 } 7613 7614 nsCOMPtr<nsIDocumentViewer> viewer = mLSHE->GetDocumentViewer(); 7615 if (!viewer) { 7616 return NS_ERROR_FAILURE; 7617 } 7618 7619 if (mSavingOldViewer) { 7620 // We determined that it was safe to cache the document presentation 7621 // at the time we initiated the new load. We need to check whether 7622 // it's still safe to do so, since there may have been DOM mutations 7623 // or new requests initiated. 7624 RefPtr<Document> doc = viewer->GetDocument(); 7625 nsIRequest* request = nullptr; 7626 if (doc) { 7627 request = doc->GetChannel(); 7628 } 7629 mSavingOldViewer = CanSavePresentation( 7630 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false); 7631 } 7632 7633 // Protect against mLSHE going away via a load triggered from 7634 // pagehide or unload. 7635 nsCOMPtr<nsISHEntry> origLSHE = mLSHE; 7636 7637 // Make sure to blow away our mLoadingURI just in case. No loads 7638 // from inside this pagehide. 7639 mLoadingURI = nullptr; 7640 7641 // Notify the old content viewer that it's being hidden. 7642 FirePageHideNotification(!mSavingOldViewer); 7643 // pagehide notification might destroy this docshell. 7644 if (mIsBeingDestroyed) { 7645 return NS_ERROR_DOCSHELL_DYING; 7646 } 7647 7648 // If mLSHE was changed as a result of the pagehide event, then 7649 // something else was loaded. Don't finish restoring. 7650 if (mLSHE != origLSHE) { 7651 return NS_OK; 7652 } 7653 7654 // Add the request to our load group. We do this before swapping out 7655 // the content viewers so that consumers of STATE_START can access 7656 // the old document. We only deal with the toplevel load at this time -- 7657 // to be consistent with normal document loading, subframes cannot start 7658 // loading until after data arrives, which is after STATE_START completes. 7659 7660 RefPtr<RestorePresentationEvent> currentPresentationRestoration = 7661 mRestorePresentationEvent.get(); 7662 Stop(); 7663 // Make sure we're still restoring the same presentation. 7664 // If we aren't, docshell is in process doing another load already. 7665 NS_ENSURE_STATE(currentPresentationRestoration == 7666 mRestorePresentationEvent.get()); 7667 BeginRestore(viewer, true); 7668 NS_ENSURE_STATE(currentPresentationRestoration == 7669 mRestorePresentationEvent.get()); 7670 forgetter.Forget(); 7671 7672 // Set mFiredUnloadEvent = false so that the unload handler for the 7673 // *new* document will fire. 7674 mFiredUnloadEvent = false; 7675 7676 mURIResultedInDocument = true; 7677 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 7678 if (rootSH) { 7679 mPreviousEntryIndex = rootSH->Index(); 7680 rootSH->LegacySHistory()->UpdateIndex(); 7681 mLoadedEntryIndex = rootSH->Index(); 7682 MOZ_LOG(gPageCacheLog, LogLevel::Verbose, 7683 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex, 7684 mLoadedEntryIndex)); 7685 } 7686 7687 // Rather than call Embed(), we will retrieve the viewer from the session 7688 // history entry and swap it in. 7689 // XXX can we refactor this so that we can just call Embed()? 7690 PersistLayoutHistoryState(); 7691 nsresult rv; 7692 if (mDocumentViewer) { 7693 if (mSavingOldViewer && NS_FAILED(CaptureState())) { 7694 if (mOSHE) { 7695 mOSHE->SyncPresentationState(); 7696 } 7697 mSavingOldViewer = false; 7698 } 7699 } 7700 7701 mSavedRefreshURIList = nullptr; 7702 7703 // In cases where we use a transient about:blank viewer between loads, 7704 // we never show the transient viewer, so _its_ previous viewer is never 7705 // destroyed. Destroy any such previous viewer now. 7706 if (mDocumentViewer) { 7707 // Make sure to hold a strong ref to previousViewer here while we 7708 // drop the reference to it from mDocumentViewer. 7709 nsCOMPtr<nsIDocumentViewer> previousViewer = 7710 mDocumentViewer->GetPreviousViewer(); 7711 if (previousViewer) { 7712 mDocumentViewer->SetPreviousViewer(nullptr); 7713 previousViewer->Destroy(); 7714 } 7715 } 7716 7717 // Save the bounds of the root view's widget. 7718 LayoutDeviceIntRect newBounds(0, 0, 0, 0); 7719 7720 PresShell* oldPresShell = GetPresShell(); 7721 if (oldPresShell) { 7722 mDocumentViewer->GetBounds(newBounds); 7723 } 7724 7725 // Transfer ownership to mDocumentViewer. By ensuring that either the 7726 // docshell or the session history, but not both, have references to the 7727 // content viewer, we prevent the viewer from being torn down after 7728 // Destroy() is called. 7729 7730 if (mDocumentViewer) { 7731 mDocumentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr); 7732 viewer->SetPreviousViewer(mDocumentViewer); 7733 } 7734 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) { 7735 // We don't plan to save a viewer in mOSHE; tell it to drop 7736 // any other state it's holding. 7737 mOSHE->SyncPresentationState(); 7738 } 7739 7740 // Order the mDocumentViewer setup just like Embed does. 7741 mDocumentViewer = nullptr; 7742 7743 // Now that we're about to switch documents, forget all of our children. 7744 // Note that we cached them as needed up in CaptureState above. 7745 DestroyChildren(); 7746 7747 mDocumentViewer.swap(viewer); 7748 7749 // Grab all of the related presentation from the SHEntry now. 7750 // Clearing the viewer from the SHEntry will clear all of this state. 7751 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState(); 7752 mLSHE->SetWindowState(nullptr); 7753 7754 bool sticky = mLSHE->GetSticky(); 7755 7756 RefPtr<Document> document = mDocumentViewer->GetDocument(); 7757 7758 nsCOMArray<nsIDocShellTreeItem> childShells; 7759 int32_t i = 0; 7760 nsCOMPtr<nsIDocShellTreeItem> child; 7761 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) && 7762 child) { 7763 childShells.AppendObject(child); 7764 } 7765 7766 // get the previous content viewer size 7767 nsIntRect oldBounds(0, 0, 0, 0); 7768 mLSHE->GetViewerBounds(oldBounds); 7769 7770 // Restore the refresh URI list. The refresh timers will be restarted 7771 // when EndPageLoad() is called. 7772 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList(); 7773 7774 // Reattach to the window object. 7775 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive 7776 rv = mDocumentViewer->Open(windowState, mLSHE); 7777 mIsRestoringDocument = false; 7778 7779 // Hack to keep nsDocShellEditorData alive across the 7780 // SetDocumentViewer(nullptr) call below. 7781 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData()); 7782 7783 // Now remove it from the cached presentation. 7784 mLSHE->SetDocumentViewer(nullptr); 7785 mEODForCurrentDocument = false; 7786 7787 mLSHE->SetEditorData(data.release()); 7788 7789 #ifdef DEBUG 7790 { 7791 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList(); 7792 nsCOMPtr<nsIDocShellTreeItem> childShell; 7793 mLSHE->ChildShellAt(0, getter_AddRefs(childShell)); 7794 NS_ASSERTION(!refreshURIs && !childShell, 7795 "SHEntry should have cleared presentation state"); 7796 } 7797 #endif 7798 7799 // Restore the sticky state of the viewer. The viewer has set this state 7800 // on the history entry in Destroy() just before marking itself non-sticky, 7801 // to avoid teardown of the presentation. 7802 mDocumentViewer->SetSticky(sticky); 7803 7804 NS_ENSURE_SUCCESS(rv, rv); 7805 7806 // mLSHE is now our currently-loaded document. 7807 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE)); 7808 7809 // We aren't going to restore any items from the LayoutHistoryState, 7810 // but we don't want them to stay around in case the page is reloaded. 7811 SetLayoutHistoryState(nullptr); 7812 7813 // This is the end of our Embed() replacement 7814 7815 mSavingOldViewer = false; 7816 mEODForCurrentDocument = false; 7817 7818 if (document) { 7819 RefPtr<nsDocShell> parent = GetInProcessParentDocshell(); 7820 if (parent) { 7821 RefPtr<Document> d = parent->GetDocument(); 7822 if (d) { 7823 if (d->EventHandlingSuppressed()) { 7824 document->SuppressEventHandling(d->EventHandlingSuppressed()); 7825 } 7826 } 7827 } 7828 7829 // Use the uri from the mLSHE we had when we entered this function 7830 // (which need not match the document's URI if anchors are involved), 7831 // since that's the history entry we're loading. Note that if we use 7832 // origLSHE we don't have to worry about whether the entry in question 7833 // is still mLSHE or whether it's now mOSHE. 7834 nsCOMPtr<nsIURI> uri = origLSHE->GetURI(); 7835 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true, 7836 /* aIsInitialAboutBlank */ false, 7837 /* aLocationFlags */ 0); 7838 } 7839 7840 // This is the end of our CreateDocumentViewer() replacement. 7841 // Now we simulate a load. First, we restore the state of the javascript 7842 // window object. 7843 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow(); 7844 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface"); 7845 7846 // Now, dispatch a title change event which would happen as the 7847 // <head> is parsed. 7848 document->NotifyPossibleTitleChange(false); 7849 7850 // Now we simulate appending child docshells for subframes. 7851 for (i = 0; i < childShells.Count(); ++i) { 7852 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i); 7853 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem); 7854 7855 // Make sure to not clobber the state of the child. Since AddChild 7856 // always clobbers it, save it off first. 7857 bool allowRedirects; 7858 childShell->GetAllowMetaRedirects(&allowRedirects); 7859 7860 bool allowSubframes; 7861 childShell->GetAllowSubframes(&allowSubframes); 7862 7863 bool allowImages; 7864 childShell->GetAllowImages(&allowImages); 7865 7866 bool allowMedia = childShell->GetAllowMedia(); 7867 7868 bool allowDNSPrefetch; 7869 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch); 7870 7871 bool allowContentRetargeting = childShell->GetAllowContentRetargeting(); 7872 bool allowContentRetargetingOnChildren = 7873 childShell->GetAllowContentRetargetingOnChildren(); 7874 7875 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that 7876 // the child inherits our state. Among other things, this means that the 7877 // child inherits our mPrivateBrowsingId, which is what we want. 7878 AddChild(childItem); 7879 7880 childShell->SetAllowMetaRedirects(allowRedirects); 7881 childShell->SetAllowSubframes(allowSubframes); 7882 childShell->SetAllowImages(allowImages); 7883 childShell->SetAllowMedia(allowMedia); 7884 childShell->SetAllowDNSPrefetch(allowDNSPrefetch); 7885 childShell->SetAllowContentRetargeting(allowContentRetargeting); 7886 childShell->SetAllowContentRetargetingOnChildren( 7887 allowContentRetargetingOnChildren); 7888 7889 rv = childShell->BeginRestore(nullptr, false); 7890 NS_ENSURE_SUCCESS(rv, rv); 7891 } 7892 7893 // Make sure to restore the window state after adding the child shells back 7894 // to the tree. This is necessary for Thaw() and Resume() to propagate 7895 // properly. 7896 rv = privWin->RestoreWindowState(windowState); 7897 NS_ENSURE_SUCCESS(rv, rv); 7898 7899 RefPtr<PresShell> presShell = GetPresShell(); 7900 7901 // We may be displayed on a different monitor (or in a different 7902 // HiDPI mode) than when we got into the history list. So we need 7903 // to check if this has happened. See bug 838239. 7904 7905 // Because the prescontext normally handles resolution changes via 7906 // a runnable (see nsPresContext::UIResolutionChanged), its device 7907 // context won't be -immediately- updated as a result of calling 7908 // presShell->BackingScaleFactorChanged(). 7909 7910 // But we depend on that device context when adjusting the view size 7911 // via mDocumentViewer->SetBounds(newBounds) below. So we need to 7912 // explicitly tell it to check for changed resolution here. 7913 if (presShell) { 7914 RefPtr<nsPresContext> pc = presShell->GetPresContext(); 7915 if (pc->DeviceContext()->CheckDPIChange()) { 7916 presShell->BackingScaleFactorChanged(); 7917 } 7918 // Recompute zoom and text-zoom and such. 7919 pc->RecomputeBrowsingContextDependentData(); 7920 } 7921 7922 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow(); 7923 7924 // If parent is suspended, increase suspension count. 7925 // This can't be done as early as event suppression since this 7926 // depends on docshell tree. 7927 privWinInner->SyncStateFromParentWindow(); 7928 7929 // Now that all of the child docshells have been put into place, we can 7930 // restart the timers for the window and all of the child frames. 7931 privWinInner->Resume(); 7932 7933 // Now that we have found the inner window of the page restored 7934 // from the history, we have to make sure that 7935 // performance.navigation.type is 2. 7936 Performance* performance = privWinInner->GetPerformance(); 7937 if (performance) { 7938 performance->GetDOMTiming()->NotifyRestoreStart(); 7939 } 7940 7941 // Restore the refresh URI list. The refresh timers will be restarted 7942 // when EndPageLoad() is called. 7943 mRefreshURIList = refreshURIList; 7944 7945 // Meta-refresh timers have been restarted for this shell, but not 7946 // for our children. Walk the child shells and restart their timers. 7947 for (auto* childDocLoader : mChildList.ForwardRange()) { 7948 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader); 7949 if (child) { 7950 child->ResumeRefreshURIs(); 7951 } 7952 } 7953 7954 // Make sure this presentation is the same size as the previous 7955 // presentation. If this is not the same size we showed it at last time, 7956 // then we need to resize the widget. 7957 7958 if (presShell) { 7959 if (!newBounds.IsEmpty() && 7960 !newBounds.ToUnknownRect().IsEqualEdges(oldBounds)) { 7961 MOZ_LOG(gPageCacheLog, LogLevel::Debug, 7962 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y, 7963 newBounds.width, newBounds.height)); 7964 mDocumentViewer->SetBounds(newBounds); 7965 } else if (ScrollContainerFrame* sf = 7966 presShell->GetRootScrollContainerFrame()) { 7967 sf->PostScrolledAreaEventForCurrentArea(); 7968 } 7969 } 7970 7971 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to 7972 // update it. 7973 if (oldPresShell && presShell && 7974 presShell->IsUnderHiddenEmbedderElement() != 7975 oldPresShell->IsUnderHiddenEmbedderElement()) { 7976 presShell->SetIsUnderHiddenEmbedderElement( 7977 oldPresShell->IsUnderHiddenEmbedderElement()); 7978 } 7979 7980 // Simulate the completion of the load. 7981 nsDocShell::FinishRestore(); 7982 7983 // Restart plugins, and paint the content. 7984 if (presShell) { 7985 presShell->Thaw(); 7986 } 7987 7988 return privWin->FireDelayedDOMEvents(true); 7989 } 7990 7991 nsresult nsDocShell::CreateDocumentViewer(const nsACString& aContentType, 7992 nsIRequest* aRequest, 7993 nsIStreamListener** aContentHandler) { 7994 *aContentHandler = nullptr; 7995 7996 if (!mTreeOwner || mIsBeingDestroyed) { 7997 // If we don't have a tree owner, then we're in the process of being 7998 // destroyed. Rather than continue trying to load something, just give up. 7999 return NS_ERROR_DOCSHELL_DYING; 8000 } 8001 8002 if (!mBrowsingContext->AncestorsAreCurrent() || 8003 mBrowsingContext->IsInBFCache()) { 8004 mBrowsingContext->RemoveRootFromBFCacheSync(); 8005 return NS_ERROR_NOT_AVAILABLE; 8006 } 8007 8008 // Can we check the content type of the current content viewer 8009 // and reuse it without destroying it and re-creating it? 8010 8011 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?"); 8012 8013 // Instantiate the content viewer object 8014 nsCOMPtr<nsIDocumentViewer> viewer; 8015 nsresult rv = NewDocumentViewerObj(aContentType, aRequest, mLoadGroup, 8016 aContentHandler, getter_AddRefs(viewer)); 8017 8018 if (NS_FAILED(rv)) { 8019 return rv; 8020 } 8021 8022 // Notify the current document that it is about to be unloaded!! 8023 // 8024 // It is important to fire the unload() notification *before* any state 8025 // is changed within the DocShell - otherwise, javascript will get the 8026 // wrong information :-( 8027 // 8028 8029 if (mSavingOldViewer) { 8030 // We determined that it was safe to cache the document presentation 8031 // at the time we initiated the new load. We need to check whether 8032 // it's still safe to do so, since there may have been DOM mutations 8033 // or new requests initiated. 8034 RefPtr<Document> doc = viewer->GetDocument(); 8035 mSavingOldViewer = CanSavePresentation( 8036 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false); 8037 } 8038 8039 NS_ASSERTION(!mLoadingURI, "Re-entering unload?"); 8040 8041 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest); 8042 if (aOpenedChannel) { 8043 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI)); 8044 } 8045 8046 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset 8047 // it before we do call Embed. 8048 nsCOMPtr<nsIURI> previousURI = mCurrentURI; 8049 8050 FirePageHideNotification(!mSavingOldViewer); 8051 if (mIsBeingDestroyed) { 8052 // Force to stop the newly created orphaned viewer. 8053 viewer->Stop(); 8054 return NS_ERROR_DOCSHELL_DYING; 8055 } 8056 mLoadingURI = nullptr; 8057 8058 // Set mFiredUnloadEvent = false so that the unload handler for the 8059 // *new* document will fire. 8060 mFiredUnloadEvent = false; 8061 8062 // we've created a new document so go ahead and call 8063 // OnNewURI(), but don't fire OnLocationChange() 8064 // notifications before we've called Embed(). See bug 284993. 8065 mURIResultedInDocument = true; 8066 bool errorOnLocationChangeNeeded = false; 8067 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel; 8068 nsCOMPtr<nsIURI> failedURI; 8069 8070 // https://html.spec.whatwg.org/#finalize-a-cross-document-navigation 8071 // 9. If entryToReplace is null, then: ... 8072 // Otherwise: ... 8073 // 4. If historyEntry's document state's origin is same origin with 8074 // entryToReplace's document state's origin, then set 8075 // historyEntry's navigation API key to entryToReplace's 8076 // navigation API key. 8077 bool isReplace = 8078 mActiveEntry && mLoadingEntry && 8079 mLoadingEntry->mTriggeringNavigationType 8080 .map([](auto type) { return type == NavigationType::Replace; }) 8081 .valueOr(false); 8082 if (isReplace) { 8083 nsCOMPtr<nsIURI> uri = mActiveEntry->GetURIOrInheritedForAboutBlank(); 8084 nsCOMPtr<nsIURI> targetURI = 8085 mLoadingEntry->mInfo.GetURIOrInheritedForAboutBlank(); 8086 bool sameOrigin = 8087 NS_SUCCEEDED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI( 8088 targetURI, uri, false, false)); 8089 if (sameOrigin) { 8090 mLoadingEntry->mInfo.NavigationKey() = mActiveEntry->NavigationKey(); 8091 } 8092 } 8093 8094 if (mLoadType == LOAD_ERROR_PAGE) { 8095 // We need to set the SH entry and our current URI here and not 8096 // at the moment we load the page. We want the same behavior 8097 // of Stop() as for a normal page load. See bug 514232 for details. 8098 8099 // Revert mLoadType to load type to state the page load failed, 8100 // following function calls need it. 8101 mLoadType = mFailedLoadType; 8102 8103 Document* doc = viewer->GetDocument(); 8104 if (doc) { 8105 doc->SetFailedChannel(failedChannel); 8106 } 8107 8108 nsCOMPtr<nsIPrincipal> triggeringPrincipal; 8109 if (failedChannel) { 8110 // Make sure we have a URI to set currentURI. 8111 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI)); 8112 } else { 8113 // if there is no failed channel we have to explicitly provide 8114 // a triggeringPrincipal for the history entry. 8115 triggeringPrincipal = nsContentUtils::GetSystemPrincipal(); 8116 } 8117 8118 if (!failedURI) { 8119 failedURI = mFailedURI; 8120 } 8121 if (!failedURI) { 8122 // We need a URI object to store a session history entry, so make up a URI 8123 NS_NewURI(getter_AddRefs(failedURI), "about:blank"); 8124 } 8125 8126 // When we don't have failedURI, something wrong will happen. See 8127 // bug 291876. 8128 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs."); 8129 8130 mFailedChannel = nullptr; 8131 mFailedURI = nullptr; 8132 8133 // Create an shistory entry for the old load. 8134 if (failedURI) { 8135 errorOnLocationChangeNeeded = 8136 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr, 8137 nullptr, nullptr, false, false); 8138 } 8139 8140 // Be sure to have a correct mLSHE, it may have been cleared by 8141 // EndPageLoad. See bug 302115. 8142 ChildSHistory* shistory = GetSessionHistory(); 8143 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) { 8144 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex(); 8145 if (idx == -1) { 8146 idx = shistory->Index(); 8147 } 8148 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE)); 8149 } 8150 8151 mLoadType = LOAD_ERROR_PAGE; 8152 } 8153 8154 nsCOMPtr<nsIURI> finalURI; 8155 // If this a redirect, use the final url (uri) 8156 // else use the original url 8157 // 8158 // Note that this should match what documents do (see Document::Reset). 8159 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI)); 8160 8161 bool onLocationChangeNeeded = false; 8162 if (finalURI) { 8163 // Pass false for aCloneSHChildren, since we're loading a new page here. 8164 onLocationChangeNeeded = OnNewURI(finalURI, aOpenedChannel, nullptr, 8165 nullptr, nullptr, nullptr, true, false); 8166 } 8167 8168 // We inherit the classification flags from the parent document if the 8169 // document is about:blank and the principal matches. 8170 nsCOMPtr<nsIDocShellTreeItem> parentItem; 8171 GetInProcessSameTypeParent(getter_AddRefs(parentItem)); 8172 if (parentItem && finalURI && NS_IsAboutBlank(finalURI)) { 8173 RefPtr<Document> doc = viewer->GetDocument(); 8174 RefPtr<Document> parentDocument = parentItem->GetDocument(); 8175 if (parentDocument && doc && 8176 doc->NodePrincipal()->Equals(parentDocument->NodePrincipal())) { 8177 doc->SetClassificationFlags(parentDocument->GetClassificationFlags()); 8178 } 8179 } 8180 8181 // let's try resetting the load group if we need to... 8182 nsCOMPtr<nsILoadGroup> currentLoadGroup; 8183 NS_ENSURE_SUCCESS( 8184 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)), 8185 NS_ERROR_FAILURE); 8186 8187 if (currentLoadGroup != mLoadGroup) { 8188 nsLoadFlags loadFlags = 0; 8189 8190 // Cancel any URIs that are currently loading... 8191 // XXX: Need to do this eventually Stop(); 8192 // 8193 // Retarget the document to this loadgroup... 8194 // 8195 /* First attach the channel to the right loadgroup 8196 * and then remove from the old loadgroup. This 8197 * puts the notifications in the right order and 8198 * we don't null-out mLSHE in OnStateChange() for 8199 * all redirected urls 8200 */ 8201 aOpenedChannel->SetLoadGroup(mLoadGroup); 8202 8203 // Mark the channel as being a document URI... 8204 aOpenedChannel->GetLoadFlags(&loadFlags); 8205 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI; 8206 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo(); 8207 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) { 8208 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE; 8209 } 8210 8211 aOpenedChannel->SetLoadFlags(loadFlags); 8212 8213 mLoadGroup->AddRequest(aRequest, nullptr); 8214 if (currentLoadGroup) { 8215 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED); 8216 } 8217 8218 // Update the notification callbacks, so that progress and 8219 // status information are sent to the right docshell... 8220 aOpenedChannel->SetNotificationCallbacks(this); 8221 } 8222 8223 if (mLoadingEntry && mBrowsingContext->IsTop() && 8224 !ShouldAddToSessionHistory(finalURI, aOpenedChannel)) { 8225 mLoadingEntry->mInfo.SetTransient(); 8226 } 8227 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false, aOpenedChannel, previousURI), 8228 NS_ERROR_FAILURE); 8229 8230 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) { 8231 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true)); 8232 } 8233 8234 mSavedRefreshURIList = nullptr; 8235 mSavingOldViewer = false; 8236 mEODForCurrentDocument = false; 8237 8238 // if this document is part of a multipart document, 8239 // the ID can be used to distinguish it from the other parts. 8240 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest)); 8241 if (multiPartChannel) { 8242 if (PresShell* presShell = GetPresShell()) { 8243 if (Document* doc = presShell->GetDocument()) { 8244 uint32_t partID; 8245 multiPartChannel->GetPartID(&partID); 8246 doc->SetPartID(partID); 8247 } 8248 } 8249 } 8250 8251 if (errorOnLocationChangeNeeded) { 8252 FireOnLocationChange(this, failedChannel, failedURI, 8253 LOCATION_CHANGE_ERROR_PAGE); 8254 } else if (onLocationChangeNeeded) { 8255 uint32_t locationFlags = 8256 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0; 8257 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags); 8258 } 8259 8260 // Arrange to show a Tor onion service client authentication prompt if 8261 // appropriate. 8262 if ((mLoadType == LOAD_ERROR_PAGE) && failedChannel) { 8263 nsresult status = NS_OK; 8264 if (NS_SUCCEEDED(failedChannel->GetStatus(&status)) && 8265 ((status == NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH) || 8266 (status == NS_ERROR_TOR_ONION_SVC_BAD_CLIENT_AUTH))) { 8267 nsAutoCString onionHost; 8268 failedURI->GetHost(onionHost); 8269 const char* topic = (status == NS_ERROR_TOR_ONION_SVC_MISSING_CLIENT_AUTH) 8270 ? "tor-onion-services-clientauth-missing" 8271 : "tor-onion-services-clientauth-incorrect"; 8272 if (XRE_IsContentProcess()) { 8273 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild(); 8274 if (browserChild) { 8275 static_cast<BrowserChild*>(browserChild.get()) 8276 ->SendShowOnionServicesAuthPrompt(onionHost, nsCString(topic)); 8277 } 8278 } else { 8279 nsCOMPtr<nsPIDOMWindowOuter> browserWin = GetWindow(); 8280 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 8281 if (browserWin && obsSvc) { 8282 obsSvc->NotifyObservers(browserWin, topic, 8283 NS_ConvertUTF8toUTF16(onionHost).get()); 8284 } 8285 } 8286 } 8287 } 8288 8289 return NS_OK; 8290 } 8291 8292 nsresult nsDocShell::NewDocumentViewerObj(const nsACString& aContentType, 8293 nsIRequest* aRequest, 8294 nsILoadGroup* aLoadGroup, 8295 nsIStreamListener** aContentHandler, 8296 nsIDocumentViewer** aViewer) { 8297 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest); 8298 8299 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory = 8300 nsContentUtils::FindInternalDocumentViewer(aContentType); 8301 if (!docLoaderFactory) { 8302 return NS_ERROR_FAILURE; 8303 } 8304 8305 // Now create an instance of the content viewer nsLayoutDLF makes the 8306 // determination if it should be a "view-source" instead of "view" 8307 nsresult rv = docLoaderFactory->CreateInstance( 8308 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr, 8309 aContentHandler, aViewer); 8310 NS_ENSURE_SUCCESS(rv, rv); 8311 8312 (*aViewer)->SetContainer(this); 8313 return NS_OK; 8314 } 8315 8316 nsresult nsDocShell::SetupNewViewer(nsIDocumentViewer* aNewViewer, 8317 WindowGlobalChild* aWindowActor) { 8318 MOZ_ASSERT(!mIsBeingDestroyed); 8319 8320 // 8321 // Copy content viewer state from previous or parent content viewer. 8322 // 8323 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad! 8324 // 8325 // Do NOT to maintain a reference to the old content viewer outside 8326 // of this "copying" block, or it will not be destroyed until the end of 8327 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315) 8328 // 8329 // In this block of code, if we get an error result, we return it 8330 // but if we get a null pointer, that's perfectly legal for parent 8331 // and parentDocumentViewer. 8332 // 8333 8334 int32_t x = 0; 8335 int32_t y = 0; 8336 int32_t cx = 0; 8337 int32_t cy = 0; 8338 8339 // This will get the size from the current content viewer or from the 8340 // Init settings 8341 DoGetPositionAndSize(&x, &y, &cx, &cy); 8342 8343 nsCOMPtr<nsIDocShellTreeItem> parentAsItem; 8344 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)), 8345 NS_ERROR_FAILURE); 8346 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem)); 8347 8348 const Encoding* reloadEncoding = nullptr; 8349 int32_t reloadEncodingSource = kCharsetUninitialized; 8350 // |newMUDV| also serves as a flag to set the data from the above vars 8351 nsCOMPtr<nsIDocumentViewer> newViewer; 8352 8353 if (mDocumentViewer || parent) { 8354 nsCOMPtr<nsIDocumentViewer> oldViewer; 8355 if (mDocumentViewer) { 8356 // Get any interesting state from old content viewer 8357 // XXX: it would be far better to just reuse the document viewer , 8358 // since we know we're just displaying the same document as before 8359 oldViewer = mDocumentViewer; 8360 8361 // Tell the old content viewer to hibernate in session history when 8362 // it is destroyed. 8363 8364 if (mSavingOldViewer && NS_FAILED(CaptureState())) { 8365 if (mOSHE) { 8366 mOSHE->SyncPresentationState(); 8367 } 8368 mSavingOldViewer = false; 8369 } 8370 } else { 8371 // No old content viewer, so get state from parent's content viewer 8372 parent->GetDocViewer(getter_AddRefs(oldViewer)); 8373 } 8374 8375 if (oldViewer) { 8376 newViewer = aNewViewer; 8377 if (newViewer) { 8378 reloadEncoding = 8379 oldViewer->GetReloadEncodingAndSource(&reloadEncodingSource); 8380 } 8381 } 8382 } 8383 8384 SingleCanvasBackground canvasBg = {}; 8385 bool isUnderHiddenEmbedderElement = false; 8386 // Ensure that the content viewer is destroyed *after* the GC - bug 71515 8387 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer; 8388 if (viewer) { 8389 // Stop any activity that may be happening in the old document before 8390 // releasing it... 8391 viewer->Stop(); 8392 8393 // Try to extract the canvas background color from the old 8394 // presentation shell, so we can use it for the next document. 8395 if (PresShell* presShell = viewer->GetPresShell()) { 8396 canvasBg = presShell->GetViewportCanvasBackground(); 8397 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement(); 8398 } 8399 8400 viewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr); 8401 aNewViewer->SetPreviousViewer(viewer); 8402 } 8403 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) { 8404 // We don't plan to save a viewer in mOSHE; tell it to drop 8405 // any other state it's holding. 8406 mOSHE->SyncPresentationState(); 8407 } 8408 8409 mDocumentViewer = nullptr; 8410 8411 // Now that we're about to switch documents, forget all of our children. 8412 // Note that we cached them as needed up in CaptureState above. 8413 DestroyChildren(); 8414 8415 mDocumentViewer = aNewViewer; 8416 8417 nsCOMPtr<nsIWidget> widget = GetMainWidget(); 8418 LayoutDeviceIntRect bounds(x, y, cx, cy); 8419 8420 mDocumentViewer->SetNavigationTiming(mTiming); 8421 8422 if (NS_FAILED(mDocumentViewer->Init(widget, bounds, aWindowActor))) { 8423 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer; 8424 viewer->Close(nullptr); 8425 viewer->Destroy(); 8426 mDocumentViewer = nullptr; 8427 SetCurrentURIInternal(nullptr); 8428 NS_WARNING("DocumentViewer Initialization failed"); 8429 return NS_ERROR_FAILURE; 8430 } 8431 8432 // If we have old state to copy, set the old state onto the new content 8433 // viewer 8434 if (newViewer) { 8435 newViewer->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource); 8436 } 8437 8438 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE); 8439 8440 // Stuff the bgcolor from the old pres shell into the new 8441 // pres shell. This improves page load continuity. 8442 if (RefPtr<PresShell> presShell = mDocumentViewer->GetPresShell()) { 8443 presShell->SetViewportCanvasBackground(canvasBg); 8444 presShell->ActivenessMaybeChanged(); 8445 if (isUnderHiddenEmbedderElement) { 8446 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement); 8447 } 8448 } 8449 8450 // XXX: It looks like the LayoutState gets restored again in Embed() 8451 // right after the call to SetupNewViewer(...) 8452 8453 // We don't show the mDocumentViewer yet, since we want to draw the old page 8454 // until we have enough of the new page to show. Just return with the new 8455 // viewer still set to hidden. 8456 8457 return NS_OK; 8458 } 8459 8460 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry, 8461 SessionHistoryInfo* aInfo) { 8462 NS_ENSURE_TRUE_VOID(mDocumentViewer); 8463 8464 RefPtr<Document> document = GetDocument(); 8465 NS_ENSURE_TRUE_VOID(document); 8466 8467 nsCOMPtr<nsIStructuredCloneContainer> scContainer; 8468 if (mozilla::SessionHistoryInParent()) { 8469 // If aInfo is null, just set the document's state object to null. 8470 if (aInfo) { 8471 scContainer = aInfo->GetStateData(); 8472 } 8473 MOZ_LOG(gSHLog, LogLevel::Debug, 8474 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get())); 8475 } else { 8476 if (aShEntry) { 8477 scContainer = aShEntry->GetStateData(); 8478 8479 // If aShEntry is null, just set the document's state object to null. 8480 } 8481 } 8482 8483 // It's OK for scContainer too be null here; that just means there's no 8484 // state data associated with this history entry. 8485 document->SetStateObject(scContainer); 8486 } 8487 8488 nsresult nsDocShell::CheckLoadingPermissions() { 8489 // This method checks whether the caller may load content into 8490 // this docshell. Even though we've done our best to hide windows 8491 // from code that doesn't have the right to access them, it's 8492 // still possible for an evil site to open a window and access 8493 // frames in the new window through window.frames[] (which is 8494 // allAccess for historic reasons), so we still need to do this 8495 // check on load. 8496 nsresult rv = NS_OK; 8497 8498 if (!IsSubframe()) { 8499 // We're not a frame. Permit all loads. 8500 return rv; 8501 } 8502 8503 // Note - The check for a current JSContext here isn't necessarily sensical. 8504 // It's just designed to preserve the old semantics during a mass-conversion 8505 // patch. 8506 if (!nsContentUtils::GetCurrentJSContext()) { 8507 return NS_OK; 8508 } 8509 8510 // Check if the caller is from the same origin as this docshell, 8511 // or any of its ancestors. 8512 nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal(); 8513 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc; 8514 bc = bc->GetParent()) { 8515 // If the BrowsingContext is not in process, then it 8516 // is true by construction that its principal will not 8517 // subsume the current docshell principal. 8518 if (!bc->IsInProcess()) { 8519 continue; 8520 } 8521 8522 nsCOMPtr<nsIScriptGlobalObject> sgo = 8523 bc->GetDocShell()->GetScriptGlobalObject(); 8524 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo)); 8525 8526 nsIPrincipal* p; 8527 if (!sop || !(p = sop->GetPrincipal())) { 8528 return NS_ERROR_UNEXPECTED; 8529 } 8530 8531 // file: URIs are considered the same domain for the purpose of frame 8532 // navigation by clicking a targeted link, regardless of script 8533 // accessibility (bug 1934807). 8534 if (subjectPrincipal->Subsumes(p) || 8535 (subjectPrincipal->SchemeIs("file") && p->SchemeIs("file"))) { 8536 // Same origin, permit load 8537 return NS_OK; 8538 } 8539 } 8540 8541 return NS_ERROR_DOM_PROP_ACCESS_DENIED; 8542 } 8543 8544 //***************************************************************************** 8545 // nsDocShell: Site Loading 8546 //***************************************************************************** 8547 8548 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI, 8549 bool aInPrivateBrowsing) { 8550 if (XRE_IsContentProcess()) { 8551 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); 8552 if (contentChild) { 8553 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing); 8554 } 8555 return; 8556 } 8557 8558 #ifdef MOZ_PLACES 8559 auto* faviconService = nsFaviconService::GetFaviconService(); 8560 if (faviconService) { 8561 faviconService->AsyncTryCopyFavicons( 8562 aOldURI, aNewURI, 8563 aInPrivateBrowsing ? nsIFaviconService::FAVICON_LOAD_PRIVATE 8564 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE); 8565 } 8566 #endif 8567 } 8568 8569 class InternalLoadEvent : public Runnable { 8570 public: 8571 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState) 8572 : mozilla::Runnable("InternalLoadEvent"), 8573 mDocShell(aDocShell), 8574 mLoadState(aLoadState) { 8575 // For events, both target and filename should be the version of "null" they 8576 // expect. By the time the event is fired, both window targeting and file 8577 // downloading have been handled, so we should never have an internal load 8578 // event that retargets or had a download. 8579 mLoadState->SetTarget(u""_ns); 8580 mLoadState->SetFileName(VoidString()); 8581 } 8582 8583 NS_IMETHOD 8584 Run() override { 8585 #ifndef ANDROID 8586 MOZ_ASSERT(mLoadState->TriggeringPrincipal(), 8587 "InternalLoadEvent: Should always have a principal here"); 8588 #endif 8589 return mDocShell->InternalLoad(mLoadState); 8590 } 8591 8592 private: 8593 RefPtr<nsDocShell> mDocShell; 8594 RefPtr<nsDocShellLoadState> mLoadState; 8595 }; 8596 8597 /** 8598 * Returns true if we started an asynchronous load (i.e., from the network), but 8599 * the document we're loading there hasn't yet become this docshell's active 8600 * document. 8601 * 8602 * When JustStartedNetworkLoad is true, you should be careful about modifying 8603 * mLoadType and mLSHE. These are both set when the asynchronous load first 8604 * starts, and the load expects that, when it eventually runs InternalLoad, 8605 * mLoadType and mLSHE will have their original values. 8606 */ 8607 bool nsDocShell::JustStartedNetworkLoad() { 8608 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel(); 8609 } 8610 8611 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a 8612 // non-toplevel browsing context in spec terms. (frame, iframe, <object>, 8613 // <embed>, etc) 8614 // 8615 // This return value will be used when we call NS_CheckContentLoadPolicy, and 8616 // later when we call DoURILoad. 8617 nsContentPolicyType nsDocShell::DetermineContentType() { 8618 if (!IsSubframe()) { 8619 return nsIContentPolicy::TYPE_DOCUMENT; 8620 } 8621 8622 const auto& maybeEmbedderElementType = 8623 GetBrowsingContext()->GetEmbedderElementType(); 8624 if (!maybeEmbedderElementType) { 8625 // If the EmbedderElementType hasn't been set yet, just assume we're 8626 // an iframe since that's more common. 8627 return nsIContentPolicy::TYPE_INTERNAL_IFRAME; 8628 } 8629 8630 return maybeEmbedderElementType->EqualsLiteral("iframe") 8631 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME 8632 : nsIContentPolicy::TYPE_INTERNAL_FRAME; 8633 } 8634 8635 bool nsDocShell::NoopenerForceEnabled() { 8636 // If current's top-level browsing context's active document's 8637 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then 8638 // if currentDoc's origin is not same origin with currentDoc's top-level 8639 // origin, noopener is force enabled, and name is cleared to "_blank". 8640 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy(); 8641 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN || 8642 topPolicy == 8643 nsILoadInfo:: 8644 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) && 8645 !mBrowsingContext->SameOriginWithTop(); 8646 } 8647 8648 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) { 8649 MOZ_ASSERT(aLoadState, "need a load state!"); 8650 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!"); 8651 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(), 8652 "should not have picked target yet"); 8653 8654 nsresult rv = NS_OK; 8655 RefPtr<BrowsingContext> targetContext; 8656 8657 // Only _self, _parent, and _top are supported in noopener case. But we 8658 // have to be careful to not apply that to the noreferrer case. See bug 8659 // 1358469. 8660 bool allowNamedTarget = 8661 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) || 8662 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER); 8663 if (allowNamedTarget || 8664 aLoadState->Target().LowerCaseEqualsLiteral("_self") || 8665 aLoadState->Target().LowerCaseEqualsLiteral("_parent") || 8666 aLoadState->Target().LowerCaseEqualsLiteral("_top")) { 8667 Document* document = GetDocument(); 8668 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); 8669 WindowGlobalChild* wgc = document->GetWindowGlobalChild(); 8670 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE); 8671 targetContext = wgc->FindBrowsingContextWithName( 8672 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false); 8673 } 8674 8675 if (!targetContext) { 8676 // If the targetContext doesn't exist, then this is a new docShell and we 8677 // should consider this a TYPE_DOCUMENT load 8678 // 8679 // For example, when target="_blank" 8680 8681 // If there's no targetContext, that means we are about to create a new 8682 // window. Perform a content policy check before creating the window. Please 8683 // note for all other docshell loads content policy checks are performed 8684 // within the contentSecurityManager when the channel is about to be 8685 // openend. 8686 nsISupports* requestingContext = nullptr; 8687 if (XRE_IsContentProcess()) { 8688 // In e10s the child process doesn't have access to the element that 8689 // contains the browsing context (because that element is in the chrome 8690 // process). So we just pass mScriptGlobal. 8691 requestingContext = ToSupports(mScriptGlobal); 8692 } else { 8693 // This is for loading non-e10s tabs and toplevel windows of various 8694 // sorts. 8695 // For the toplevel window cases, requestingElement will be null. 8696 nsCOMPtr<Element> requestingElement = 8697 mScriptGlobal->GetFrameElementInternal(); 8698 requestingContext = requestingElement; 8699 } 8700 8701 // Ideally we should use the same loadinfo as within DoURILoad which 8702 // should match this one when both are applicable. 8703 nsCOMPtr<nsILoadInfo> secCheckLoadInfo = 8704 new LoadInfo(mScriptGlobal, aLoadState->URI(), 8705 aLoadState->TriggeringPrincipal(), requestingContext, 8706 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0); 8707 8708 // Since Content Policy checks are performed within docShell as well as 8709 // the ContentSecurityManager we need a reliable way to let certain 8710 // nsIContentPolicy consumers ignore duplicate calls. 8711 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true); 8712 8713 int16_t shouldLoad = nsIContentPolicy::ACCEPT; 8714 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo, 8715 &shouldLoad); 8716 8717 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) { 8718 if (NS_SUCCEEDED(rv)) { 8719 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) { 8720 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT; 8721 } 8722 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) { 8723 return NS_ERROR_BLOCKED_BY_POLICY; 8724 } 8725 } 8726 8727 return NS_ERROR_CONTENT_BLOCKED; 8728 } 8729 } 8730 8731 // 8732 // Resolve the window target before going any further... 8733 // If the load has been targeted to another DocShell, then transfer the 8734 // load to it... 8735 // 8736 8737 // We've already done our owner-inheriting. Mask out that bit, so we 8738 // don't try inheriting an owner from the target window if we came up 8739 // with a null owner above. 8740 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL); 8741 8742 if (!targetContext) { 8743 // If the docshell's document is sandboxed, only open a new window 8744 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set. 8745 // (i.e. if allow-popups is specified) 8746 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE); 8747 Document* doc = mDocumentViewer->GetDocument(); 8748 8749 const bool isDocumentAuxSandboxed = 8750 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION); 8751 8752 if (isDocumentAuxSandboxed) { 8753 return NS_ERROR_DOM_INVALID_ACCESS_ERR; 8754 } 8755 8756 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow(); 8757 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE); 8758 8759 RefPtr<BrowsingContext> newBC; 8760 nsAutoCString spec; 8761 aLoadState->URI()->GetSpec(spec); 8762 8763 // If we are a noopener load, we just hand the whole thing over to our 8764 // window. 8765 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) || 8766 NoopenerForceEnabled()) { 8767 // Various asserts that we know to hold because NO_OPENER loads can only 8768 // happen for links. 8769 MOZ_ASSERT(!aLoadState->LoadReplace()); 8770 MOZ_ASSERT(aLoadState->PrincipalToInherit() == 8771 aLoadState->TriggeringPrincipal()); 8772 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() & 8773 ~(INTERNAL_LOAD_FLAGS_NO_OPENER | 8774 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)), 8775 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and " 8776 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set"); 8777 MOZ_ASSERT_IF(aLoadState->PostDataStream(), 8778 aLoadState->IsFormSubmission()); 8779 MOZ_ASSERT(!aLoadState->HeadersStream()); 8780 // If OnLinkClickSync was invoked inside the onload handler, the load 8781 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be 8782 // LOAD_LINK. 8783 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK || 8784 aLoadState->LoadType() == LOAD_NORMAL_REPLACE); 8785 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory()); 8786 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this. 8787 8788 RefPtr<nsDocShellLoadState> loadState = 8789 new nsDocShellLoadState(aLoadState->URI()); 8790 8791 // Set up our loadinfo so it will do the load as much like we would have 8792 // as possible. 8793 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo()); 8794 loadState->SetOriginalURI(aLoadState->OriginalURI()); 8795 8796 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI; 8797 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI); 8798 8799 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI); 8800 loadState->SetKeepResultPrincipalURIIfSet( 8801 aLoadState->KeepResultPrincipalURIIfSet()); 8802 // LoadReplace will always be false due to asserts above, skip setting 8803 // it. 8804 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal()); 8805 loadState->SetTriggeringSandboxFlags( 8806 aLoadState->TriggeringSandboxFlags()); 8807 loadState->SetTriggeringWindowId(aLoadState->TriggeringWindowId()); 8808 loadState->SetTriggeringStorageAccess( 8809 aLoadState->TriggeringStorageAccess()); 8810 loadState->SetTriggeringClassificationFlags( 8811 aLoadState->TriggeringClassificationFlags()); 8812 loadState->SetPolicyContainer(aLoadState->PolicyContainer()); 8813 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags( 8814 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)); 8815 // Explicit principal because we do not want any guesses as to what the 8816 // principal to inherit is: it should be aTriggeringPrincipal. 8817 loadState->SetPrincipalIsExplicit(true); 8818 loadState->SetLoadType(aLoadState->LoadType()); 8819 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags( 8820 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI)); 8821 8822 loadState->SetHasValidUserGestureActivation( 8823 aLoadState->HasValidUserGestureActivation()); 8824 8825 loadState->SetTextDirectiveUserActivation( 8826 aLoadState->GetTextDirectiveUserActivation()); 8827 8828 // Propagate POST data to the new load. 8829 loadState->SetPostDataStream(aLoadState->PostDataStream()); 8830 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission()); 8831 8832 loadState->SetNavigationAPIState(aLoadState->GetNavigationAPIState()); 8833 8834 rv = win->Open(spec, 8835 aLoadState->Target(), // window name 8836 u""_ns, // Features 8837 loadState, 8838 true, // aForceNoOpener 8839 getter_AddRefs(newBC)); 8840 MOZ_ASSERT(!newBC); 8841 return rv; 8842 } 8843 8844 rv = win->OpenNoNavigate(spec, 8845 aLoadState->Target(), // window name 8846 u""_ns, // Features 8847 getter_AddRefs(newBC)); 8848 8849 // In some cases the Open call doesn't actually result in a new 8850 // window being opened. We can detect these cases by examining the 8851 // document in |newBC|, if any. 8852 nsCOMPtr<nsPIDOMWindowOuter> piNewWin = 8853 newBC ? newBC->GetDOMWindow() : nullptr; 8854 if (piNewWin) { 8855 RefPtr<Document> newDoc = piNewWin->GetExtantDoc(); 8856 if (!newDoc || newDoc->IsInitialDocument()) { 8857 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD); 8858 } 8859 } 8860 8861 if (newBC) { 8862 targetContext = newBC; 8863 } 8864 } 8865 NS_ENSURE_SUCCESS(rv, rv); 8866 NS_ENSURE_TRUE(targetContext, rv); 8867 8868 // If our target BrowsingContext is still pending initialization, ignore the 8869 // navigation request targeting it. 8870 if (NS_WARN_IF(targetContext->GetPendingInitialization())) { 8871 return NS_OK; 8872 } 8873 8874 aLoadState->SetTargetBrowsingContext(targetContext); 8875 if (aLoadState->IsFormSubmission()) { 8876 aLoadState->SetLoadType( 8877 GetLoadTypeForFormSubmission(targetContext, aLoadState)); 8878 } 8879 8880 // 8881 // Transfer the load to the target BrowsingContext... Clear the window target 8882 // name to the empty string to prevent recursive retargeting! 8883 // 8884 // No window target 8885 aLoadState->SetTarget(u""_ns); 8886 // No forced download 8887 aLoadState->SetFileName(VoidString()); 8888 return targetContext->InternalLoad(aLoadState); 8889 } 8890 8891 static nsAutoCString RefMaybeNull(nsIURI* aURI) { 8892 nsAutoCString result; 8893 if (NS_FAILED(aURI->GetRef(result))) { 8894 result.SetIsVoid(true); 8895 } 8896 return result; 8897 } 8898 8899 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) { 8900 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT; 8901 8902 bool equal = false; 8903 if (mCurrentURI && 8904 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal && 8905 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) { 8906 flags |= LOCATION_CHANGE_HASHCHANGE; 8907 } 8908 8909 return flags; 8910 } 8911 8912 struct SameDocumentNavigationState { 8913 nsAutoCString mCurrentHash; 8914 nsAutoCString mNewHash; 8915 nsTArray<TextDirective> mTextDirectives; 8916 bool mCurrentURIHasRef = false; 8917 bool mNewURIHasRef = false; 8918 bool mSameExceptHashes = false; 8919 bool mSecureUpgradeURI = false; 8920 bool mHistoryNavBetweenSameDoc = false; 8921 bool mIdentical = false; 8922 }; 8923 8924 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState, 8925 SameDocumentNavigationState& aState) { 8926 MOZ_ASSERT(aLoadState); 8927 if (!(aLoadState->LoadType() == LOAD_NORMAL || 8928 aLoadState->LoadType() == LOAD_STOP_CONTENT || 8929 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), 8930 LOAD_FLAGS_REPLACE_HISTORY) || 8931 aLoadState->LoadType() == LOAD_HISTORY || 8932 aLoadState->LoadType() == LOAD_LINK)) { 8933 return false; 8934 } 8935 8936 if (GetExtantDocument() && 8937 GetExtantDocument()->IsUncommittedInitialDocument()) { 8938 MOZ_LOG(gSHLog, LogLevel::Debug, 8939 ("nsDocShell::IsSameDocumentNavigation %p false, document is " 8940 "uncommitted initial", 8941 this)); 8942 return false; 8943 } 8944 8945 nsCOMPtr<nsIURI> currentURI = mCurrentURI; 8946 8947 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash); 8948 if (NS_SUCCEEDED(rvURINew)) { 8949 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef); 8950 } 8951 8952 // A Fragment Directive must be removed from the new hash in order to allow 8953 // fallback element id scroll. 8954 FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragmentString( 8955 aState.mNewHash, &aState.mTextDirectives, aLoadState->URI()); 8956 8957 if (currentURI && NS_SUCCEEDED(rvURINew)) { 8958 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash); 8959 if (NS_SUCCEEDED(rvURIOld)) { 8960 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef); 8961 } 8962 if (NS_SUCCEEDED(rvURIOld)) { 8963 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(), 8964 &aState.mSameExceptHashes))) { 8965 aState.mSameExceptHashes = false; 8966 } 8967 } 8968 } 8969 8970 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) { 8971 // Maybe aLoadState->URI() came from the exposable form of currentURI? 8972 nsCOMPtr<nsIURI> currentExposableURI = 8973 nsIOService::CreateExposableURI(currentURI); 8974 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash); 8975 if (NS_SUCCEEDED(rvURIOld)) { 8976 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef); 8977 } 8978 if (NS_SUCCEEDED(rvURIOld)) { 8979 if (NS_FAILED(currentExposableURI->EqualsExceptRef( 8980 aLoadState->URI(), &aState.mSameExceptHashes))) { 8981 aState.mSameExceptHashes = false; 8982 } 8983 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we 8984 // have to perform a special check here to avoid an actual navigation. If 8985 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the 8986 // fact that the new URI is currently http), then set mSameExceptHashes to 8987 // true and only perform a fragment navigation. 8988 if (!aState.mSameExceptHashes) { 8989 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) { 8990 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo(); 8991 nsHTTPSOnlyUtils::UpgradeMode upgradeMode = 8992 nsHTTPSOnlyUtils::GetUpgradeMode(docLoadInfo); 8993 if (!docLoadInfo->GetLoadErrorPage() && 8994 (upgradeMode == nsHTTPSOnlyUtils::HTTPS_ONLY_MODE || 8995 upgradeMode == nsHTTPSOnlyUtils::HTTPS_FIRST_MODE) && 8996 nsHTTPSOnlyUtils::IsHttpDowngrade(currentExposableURI, 8997 aLoadState->URI())) { 8998 uint32_t status = docLoadInfo->GetHttpsOnlyStatus(); 8999 if ((status & 9000 (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED | 9001 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) && 9002 !(status & nsILoadInfo::HTTPS_ONLY_EXEMPT)) { 9003 // At this point the requested URI is for sure a fragment 9004 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is 9005 // enabled. Also it is not interfering the upgrade order of 9006 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953. 9007 // Since we are on an HTTPS site the fragment 9008 // navigation should also be an HTTPS. 9009 // For that reason we should upgrade the URI to HTTPS. 9010 aState.mSecureUpgradeURI = true; 9011 aState.mSameExceptHashes = true; 9012 } 9013 } 9014 } 9015 } 9016 } 9017 } 9018 9019 if (mozilla::SessionHistoryInParent()) { 9020 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) { 9021 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith( 9022 aLoadState->GetLoadingSessionHistoryInfo()->mInfo); 9023 } 9024 MOZ_LOG(gSHLog, LogLevel::Debug, 9025 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d", 9026 this, aState.mHistoryNavBetweenSameDoc)); 9027 } else { 9028 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) { 9029 // We're doing a history load. 9030 9031 mOSHE->SharesDocumentWith(aLoadState->SHEntry(), 9032 &aState.mHistoryNavBetweenSameDoc); 9033 } 9034 } 9035 9036 // Two URIs are identical if they're same except hashes, they both have 9037 // hashes, and their hashes are the same. 9038 aState.mIdentical = aState.mSameExceptHashes && 9039 (aState.mNewURIHasRef == aState.mCurrentURIHasRef) && 9040 aState.mCurrentHash.Equals(aState.mNewHash); 9041 9042 // A same document navigation happens when we navigate between two SHEntries 9043 // for the same document. We do a same document navigation under two 9044 // circumstances. Either 9045 // 9046 // a) we're navigating between two different SHEntries which share a 9047 // document, or 9048 // 9049 // b) we're navigating to a new shentry whose URI differs from the 9050 // current URI only in its hash, the new hash is non-empty, and 9051 // we're not doing a POST. 9052 // 9053 // The restriction that the SHEntries in (a) must be different ensures 9054 // that history.go(0) and the like trigger full refreshes, rather than 9055 // same document navigations. 9056 if (!mozilla::SessionHistoryInParent()) { 9057 bool doSameDocumentNavigation = 9058 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) || 9059 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() && 9060 aState.mSameExceptHashes && aState.mNewURIHasRef); 9061 MOZ_LOG(gSHLog, LogLevel::Debug, 9062 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this, 9063 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation)); 9064 return doSameDocumentNavigation; 9065 } 9066 9067 if (aState.mHistoryNavBetweenSameDoc && 9068 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) { 9069 return true; 9070 } 9071 9072 MOZ_LOG( 9073 gSHLog, LogLevel::Debug, 9074 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s " 9075 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s", 9076 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false", 9077 !aLoadState->PostDataStream() ? "true" : "false", 9078 aState.mSameExceptHashes ? "true" : "false", 9079 aState.mNewURIHasRef ? "true" : "false")); 9080 return !aLoadState->LoadIsFromSessionHistory() && 9081 !aLoadState->PostDataStream() && aState.mSameExceptHashes && 9082 aState.mNewURIHasRef; 9083 } 9084 9085 static bool IsSamePrincipalForDocumentURI(nsIPrincipal* aCurrentPrincipal, 9086 nsIURI* aCurrentURI, 9087 nsIURI* aNewURI) { 9088 nsCOMPtr<nsIURI> principalURI = aCurrentPrincipal->GetURI(); 9089 if (aCurrentPrincipal->GetIsNullPrincipal()) { 9090 if (nsCOMPtr<nsIPrincipal> precursor = 9091 aCurrentPrincipal->GetPrecursorPrincipal()) { 9092 principalURI = precursor->GetURI(); 9093 } 9094 } 9095 9096 return !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI, 9097 aNewURI) && 9098 !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI, 9099 aCurrentURI) && 9100 !nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(aCurrentURI, 9101 aNewURI); 9102 } 9103 9104 nsresult nsDocShell::HandleSameDocumentNavigation( 9105 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState, 9106 bool& aSameDocument) { 9107 aSameDocument = true; 9108 #ifdef DEBUG 9109 SameDocumentNavigationState state; 9110 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state)); 9111 #endif 9112 9113 MOZ_LOG(gSHLog, LogLevel::Debug, 9114 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this, 9115 mCurrentURI->GetSpecOrDefault().get(), 9116 aLoadState->URI()->GetSpecOrDefault().get())); 9117 9118 RefPtr<Document> doc = GetDocument(); 9119 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 9120 9121 nsCOMPtr<nsIURI> currentURI = mCurrentURI; 9122 9123 // We need to upgrade the new URI from http: to https: 9124 nsCOMPtr<nsIURI> newURI = aLoadState->URI(); 9125 if (aState.mSecureUpgradeURI) { 9126 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI))); 9127 MOZ_LOG(gSHLog, LogLevel::Debug, 9128 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get())); 9129 } 9130 9131 // check if documentPrincipal, mCurrentURI, and aLoadState->URI() are same 9132 // origin skip handling otherwise 9133 if (!IsSamePrincipalForDocumentURI(doc->NodePrincipal(), mCurrentURI, 9134 newURI)) { 9135 aSameDocument = false; 9136 MOZ_LOG(gSHLog, LogLevel::Debug, 9137 ("nsDocShell[%p]: possible violation of the same origin policy " 9138 "during same document navigation", 9139 this)); 9140 return NS_OK; 9141 } 9142 9143 // https://html.spec.whatwg.org/#navigate-fragid 9144 // Step 2 9145 RefPtr<nsIStructuredCloneContainer> destinationNavigationAPIState = 9146 mActiveEntry ? mActiveEntry->GetNavigationAPIState() : nullptr; 9147 // Step 3 9148 if (auto* navigationAPIState = aLoadState->GetNavigationAPIState()) { 9149 destinationNavigationAPIState = navigationAPIState; 9150 } 9151 9152 if (nsCOMPtr<nsPIDOMWindowInner> window = doc->GetInnerWindow(); 9153 window && !aState.mHistoryNavBetweenSameDoc) { 9154 // https://html.spec.whatwg.org/#navigate-fragid 9155 // Step 1 9156 if (RefPtr<Navigation> navigation = window->Navigation()) { 9157 AutoJSAPI jsapi; 9158 if (jsapi.Init(window)) { 9159 RefPtr<Element> sourceElement = aLoadState->GetSourceElement(); 9160 // Step 4 9161 RefPtr apiMethodTracker = aLoadState->GetNavigationAPIMethodTracker(); 9162 bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent( 9163 jsapi.cx(), aLoadState->GetNavigationType(), newURI, 9164 /* aIsSameDocument */ true, 9165 Some(aLoadState->UserNavigationInvolvement()), sourceElement, 9166 /* aFormDataEntryList */ nullptr, 9167 /* aNavigationAPIState */ destinationNavigationAPIState, 9168 /* aClassicHistoryAPIState */ nullptr, apiMethodTracker); 9169 9170 // Step 5 9171 if (!shouldContinue) { 9172 return NS_OK; 9173 } 9174 } 9175 } 9176 } 9177 9178 doc->DoNotifyPossibleTitleChange(); 9179 9180 // Store the pending uninvoked directives if it is a same document 9181 // navigation. We need to set it here, in case the navigation happens before 9182 // the document has actually finished loading. 9183 doc->FragmentDirective()->SetTextDirectives( 9184 std::move(aState.mTextDirectives)); 9185 9186 #ifdef DEBUG 9187 if (aState.mSameExceptHashes) { 9188 bool sameExceptHashes = false; 9189 currentURI->EqualsExceptRef(newURI, &sameExceptHashes); 9190 MOZ_ASSERT(sameExceptHashes); 9191 } 9192 #endif 9193 const nsCOMPtr<nsILoadInfo> loadInfo = 9194 doc->GetChannel() ? doc->GetChannel()->LoadInfo() : nullptr; 9195 if (loadInfo) { 9196 loadInfo->SetIsSameDocumentNavigation(true); 9197 } 9198 // Save the position of the scrollers. 9199 nsPoint scrollPos = GetCurScrollPos(); 9200 9201 // Reset mLoadType to its original value once we exit this block, because this 9202 // same document navigation might have started after a normal, network load, 9203 // and we don't want to clobber its load type. See bug 737307. 9204 Maybe<AutoRestore<uint32_t>> loadTypeResetter; 9205 if (StaticPrefs:: 9206 docshell_shistory_sameDocumentNavigationOverridesLoadType() && 9207 !doc->NodePrincipal()->IsURIInPrefList( 9208 "docshell.shistory.sameDocumentNavigationOverridesLoadType." 9209 "forceDisable")) { 9210 loadTypeResetter.emplace(mLoadType); 9211 } 9212 if (JustStartedNetworkLoad() && !loadTypeResetter.isSome()) { 9213 loadTypeResetter.emplace(mLoadType); 9214 } 9215 9216 // If a non-same-document-navigation (i.e., a network load) is pending, make 9217 // this a replacement load, so that we don't add a SHEntry here and the 9218 // network load goes into the SHEntry it expects to. 9219 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) { 9220 mLoadType = LOAD_NORMAL_REPLACE; 9221 } else { 9222 mLoadType = aLoadState->LoadType(); 9223 } 9224 9225 mURIResultedInDocument = true; 9226 9227 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE; 9228 9229 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on 9230 // History loads, SetCurrentURI() called from OnNewURI() will send proper 9231 // onLocationChange() notifications to the browser to update back/forward 9232 // buttons. 9233 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()), 9234 Nothing()); 9235 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry; 9236 mLoadingEntry.swap(oldLoadingEntry); 9237 if (aLoadState->GetLoadingSessionHistoryInfo()) { 9238 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>( 9239 *aLoadState->GetLoadingSessionHistoryInfo()); 9240 mNeedToReportActiveAfterLoadingBecomesActive = false; 9241 } 9242 9243 // Set the doc's URI according to the new history entry's URI. 9244 doc->SetDocumentURI(newURI); 9245 9246 /* This is a anchor traversal within the same page. 9247 * call OnNewURI() so that, this traversal will be 9248 * recorded in session and global history. 9249 */ 9250 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit, 9251 newURIPartitionedPrincipalToInherit; 9252 nsCOMPtr<nsIPolicyContainer> newPolicyContainer; 9253 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) { 9254 if (mozilla::SessionHistoryInParent()) { 9255 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal(); 9256 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit(); 9257 newURIPartitionedPrincipalToInherit = 9258 mActiveEntry->GetPartitionedPrincipalToInherit(); 9259 newPolicyContainer = mActiveEntry->GetPolicyContainer(); 9260 } else { 9261 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal(); 9262 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit(); 9263 newURIPartitionedPrincipalToInherit = 9264 mOSHE->GetPartitionedPrincipalToInherit(); 9265 newPolicyContainer = mOSHE->GetPolicyContainer(); 9266 } 9267 } else { 9268 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal(); 9269 newURIPrincipalToInherit = doc->NodePrincipal(); 9270 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal(); 9271 newPolicyContainer = doc->GetPolicyContainer(); 9272 } 9273 9274 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI); 9275 9276 // Pass true for aCloneSHChildren, since we're not 9277 // changing documents here, so all of our subframes are 9278 // still relevant to the new session history entry. 9279 // 9280 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT 9281 // flag on firing onLocationChange(...). 9282 // Anyway, aCloneSHChildren param is simply reflecting 9283 // doSameDocumentNavigation in this scope. 9284 // 9285 // Note: we'll actually fire onLocationChange later, in order to preserve 9286 // ordering of HistoryCommit() in the parent vs onLocationChange (bug 9287 // 1668126) 9288 bool locationChangeNeeded = OnNewURI( 9289 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit, 9290 newURIPartitionedPrincipalToInherit, newPolicyContainer, true, true); 9291 9292 nsCOMPtr<nsIInputStream> postData; 9293 nsCOMPtr<nsIReferrerInfo> referrerInfo; 9294 uint32_t cacheKey = 0; 9295 9296 bool scrollRestorationIsManual = false; 9297 if (!mozilla::SessionHistoryInParent()) { 9298 if (mOSHE) { 9299 /* save current position of scroller(s) (bug 59774) */ 9300 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y); 9301 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual(); 9302 // Get the postdata, page ident and referrer info from the current page, 9303 // if the new load is being done via normal means. Note that "normal 9304 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given 9305 // the loadType and allowScroll check above -- it filters out some 9306 // LOAD_CMD_NORMAL cases that we wouldn't want here. 9307 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) { 9308 postData = mOSHE->GetPostData(); 9309 cacheKey = mOSHE->GetCacheKey(); 9310 referrerInfo = mOSHE->GetReferrerInfo(); 9311 } 9312 9313 // Link our new SHEntry to the old SHEntry's back/forward 9314 // cache data, since the two SHEntries correspond to the 9315 // same document. 9316 if (mLSHE) { 9317 if (!aLoadState->LoadIsFromSessionHistory()) { 9318 // If we're not doing a history load, scroll restoration 9319 // should be inherited from the previous session history entry. 9320 SetScrollRestorationIsManualOnHistoryEntry(mLSHE, 9321 scrollRestorationIsManual); 9322 } 9323 mLSHE->AdoptBFCacheEntry(mOSHE); 9324 } 9325 } 9326 } else { 9327 if (mActiveEntry) { 9328 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y); 9329 if (mBrowsingContext) { 9330 CollectWireframe(); 9331 if (XRE_IsParentProcess()) { 9332 SessionHistoryEntry* entry = 9333 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 9334 if (entry) { 9335 entry->SetScrollPosition(scrollPos.x, scrollPos.y); 9336 } 9337 } else { 9338 (void)ContentChild::GetSingleton() 9339 ->SendSessionHistoryEntryScrollPosition(mBrowsingContext, 9340 scrollPos.x, scrollPos.y); 9341 } 9342 } 9343 } 9344 if (mLoadingEntry) { 9345 if (!mLoadingEntry->mLoadIsFromSessionHistory) { 9346 // If we're not doing a history load, scroll restoration 9347 // should be inherited from the previous session history entry. 9348 // XXX This needs most probably tweaks once fragment navigation is 9349 // fixed to work with session-history-in-parent. 9350 SetScrollRestorationIsManualOnHistoryEntry(nullptr, 9351 scrollRestorationIsManual); 9352 } 9353 } 9354 } 9355 9356 // If we're doing a history load, use its scroll restoration state. 9357 if (aLoadState->LoadIsFromSessionHistory()) { 9358 if (mozilla::SessionHistoryInParent()) { 9359 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo() 9360 ->mInfo.GetScrollRestorationIsManual(); 9361 } else { 9362 scrollRestorationIsManual = 9363 aLoadState->SHEntry()->GetScrollRestorationIsManual(); 9364 } 9365 } 9366 9367 /* Assign mLSHE to mOSHE. This will either be a new entry created 9368 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history 9369 * loads. 9370 */ 9371 if (!mozilla::SessionHistoryInParent()) { 9372 if (mLSHE) { 9373 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE)); 9374 // Save the postData obtained from the previous page 9375 // in to the session history entry created for the 9376 // anchor page, so that any history load of the anchor 9377 // page will restore the appropriate postData. 9378 if (postData) { 9379 mOSHE->SetPostData(postData); 9380 } 9381 9382 // Make sure we won't just repost without hitting the 9383 // cache first 9384 if (cacheKey != 0) { 9385 mOSHE->SetCacheKey(cacheKey); 9386 } 9387 9388 // As the document has not changed, the referrer info hasn't changed too, 9389 // so we can just copy it over. 9390 if (referrerInfo) { 9391 mOSHE->SetReferrerInfo(referrerInfo); 9392 } 9393 } 9394 9395 /* Set the title for the SH entry for this target url so that 9396 * SH menus in go/back/forward buttons won't be empty for this. 9397 * Note, this happens on mOSHE (and mActiveEntry in the future) because of 9398 * the code above. 9399 * Note, when session history lives in the parent process, this does not 9400 * update the title there. 9401 */ 9402 SetTitleOnHistoryEntry(false); 9403 } else { 9404 if (aLoadState->LoadIsFromSessionHistory()) { 9405 MOZ_LOG( 9406 gSHLog, LogLevel::Debug, 9407 ("Moving the loading entry to the active entry on nsDocShell %p to " 9408 "%s", 9409 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get())); 9410 9411 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState; 9412 if (mActiveEntry) { 9413 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState(); 9414 } 9415 9416 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release()); 9417 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo); 9418 if (currentLayoutHistoryState) { 9419 // Restore the existing nsILayoutHistoryState object, since it is 9420 // possibly being used by the layout. When doing a new load, the 9421 // shared state is copied from the existing active entry, so this 9422 // special case is needed only with the history loads. 9423 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState); 9424 } 9425 9426 if (cacheKey != 0) { 9427 mActiveEntry->SetCacheKey(cacheKey); 9428 } 9429 9430 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit 9431 // does require a non-null uri if this is for a refresh load of the same 9432 // URI, but in that case mCurrentURI won't be null here. 9433 mBrowsingContext->SessionHistoryCommit( 9434 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(), 9435 true, 9436 /* No expiration update on the same document loads*/ 9437 false, cacheKey); 9438 // FIXME Need to set postdata. 9439 9440 // Set the title for the SH entry for this target url so that 9441 // SH menus in go/back/forward buttons won't be empty for this. 9442 // Note, when session history lives in the parent process, this does not 9443 // update the title there. 9444 SetTitleOnHistoryEntry(false); 9445 } else { 9446 Maybe<bool> scrollRestorationIsManual; 9447 if (mActiveEntry) { 9448 scrollRestorationIsManual.emplace( 9449 mActiveEntry->GetScrollRestorationIsManual()); 9450 9451 // Get the postdata, page ident and referrer info from the current page, 9452 // if the new load is being done via normal means. Note that "normal 9453 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given 9454 // the loadType and allowScroll check above -- it filters out some 9455 // LOAD_CMD_NORMAL cases that we wouldn't want here. 9456 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) { 9457 postData = mActiveEntry->GetPostData(); 9458 cacheKey = mActiveEntry->GetCacheKey(); 9459 referrerInfo = mActiveEntry->GetReferrerInfo(); 9460 } 9461 } 9462 9463 MOZ_LOG(gSHLog, LogLevel::Debug, 9464 ("Creating an active entry on nsDocShell %p to %s", this, 9465 newURI->GetSpecOrDefault().get())); 9466 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release()); 9467 if (previousActiveEntry) { 9468 mActiveEntry = 9469 MakeUnique<SessionHistoryInfo>(*previousActiveEntry, newURI); 9470 } else { 9471 mActiveEntry = MakeUnique<SessionHistoryInfo>( 9472 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit, 9473 newURIPartitionedPrincipalToInherit, newPolicyContainer, 9474 mContentTypeHint); 9475 } 9476 9477 // Save the postData obtained from the previous page in to the session 9478 // history entry created for the anchor page, so that any history load of 9479 // the anchor page will restore the appropriate postData. 9480 if (postData) { 9481 mActiveEntry->SetPostData(postData); 9482 } 9483 9484 // Make sure we won't just repost without hitting the 9485 // cache first 9486 if (cacheKey != 0) { 9487 mActiveEntry->SetCacheKey(cacheKey); 9488 } 9489 9490 // As the document has not changed, the referrer info hasn't changed too, 9491 // so we can just copy it over. 9492 if (referrerInfo) { 9493 mActiveEntry->SetReferrerInfo(referrerInfo); 9494 } 9495 9496 // Set the title for the SH entry for this target url so that 9497 // SH menus in go/back/forward buttons won't be empty for this. 9498 mActiveEntry->SetTitle(mTitle); 9499 9500 if (scrollRestorationIsManual.isSome()) { 9501 mActiveEntry->SetScrollRestorationIsManual( 9502 scrollRestorationIsManual.value()); 9503 } 9504 9505 if (destinationNavigationAPIState) { 9506 mActiveEntry->SetNavigationAPIState(destinationNavigationAPIState); 9507 } 9508 9509 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) { 9510 if (previousActiveEntry) { 9511 mActiveEntry->NavigationKey() = previousActiveEntry->NavigationKey(); 9512 } 9513 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get()); 9514 } else { 9515 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext(); 9516 // FIXME We should probably just compute mChildOffset in the parent 9517 // instead of passing it over IPC here. 9518 mBrowsingContext->SetActiveSessionHistoryEntry( 9519 Some(scrollPos), mActiveEntry.get(), previousActiveEntry.get(), 9520 mLoadType, cacheKey); 9521 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex? 9522 } 9523 } 9524 } 9525 9526 if (locationChangeNeeded) { 9527 FireOnLocationChange(this, nullptr, newURI, locationChangeFlags); 9528 } 9529 9530 /* Restore the original LSHE if we were loading something 9531 * while same document navigation was initiated. 9532 */ 9533 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing()); 9534 mLoadingEntry.swap(oldLoadingEntry); 9535 9536 /* Set the title for the Global History entry for this anchor url. 9537 */ 9538 UpdateGlobalHistoryTitle(newURI); 9539 9540 SetDocCurrentStateObj(mOSHE, mActiveEntry.get()); 9541 9542 // Inform the favicon service that the favicon for oldURI also 9543 // applies to newURI. 9544 CopyFavicon(currentURI, newURI, UsePrivateBrowsing()); 9545 9546 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal; 9547 nsCOMPtr<nsPIDOMWindowInner> win = 9548 scriptGlobal ? scriptGlobal->GetCurrentInnerWindow() : nullptr; 9549 9550 // https://html.spec.whatwg.org/#scroll-to-fragid 9551 // 14. Update document for history step application given navigable's 9552 // active document, historyEntry, true, scriptHistoryIndex, 9553 // scriptHistoryLength, and historyHandling. 9554 if (RefPtr navigation = win ? win->Navigation() : nullptr) { 9555 MOZ_LOG(gNavigationAPILog, LogLevel::Debug, 9556 ("nsDocShell %p triggering a navigation event from " 9557 "HandleSameDocumentNavigation", 9558 this)); 9559 // https://html.spec.whatwg.org/#update-document-for-history-step-application 9560 // 6.4.2. Update the navigation API entries for a same-document 9561 // navigation given navigation, entry, and navigationType. 9562 navigation->UpdateEntriesForSameDocumentNavigation( 9563 mActiveEntry.get(), 9564 NavigationUtils::NavigationTypeFromLoadType(mLoadType).valueOr( 9565 NavigationType::Push)); 9566 } 9567 9568 // The check for uninvoked directives must come before ScrollToAnchor() is 9569 // called. 9570 const bool hasTextDirectives = 9571 doc->FragmentDirective()->HasUninvokedDirectives(); 9572 9573 // https://html.spec.whatwg.org/#scroll-to-fragid 9574 // 15. Scroll to the fragment given navigable's active document. 9575 9576 // ScrollToAnchor doesn't necessarily cause us to scroll the window; 9577 // the function decides whether a scroll is appropriate based on the 9578 // arguments it receives. But even if we don't end up scrolling, 9579 // ScrollToAnchor performs other important tasks, such as informing 9580 // the presShell that we have a new hash. See bug 680257. 9581 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef, 9582 aState.mNewHash, aLoadState->LoadType()); 9583 NS_ENSURE_SUCCESS(rv, rv); 9584 9585 /* restore previous position of scroller(s), if we're moving 9586 * back in history (bug 59774) 9587 */ 9588 nscoord bx = 0; 9589 nscoord by = 0; 9590 bool needsScrollPosUpdate = false; 9591 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) && 9592 (aLoadState->LoadType() == LOAD_HISTORY || 9593 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) && 9594 !scrollRestorationIsManual) { 9595 needsScrollPosUpdate = true; 9596 if (mozilla::SessionHistoryInParent()) { 9597 mActiveEntry->GetScrollPosition(&bx, &by); 9598 } else { 9599 mOSHE->GetScrollPosition(&bx, &by); 9600 } 9601 } 9602 9603 // Dispatch the popstate and hashchange events, as appropriate. 9604 // 9605 // The event dispatch below can cause us to re-enter script and 9606 // destroy the docshell, nulling out mScriptGlobal. Hold a stack 9607 // reference to avoid null derefs. See bug 914521. 9608 if (win) { 9609 // Fire a hashchange event URIs differ, and only in their hashes. 9610 // If the fragment contains a directive, compare hasRef. 9611 bool doHashchange = aState.mSameExceptHashes && 9612 (!aState.mCurrentHash.Equals(aState.mNewHash) || 9613 (hasTextDirectives && 9614 aState.mCurrentURIHasRef != aState.mNewURIHasRef)); 9615 9616 if (aState.mHistoryNavBetweenSameDoc || doHashchange) { 9617 win->DispatchSyncPopState(); 9618 } 9619 9620 if (needsScrollPosUpdate && win->HasActiveDocument()) { 9621 SetCurScrollPosEx(bx, by); 9622 } 9623 9624 if (doHashchange) { 9625 // Note that currentURI hasn't changed because it's on the 9626 // stack, so we can just use it directly as the old URI. 9627 win->DispatchAsyncHashchange(currentURI, newURI); 9628 } 9629 } 9630 9631 return NS_OK; 9632 } 9633 9634 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell, 9635 nsDocShellLoadState* aLoadState) { 9636 if (!aLoadState->AllowFocusMove()) { 9637 return false; 9638 } 9639 if (!aLoadState->HasValidUserGestureActivation()) { 9640 return false; 9641 } 9642 const auto& sourceBC = aLoadState->SourceBrowsingContext(); 9643 if (!sourceBC || !sourceBC->IsActive()) { 9644 // If the navigation didn't come from a foreground tab, then we don't steal 9645 // focus. 9646 return false; 9647 } 9648 auto* bc = aDocShell->GetBrowsingContext(); 9649 if (sourceBC.get() == bc) { 9650 // If it comes from the same tab / frame, don't steal focus either. 9651 return false; 9652 } 9653 auto* fm = nsFocusManager::GetFocusManager(); 9654 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) { 9655 // If we're already on the foreground tab of the foreground window, then we 9656 // don't need to do this. This helps to e.g. not steal focus from the 9657 // browser chrome unnecessarily. 9658 return false; 9659 } 9660 if (auto* doc = aDocShell->GetExtantDocument()) { 9661 if (doc->IsInitialDocument()) { 9662 // If we're the initial load for the browsing context, the browser 9663 // chrome determines what to focus. This is important because the 9664 // browser chrome may want to e.g focus the url-bar 9665 return false; 9666 } 9667 } 9668 // Take loadDivertedInBackground into account so the behavior would be the 9669 // same as how the tab first opened. 9670 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false); 9671 } 9672 9673 uint32_t nsDocShell::GetLoadTypeForFormSubmission( 9674 BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) { 9675 MOZ_ASSERT(aLoadState->IsFormSubmission()); 9676 9677 // https://html.spec.whatwg.org/#form-submission-algorithm 9678 // 22. Let historyHandling be "push". 9679 // 23. If form document equals targetNavigable's active document, and 9680 // form document has not yet completely loaded, then set 9681 // historyHandling to "replace". 9682 return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument 9683 ? LOAD_NORMAL_REPLACE 9684 : LOAD_LINK; 9685 } 9686 9687 static void MaybeConvertToReplaceLoad(nsDocShellLoadState* aLoadState, 9688 Document* aExtantDocument, 9689 bool aIdenticalURI, 9690 bool aHasActiveEntry) { 9691 // MaybeConvertToReplaceLoad implements steps 12 and 13 of #navigate, but 9692 // since we're not yet using historyBehavior for all types of loads and 9693 // configurations, we need to sometimes bail and revert to the old way of 9694 // handling push to replace load conversion. The different cases we can't 9695 // handle are: 9696 // 9697 // * When we don't have an active document 9698 // * When a document doesn't yet have a session history entry 9699 // * When we don't use SHIP 9700 // * When we don't use historyBehavior 9701 if (!aExtantDocument || !aHasActiveEntry || 9702 !mozilla::SessionHistoryInParent() || !aLoadState->HistoryBehavior()) { 9703 aLoadState->ResetHistoryBehavior(); 9704 return; 9705 } 9706 9707 bool convertToReplaceLoad = aLoadState->NeedsCompletelyLoadedDocument() && 9708 !aExtantDocument->IsCompletelyLoaded(); 9709 if (const auto& historyBehavior = aLoadState->HistoryBehavior(); 9710 !convertToReplaceLoad && historyBehavior && 9711 *historyBehavior == NavigationHistoryBehavior::Auto) { 9712 convertToReplaceLoad = aIdenticalURI; 9713 if (convertToReplaceLoad && aExtantDocument->GetPrincipal()) { 9714 aExtantDocument->GetPrincipal()->Equals(aLoadState->TriggeringPrincipal(), 9715 &convertToReplaceLoad); 9716 } 9717 } 9718 9719 convertToReplaceLoad = 9720 convertToReplaceLoad || nsContentUtils::NavigationMustBeAReplace( 9721 *aLoadState->URI(), *aExtantDocument); 9722 9723 if (convertToReplaceLoad) { 9724 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, 9725 "Convert to replace when navigating from {} to {}, {}", 9726 *aExtantDocument->GetDocumentURI(), *aLoadState->URI(), 9727 (aLoadState->NeedsCompletelyLoadedDocument() && 9728 !nsContentUtils::NavigationMustBeAReplace(*aLoadState->URI(), 9729 *aExtantDocument)) 9730 ? "needs completely loaded document" 9731 : "navigation must be a replace"); 9732 // There is no replace variant for LOAD_LINK, so we convert it to 9733 // LOAD_NORMAL_REPLACE just like in nsDocShell::OnNewURI. 9734 if (aLoadState->LoadType() == LOAD_LINK) { 9735 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE); 9736 } else { 9737 aLoadState->SetLoadType( 9738 MaybeAddLoadFlags(aLoadState->LoadType(), 9739 nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY)); 9740 } 9741 aLoadState->SetHistoryBehavior(NavigationHistoryBehavior::Replace); 9742 } else { 9743 aLoadState->SetHistoryBehavior(NavigationHistoryBehavior::Push); 9744 } 9745 } 9746 9747 // InternalLoad performs several of the steps from 9748 // https://html.spec.whatwg.org/#navigate. 9749 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, 9750 Maybe<uint32_t> aCacheKey) { 9751 MOZ_ASSERT(aLoadState, "need a load state!"); 9752 MOZ_ASSERT(aLoadState->TriggeringPrincipal(), 9753 "need a valid TriggeringPrincipal"); 9754 9755 if (!aLoadState->TriggeringPrincipal()) { 9756 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal"); 9757 return NS_ERROR_FAILURE; 9758 } 9759 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) { 9760 return NS_ERROR_NOT_AVAILABLE; 9761 } 9762 9763 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState); 9764 9765 mOriginalUriString.Truncate(); 9766 9767 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, 9768 ("DOCSHELL %p InternalLoad %s\n", this, 9769 aLoadState->URI()->GetSpecOrDefault().get())); 9770 9771 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG); 9772 9773 // Cancel loads coming from Docshells that are being destroyed. 9774 if (mIsBeingDestroyed) { 9775 return NS_ERROR_NOT_AVAILABLE; 9776 } 9777 9778 nsresult rv = EnsureScriptEnvironment(); 9779 if (NS_FAILED(rv)) { 9780 return rv; 9781 } 9782 9783 // If we have a target to move to, do that now. 9784 if (!aLoadState->Target().IsEmpty()) { 9785 return PerformRetargeting(aLoadState); 9786 } 9787 9788 // This is the non-retargeting load path, we've already set the right loadtype 9789 // for form submissions in nsDocShell::OnLinkClickSync. 9790 if (aLoadState->TargetBrowsingContext().IsNull()) { 9791 aLoadState->SetTargetBrowsingContext(GetBrowsingContext()); 9792 } 9793 9794 MOZ_DIAGNOSTIC_ASSERT( 9795 aLoadState->TargetBrowsingContext() == GetBrowsingContext(), 9796 "Load must be targeting this BrowsingContext"); 9797 9798 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState)); 9799 9800 // If we don't have a target, we're loading into ourselves, and our load 9801 // delegate may want to intercept that load. 9802 SameDocumentNavigationState sameDocumentNavigationState; 9803 bool sameDocument = 9804 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) && 9805 !aLoadState->GetPendingRedirectedChannel(); 9806 9807 if (mLoadType != LOAD_ERROR_PAGE && 9808 !aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) { 9809 MaybeConvertToReplaceLoad(aLoadState, GetExtantDocument(), 9810 sameDocumentNavigationState.mIdentical, 9811 !!mActiveEntry); 9812 } 9813 9814 // Note: We do this check both here and in BrowsingContext:: 9815 // LoadURI/InternalLoad, since document-specific sandbox flags are only 9816 // available in the process triggering the load, and we don't want the target 9817 // process to have to trust the triggering process to do the appropriate 9818 // checks for the BrowsingContext's sandbox flags. 9819 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState)); 9820 MOZ_TRY(mBrowsingContext->CheckFramebusting(aLoadState)); 9821 9822 NS_ENSURE_STATE(!HasUnloadedParent()); 9823 9824 rv = CheckLoadingPermissions(); 9825 if (NS_FAILED(rv)) { 9826 return rv; 9827 } 9828 9829 if (mFiredUnloadEvent) { 9830 if (IsOKToLoadURI(aLoadState->URI())) { 9831 MOZ_ASSERT(aLoadState->Target().IsEmpty(), 9832 "Shouldn't have a window target here!"); 9833 9834 // If this is a replace load, make whatever load triggered 9835 // the unload event also a replace load, so we don't 9836 // create extra history entries. 9837 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), 9838 LOAD_FLAGS_REPLACE_HISTORY)) { 9839 mLoadType = LOAD_NORMAL_REPLACE; 9840 } 9841 9842 // Do this asynchronously 9843 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState); 9844 return Dispatch(ev.forget()); 9845 } 9846 9847 // Just ignore this load attempt 9848 return NS_OK; 9849 } 9850 9851 // If we are loading a URI that should inherit a security context (basically 9852 // javascript: at this point), and the caller has said that principal 9853 // inheritance is allowed, there are a few possible cases: 9854 // 9855 // 1) We are provided with the principal to inherit. In that case, we just use 9856 // it. 9857 // 9858 // 2) The load is coming from some other application. In this case we don't 9859 // want to inherit from whatever document we have loaded now, since the 9860 // load is unrelated to it. 9861 // 9862 // 3) It's a load from our application, but does not provide an explicit 9863 // principal to inherit. In that case, we want to inherit the principal of 9864 // our current document, or of our parent document (if any) if we don't 9865 // have a current document. 9866 { 9867 bool inherits; 9868 9869 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) && 9870 !aLoadState->PrincipalToInherit() && 9871 (aLoadState->HasInternalLoadFlags( 9872 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) && 9873 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext( 9874 aLoadState->URI(), &inherits)) && 9875 inherits) { 9876 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true)); 9877 } 9878 // If principalToInherit is still null (e.g. if some of the conditions of 9879 // were not satisfied), then no inheritance of any sort will happen: the 9880 // load will just get a principal based on the URI being loaded. 9881 } 9882 9883 // If this docshell is owned by a frameloader, make sure to cancel 9884 // possible frameloader initialization before loading a new page. 9885 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell(); 9886 if (parent) { 9887 RefPtr<Document> doc = parent->GetDocument(); 9888 if (doc) { 9889 doc->TryCancelFrameLoaderInitialization(this); 9890 } 9891 } 9892 9893 // Before going any further vet loads initiated by external programs. 9894 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) { 9895 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL); 9896 9897 // Disallow external chrome: loads targetted at content windows 9898 if (aLoadState->URI()->SchemeIs("chrome")) { 9899 NS_WARNING("blocked external chrome: url -- use '--chrome' option"); 9900 return NS_ERROR_FAILURE; 9901 } 9902 9903 // clear the decks to prevent context bleed-through (bug 298255) 9904 rv = CreateAboutBlankDocumentViewer(nullptr, nullptr, nullptr, nullptr, 9905 /* aIsInitialDocument */ false); 9906 if (NS_FAILED(rv)) { 9907 return NS_ERROR_FAILURE; 9908 } 9909 9910 if (Document* doc = GetDocument()) { 9911 doc->DisallowBFCaching(); 9912 } 9913 } 9914 9915 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags( 9916 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP); 9917 mURIResultedInDocument = false; // reset the clock... 9918 9919 // If container is an iframe element and will lazy load element steps given 9920 // container returns true, then stop intersection-observing a lazy loading 9921 // element container and set container's lazy load resumption steps to null. 9922 if (IsSubframe()) { 9923 if (auto* iframe = HTMLIFrameElement::FromNodeOrNull( 9924 mBrowsingContext->GetEmbedderElement())) { 9925 // Per spec, reload doesn't cancel lazy loading iframes. 9926 if (!(aLoadState->LoadType() & LOAD_RELOAD_NORMAL)) { 9927 iframe->CancelLazyLoading(true /* aClearLazyLoadState */); 9928 } 9929 } 9930 } 9931 9932 // See if this is actually a load between two history entries for the same 9933 // document. If the process fails, or if we successfully navigate within the 9934 // same document, return. 9935 if (sameDocument) { 9936 nsresult rv = HandleSameDocumentNavigation( 9937 aLoadState, sameDocumentNavigationState, sameDocument); 9938 NS_ENSURE_SUCCESS(rv, rv); 9939 if (shouldTakeFocus) { 9940 mBrowsingContext->Focus(CallerType::System, IgnoreErrors()); 9941 } 9942 if (sameDocument) { 9943 if (aLoadState->LoadIsFromSessionHistory() && 9944 (mLoadType & LOAD_CMD_HISTORY)) { 9945 SetOngoingNavigation(Nothing()); 9946 } 9947 return rv; 9948 } 9949 } 9950 9951 // mDocumentViewer->PermitUnload can destroy |this| docShell, which 9952 // causes the next call of CanSavePresentation to crash. 9953 // Hold onto |this| until we return, to prevent a crash from happening. 9954 // (bug#331040) 9955 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this); 9956 9957 // Don't init timing for javascript:, since it generally doesn't 9958 // actually start a load or anything. If it does, we'll init 9959 // timing then, from OnStateChange. 9960 9961 // XXXbz mTiming should know what channel it's for, so we don't 9962 // need this hackery. 9963 const bool isJavaScript = aLoadState->URI()->SchemeIs("javascript"); 9964 const bool isExternalProtocol = 9965 nsContentUtils::IsExternalProtocol(aLoadState->URI()); 9966 const bool isDownload = !aLoadState->FileName().IsVoid(); 9967 const bool toBeReset = !isJavaScript && MaybeInitTiming(); 9968 9969 // FIXME(emilio): Should this be done by javascript: uris? What about external 9970 // protocols? 9971 if (mTiming && !isDownload) { 9972 mTiming->NotifyBeforeUnload(); 9973 } 9974 9975 // The following steps are from https://html.spec.whatwg.org/#navigate 9976 // Step 19, and here we actually also perform step 2 from 9977 // #navigate-to-a-javascript:-url (step 20) where the ongoing navigation is 9978 // set to null. 9979 SetOngoingNavigation(isJavaScript ? Nothing() 9980 : Some(OngoingNavigation::NavigationID)); 9981 9982 // Step 21 9983 if (RefPtr<Document> document = GetDocument(); 9984 !aLoadState->LoadIsFromSessionHistory() && document && 9985 aLoadState->UserNavigationInvolvement() != 9986 UserNavigationInvolvement::BrowserUI && 9987 !document->IsInitialDocument() && 9988 !NS_IsAboutBlankAllowQueryAndFragment(document->GetDocumentURI()) && 9989 NS_IsFetchScheme(aLoadState->URI()) && 9990 document->NodePrincipal()->EqualsConsideringDomain( 9991 aLoadState->TriggeringPrincipal())) { 9992 if (nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow()) { 9993 // Step 21.1 9994 if (RefPtr<Navigation> navigation = window->Navigation()) { 9995 AutoJSAPI jsapi; 9996 if (jsapi.Init(window)) { 9997 RefPtr<Element> sourceElement = aLoadState->GetSourceElement(); 9998 9999 // Step 21.2 10000 RefPtr<FormData> formData = aLoadState->GetFormDataEntryList(); 10001 10002 // Step 21.3 10003 RefPtr<nsIStructuredCloneContainer> navigationAPIStateForFiring = 10004 aLoadState->GetNavigationAPIState(); 10005 10006 nsCOMPtr<nsIURI> destinationURL = aLoadState->URI(); 10007 // Step 21.4 10008 RefPtr apiMethodTracker = aLoadState->GetNavigationAPIMethodTracker(); 10009 bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent( 10010 jsapi.cx(), aLoadState->GetNavigationType(), destinationURL, 10011 /* aIsSameDocument */ false, 10012 Some(aLoadState->UserNavigationInvolvement()), sourceElement, 10013 formData, navigationAPIStateForFiring, 10014 /* aClassicHistoryAPIState */ nullptr, apiMethodTracker); 10015 10016 // Step 21.5 10017 if (!shouldContinue) { 10018 return NS_OK; 10019 } 10020 } 10021 } 10022 } 10023 } 10024 10025 // Check if the page doesn't want to be unloaded. The javascript: 10026 // protocol handler deals with this for javascript: URLs. 10027 // NOTE(emilio): As of this writing, other browsers fire beforeunload for 10028 // external protocols, so keep doing that even though they don't return data 10029 // and thus we won't really unload this... 10030 if (!isJavaScript && !isDownload && 10031 !aLoadState->NotifiedBeforeUnloadListeners() && mDocumentViewer) { 10032 // Check if request is exempted from HTTPSOnlyMode and if https-first is 10033 // enabled, if so it means: 10034 // * https-first failed to upgrade request to https 10035 // * we already asked for permission to unload and the user accepted 10036 // otherwise we wouldn't be here. 10037 const bool isPrivateWin = GetOriginAttributes().IsPrivateBrowsing(); 10038 const uint32_t loadType = aLoadState->LoadType(); 10039 10040 // Check if request is a reload. 10041 const bool isHistoryOrReload = 10042 loadType == LOAD_RELOAD_NORMAL || 10043 loadType == LOAD_RELOAD_BYPASS_CACHE || 10044 loadType == LOAD_RELOAD_BYPASS_PROXY || 10045 loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE || 10046 loadType == LOAD_HISTORY; 10047 10048 // If it isn't a reload, the request already failed to be upgraded and 10049 // https-first is enabled then don't ask the user again for permission to 10050 // unload and just unload. 10051 bool okToUnload; 10052 if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSFirstMode() && 10053 nsHTTPSOnlyUtils::GetUpgradeMode(isPrivateWin) == 10054 nsHTTPSOnlyUtils::HTTPS_FIRST_MODE) { 10055 rv = mDocumentViewer->PermitUnload( 10056 nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload, 10057 &okToUnload); 10058 } else { 10059 rv = mDocumentViewer->PermitUnload(&okToUnload); 10060 if (mIsBeingDestroyed) { 10061 // unload handler destroyed this docshell. 10062 return NS_ERROR_NOT_AVAILABLE; 10063 } 10064 } 10065 10066 if (NS_SUCCEEDED(rv) && !okToUnload) { 10067 // The user chose not to unload the page, interrupt the 10068 // load. 10069 MaybeResetInitTiming(toBeReset); 10070 return NS_OK; 10071 } 10072 } 10073 10074 if (mTiming && !isDownload) { 10075 mTiming->NotifyUnloadAccepted(mCurrentURI); 10076 } 10077 10078 // In e10s, in the parent process, we refuse to load anything other than 10079 // "safe" resources that we ship or trust enough to give "special" URLs. 10080 // Similar check will be performed by the ParentProcessDocumentChannel if in 10081 // use. 10082 if (XRE_IsE10sParentProcess() && 10083 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) && 10084 !CanLoadInParentProcess(aLoadState->URI())) { 10085 return NS_ERROR_FAILURE; 10086 } 10087 10088 // Whenever a top-level browsing context is navigated, the user agent MUST 10089 // lock the orientation of the document to the document's default 10090 // orientation. We don't explicitly check for a top-level browsing context 10091 // here because orientation is only set on top-level browsing contexts. 10092 if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) { 10093 MOZ_ASSERT(mBrowsingContext->IsTop()); 10094 MOZ_ALWAYS_SUCCEEDS( 10095 mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None)); 10096 if (mBrowsingContext->IsActive()) { 10097 ScreenOrientation::UpdateActiveOrientationLock( 10098 hal::ScreenOrientation::None); 10099 } 10100 } 10101 10102 // Check for saving the presentation here, before calling Stop(). 10103 // This is necessary so that we can catch any pending requests. 10104 // Since the new request has not been created yet, we pass null for the 10105 // new request parameter. 10106 // Also pass nullptr for the document, since it doesn't affect the return 10107 // value for our purposes here. 10108 const bool savePresentation = 10109 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr, 10110 /* aReportBFCacheComboTelemetry */ true); 10111 10112 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a 10113 // separate check for SHIP so that we know if there are ongoing requests 10114 // before calling Stop() below. 10115 if (mozilla::SessionHistoryInParent()) { 10116 Document* document = GetDocument(); 10117 uint32_t flags = 0; 10118 if (document && !document->CanSavePresentation(nullptr, flags, true)) { 10119 // This forces some flags into the WindowGlobalParent's mBFCacheStatus, 10120 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache, 10121 // and in particular we'll store BFCacheStatus::REQUEST if needed. 10122 // Also, we want to report all the flags to the parent process here (and 10123 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the 10124 // telemetry data correctly. 10125 document->DisallowBFCaching(flags); 10126 } 10127 10128 if (aLoadState->LoadIsFromSessionHistory() && 10129 (mLoadType & LOAD_CMD_HISTORY)) { 10130 SetOngoingNavigation(Nothing()); 10131 } 10132 } 10133 10134 // Don't stop current network activity for javascript: URL's since they might 10135 // not result in any data, and thus nothing should be stopped in those cases. 10136 // In the case where they do result in data, the javascript: URL channel takes 10137 // care of stopping current network activity. Similarly, downloads don't 10138 // unload this document... 10139 if (!isJavaScript && !isDownload && !isExternalProtocol) { 10140 // Stop any current network activity. 10141 // Also stop content if this is a zombie doc. otherwise 10142 // the onload will be delayed by other loads initiated in the 10143 // background by the first document that 10144 // didn't fully load before the next load was initiated. 10145 // If not a zombie, don't stop content until data 10146 // starts arriving from the new URI... 10147 if ((mDocumentViewer && mDocumentViewer->GetPreviousViewer()) || 10148 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) { 10149 rv = StopInternal(nsIWebNavigation::STOP_ALL, UnsetOngoingNavigation::No); 10150 } else { 10151 rv = StopInternal(nsIWebNavigation::STOP_NETWORK, 10152 UnsetOngoingNavigation::No); 10153 } 10154 10155 if (NS_FAILED(rv)) { 10156 return rv; 10157 } 10158 } 10159 10160 mLoadType = aLoadState->LoadType(); 10161 10162 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has 10163 // been called. But when loading an error page, do not clear the 10164 // mLSHE for the real page. 10165 if (mLoadType != LOAD_ERROR_PAGE) { 10166 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()), 10167 Nothing()); 10168 if (aLoadState->LoadIsFromSessionHistory() && 10169 !mozilla::SessionHistoryInParent()) { 10170 // We're making history navigation or a reload. Make sure our history ID 10171 // points to the same ID as SHEntry's docshell ID. 10172 nsID historyID = {}; 10173 aLoadState->SHEntry()->GetDocshellID(historyID); 10174 10175 (void)mBrowsingContext->SetHistoryID(historyID); 10176 } 10177 } 10178 10179 mSavingOldViewer = savePresentation; 10180 10181 // If we have a saved content viewer in history, restore and show it now. 10182 if (aLoadState->LoadIsFromSessionHistory() && 10183 (mLoadType & LOAD_CMD_HISTORY)) { 10184 // https://html.spec.whatwg.org/#history-traversal: 10185 // To traverse the history 10186 // "If entry has a different Document object than the current entry, then 10187 // run the following substeps: Remove any tasks queued by the history 10188 // traversal task source..." 10189 // Same document object case was handled already above with 10190 // HandleSameDocumentNavigation call. 10191 RefPtr<ChildSHistory> shistory = GetRootSessionHistory(); 10192 if (shistory) { 10193 shistory->RemovePendingHistoryNavigations(); 10194 } 10195 if (!mozilla::SessionHistoryInParent()) { 10196 // It's possible that the previous viewer of mDocumentViewer is the 10197 // viewer that will end up in aLoadState->SHEntry() when it gets closed. 10198 // If that's the case, we need to go ahead and force it into its shentry 10199 // so we can restore it. 10200 if (mDocumentViewer) { 10201 nsCOMPtr<nsIDocumentViewer> prevViewer = 10202 mDocumentViewer->GetPreviousViewer(); 10203 if (prevViewer) { 10204 #ifdef DEBUG 10205 nsCOMPtr<nsIDocumentViewer> prevPrevViewer = 10206 prevViewer->GetPreviousViewer(); 10207 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here"); 10208 #endif 10209 nsCOMPtr<nsISHEntry> viewerEntry; 10210 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry)); 10211 if (viewerEntry == aLoadState->SHEntry()) { 10212 // Make sure this viewer ends up in the right place 10213 mDocumentViewer->SetPreviousViewer(nullptr); 10214 prevViewer->Destroy(); 10215 } 10216 } 10217 } 10218 nsCOMPtr<nsISHEntry> oldEntry = mOSHE; 10219 bool restoring; 10220 rv = RestorePresentation(aLoadState->SHEntry(), &restoring); 10221 if (restoring) { 10222 glean::bfcache::page_restored 10223 .EnumGet(glean::bfcache::PageRestoredLabel::eTrue) 10224 .Add(); 10225 return rv; 10226 } 10227 glean::bfcache::page_restored 10228 .EnumGet(glean::bfcache::PageRestoredLabel::eFalse) 10229 .Add(); 10230 10231 // We failed to restore the presentation, so clean up. 10232 // Both the old and new history entries could potentially be in 10233 // an inconsistent state. 10234 if (NS_FAILED(rv)) { 10235 if (oldEntry) { 10236 oldEntry->SyncPresentationState(); 10237 } 10238 10239 aLoadState->SHEntry()->SyncPresentationState(); 10240 } 10241 } 10242 } 10243 10244 bool isTopLevelDoc = mBrowsingContext->IsTopContent(); 10245 10246 OriginAttributes attrs = GetOriginAttributes(); 10247 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI()); 10248 10249 nsCOMPtr<nsIRequest> req; 10250 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req)); 10251 10252 if (NS_SUCCEEDED(rv)) { 10253 if (shouldTakeFocus) { 10254 mBrowsingContext->Focus(CallerType::System, IgnoreErrors()); 10255 } 10256 } 10257 10258 if (NS_FAILED(rv)) { 10259 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req)); 10260 UnblockEmbedderLoadEventForFailure(); 10261 10262 // The spec says no exception should be raised for pre-navigation check 10263 // failures. 10264 if (NS_ERROR_DOM_SECURITY_ERR == rv) { 10265 return NS_OK; 10266 } 10267 10268 nsCOMPtr<nsIURI> uri = aLoadState->URI(); 10269 if (DisplayLoadError(rv, uri, nullptr, chan) && 10270 // FIXME: At this point code was using internal load flags, but checking 10271 // non-internal load flags? 10272 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) { 10273 return NS_ERROR_LOAD_SHOWED_ERRORPAGE; 10274 } 10275 10276 // We won't report any error if this is an unknown protocol error. The 10277 // reason behind this is that it will allow enumeration of external 10278 // protocols if we report an error for each unknown protocol. 10279 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) { 10280 return NS_OK; 10281 } 10282 } 10283 10284 return rv; 10285 } 10286 10287 /* static */ 10288 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) { 10289 nsCOMPtr<nsIURI> uri = aURI; 10290 // In e10s, in the parent process, we refuse to load anything other than 10291 // "safe" resources that we ship or trust enough to give "special" URLs. 10292 bool canLoadInParent = false; 10293 if (NS_SUCCEEDED(NS_URIChainHasFlags( 10294 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) && 10295 canLoadInParent) { 10296 // We allow UI resources. 10297 return true; 10298 } 10299 // For about: and extension-based URIs, which don't get 10300 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present. 10301 while (uri && uri->SchemeIs("view-source")) { 10302 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri); 10303 if (nested) { 10304 nested->GetInnerURI(getter_AddRefs(uri)); 10305 } else { 10306 break; 10307 } 10308 } 10309 // Allow about: URIs, and allow moz-extension ones if we're running 10310 // extension content in the parent process. 10311 if (!uri || uri->SchemeIs("about") || 10312 (!StaticPrefs::extensions_webextensions_remote() && 10313 uri->SchemeIs("moz-extension"))) { 10314 return true; 10315 } 10316 #ifdef MOZ_THUNDERBIRD 10317 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") || 10318 uri->SchemeIs("news") || uri->SchemeIs("nntp") || 10319 uri->SchemeIs("snews") || uri->SchemeIs("x-moz-ews")) { 10320 return true; 10321 } 10322 #endif 10323 nsAutoCString scheme; 10324 uri->GetScheme(scheme); 10325 // Allow ext+foo URIs (extension-registered custom protocols). See 10326 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers 10327 if (StringBeginsWith(scheme, "ext+"_ns) && 10328 !StaticPrefs::extensions_webextensions_remote()) { 10329 return true; 10330 } 10331 // Final exception for some legacy automated tests: 10332 if (xpc::IsInAutomation() && 10333 StaticPrefs::security_allow_unsafe_parent_loads()) { 10334 return true; 10335 } 10336 return false; 10337 } 10338 10339 nsIPrincipal* nsDocShell::GetInheritedPrincipal( 10340 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) { 10341 RefPtr<Document> document; 10342 bool inheritedFromCurrent = false; 10343 10344 if (aConsiderCurrentDocument && mDocumentViewer) { 10345 document = mDocumentViewer->GetDocument(); 10346 inheritedFromCurrent = true; 10347 } 10348 10349 if (!document) { 10350 nsCOMPtr<nsIDocShellTreeItem> parentItem; 10351 GetInProcessSameTypeParent(getter_AddRefs(parentItem)); 10352 if (parentItem) { 10353 document = parentItem->GetDocument(); 10354 } 10355 } 10356 10357 if (!document) { 10358 if (!aConsiderCurrentDocument) { 10359 return nullptr; 10360 } 10361 10362 // Make sure we end up with _something_ as the principal no matter 10363 // what.If this fails, we'll just get a null docViewer and bail. 10364 if (!VerifyDocumentViewer()) { 10365 return nullptr; 10366 } 10367 document = mDocumentViewer->GetDocument(); 10368 } 10369 10370 //-- Get the document's principal 10371 if (document) { 10372 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal 10373 ? document->PartitionedPrincipal() 10374 : document->NodePrincipal(); 10375 10376 // Don't allow loads in typeContent docShells to inherit the system 10377 // principal from existing documents. 10378 if (inheritedFromCurrent && mItemType == typeContent && 10379 docPrincipal->IsSystemPrincipal()) { 10380 return nullptr; 10381 } 10382 10383 return docPrincipal; 10384 } 10385 10386 return nullptr; 10387 } 10388 10389 /* static */ nsresult nsDocShell::CreateRealChannelForDocument( 10390 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo, 10391 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags, 10392 const nsAString& aSrcdoc, nsIURI* aBaseURI) { 10393 nsCOMPtr<nsIChannel> channel; 10394 if (aSrcdoc.IsVoid()) { 10395 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo, 10396 nullptr, // PerformanceStorage 10397 nullptr, // loadGroup 10398 aCallbacks, aLoadFlags)); 10399 10400 if (aBaseURI) { 10401 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel); 10402 if (vsc) { 10403 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI)); 10404 } 10405 } 10406 } else if (aURI->SchemeIs("view-source")) { 10407 // Instantiate view source handler protocol, if it doesn't exist already. 10408 nsCOMPtr<nsIIOService> io(do_GetIOService()); 10409 MOZ_ASSERT(io); 10410 nsCOMPtr<nsIProtocolHandler> handler; 10411 nsresult rv = 10412 io->GetProtocolHandler("view-source", getter_AddRefs(handler)); 10413 if (NS_FAILED(rv)) { 10414 return rv; 10415 } 10416 10417 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance(); 10418 if (!vsh) { 10419 return NS_ERROR_FAILURE; 10420 } 10421 10422 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo, 10423 getter_AddRefs(channel))); 10424 } else { 10425 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI, 10426 aSrcdoc, "text/html"_ns, aLoadInfo, 10427 true)); 10428 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel); 10429 MOZ_ASSERT(isc); 10430 isc->SetBaseURI(aBaseURI); 10431 } 10432 10433 if (aLoadFlags != nsIRequest::LOAD_NORMAL) { 10434 nsresult rv = channel->SetLoadFlags(aLoadFlags); 10435 NS_ENSURE_SUCCESS(rv, rv); 10436 } 10437 10438 channel.forget(aChannel); 10439 return NS_OK; 10440 } 10441 10442 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState( 10443 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState, 10444 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks, 10445 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes, 10446 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv, 10447 nsIChannel** aChannel) { 10448 MOZ_ASSERT(aLoadInfo); 10449 10450 nsString srcdoc = VoidString(); 10451 bool isSrcdoc = 10452 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC); 10453 if (isSrcdoc) { 10454 srcdoc = aLoadState->SrcdocData(); 10455 } 10456 10457 aLoadInfo->SetTriggeringRemoteType( 10458 aLoadState->GetEffectiveTriggeringRemoteType()); 10459 10460 if (aLoadState->PrincipalToInherit()) { 10461 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit()); 10462 } 10463 aLoadInfo->SetLoadTriggeredFromExternal( 10464 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)); 10465 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags( 10466 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI)); 10467 aLoadInfo->SetOriginalFrameSrcLoad( 10468 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC)); 10469 aLoadInfo->SetIsNewWindowTarget( 10470 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)); 10471 aLoadInfo->SetForceMediaDocument(aLoadState->GetForceMediaDocument()); 10472 10473 bool inheritAttrs = false; 10474 if (aLoadState->PrincipalToInherit()) { 10475 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal( 10476 aLoadState->PrincipalToInherit(), aLoadState->URI(), 10477 true, // aInheritForAboutBlank 10478 isSrcdoc); 10479 } 10480 10481 // Strip the target query parameters before creating the channel. 10482 aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext); 10483 10484 OriginAttributes attrs; 10485 10486 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is 10487 // true. Otherwise we just use the origin attributes from docshell. 10488 if (inheritAttrs) { 10489 MOZ_ASSERT(aLoadState->PrincipalToInherit(), 10490 "We should have PrincipalToInherit here."); 10491 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef(); 10492 // If firstPartyIsolation is not enabled, then PrincipalToInherit should 10493 // have the same origin attributes with docshell. 10494 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(), 10495 attrs == aOriginAttributes); 10496 } else { 10497 attrs = aOriginAttributes; 10498 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo), 10499 aLoadState->URI()); 10500 } 10501 10502 aRv = aLoadInfo->SetOriginAttributes(attrs); 10503 if (NS_WARN_IF(NS_FAILED(aRv))) { 10504 return false; 10505 } 10506 10507 if (aLoadState->GetIsFromProcessingFrameAttributes()) { 10508 aLoadInfo->SetIsFromProcessingFrameAttributes(); 10509 } 10510 10511 // Propagate the IsFormSubmission flag to the loadInfo. 10512 if (aLoadState->IsFormSubmission()) { 10513 aLoadInfo->SetIsFormSubmission(true); 10514 } 10515 10516 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI()); 10517 10518 nsCOMPtr<nsIChannel> channel; 10519 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(), 10520 aLoadInfo, aCallbacks, aLoadFlags, srcdoc, 10521 aLoadState->BaseURI()); 10522 NS_ENSURE_SUCCESS(aRv, false); 10523 10524 if (!channel) { 10525 return false; 10526 } 10527 10528 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to 10529 // HTTPS by default. This behavior can be disabled through the loadinfo flag 10530 // HTTPS_ONLY_EXEMPT. 10531 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel); 10532 10533 // hack 10534 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); 10535 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal( 10536 do_QueryInterface(channel)); 10537 nsCOMPtr<nsIURI> referrer; 10538 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo(); 10539 if (referrerInfo) { 10540 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer)); 10541 } 10542 if (httpChannelInternal) { 10543 if (aLoadState->HasInternalLoadFlags( 10544 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) { 10545 aRv = httpChannelInternal->SetThirdPartyFlags( 10546 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW); 10547 MOZ_ASSERT(NS_SUCCEEDED(aRv)); 10548 } 10549 if (aLoadState->FirstParty()) { 10550 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI()); 10551 MOZ_ASSERT(NS_SUCCEEDED(aRv)); 10552 } else { 10553 aRv = httpChannelInternal->SetDocumentURI(referrer); 10554 MOZ_ASSERT(NS_SUCCEEDED(aRv)); 10555 } 10556 aRv = httpChannelInternal->SetRedirectMode( 10557 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL); 10558 MOZ_ASSERT(NS_SUCCEEDED(aRv)); 10559 } 10560 10561 if (httpChannel) { 10562 if (aLoadState->HeadersStream()) { 10563 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel); 10564 } 10565 // Set the referrer explicitly 10566 // Referrer is currenly only set for link clicks here. 10567 if (referrerInfo) { 10568 aRv = httpChannel->SetReferrerInfo(referrerInfo); 10569 MOZ_ASSERT(NS_SUCCEEDED(aRv)); 10570 } 10571 10572 // Mark the http channel as UrgentStart for top level document loading in 10573 // active tab. 10574 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) { 10575 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); 10576 if (cos) { 10577 cos->AddClassFlags(nsIClassOfService::UrgentStart); 10578 if (StaticPrefs::dom_document_priority_incremental()) { 10579 cos->SetIncremental(true); 10580 } 10581 } 10582 } 10583 } 10584 10585 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI() 10586 : aLoadState->URI()); 10587 10588 const nsACString& typeHint = aLoadState->TypeHint(); 10589 if (!typeHint.IsVoid()) { 10590 channel->SetContentType(typeHint); 10591 } 10592 10593 const nsAString& fileName = aLoadState->FileName(); 10594 if (!fileName.IsVoid()) { 10595 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT); 10596 NS_ENSURE_SUCCESS(aRv, false); 10597 if (!fileName.IsEmpty()) { 10598 aRv = channel->SetContentDispositionFilename(fileName); 10599 NS_ENSURE_SUCCESS(aRv, false); 10600 } 10601 } 10602 10603 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) { 10604 nsCOMPtr<nsIURI> referrer; 10605 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo(); 10606 if (referrerInfo) { 10607 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer)); 10608 } 10609 // save true referrer for those who need it (e.g. xpinstall whitelisting) 10610 // Currently only http and ftp channels support this. 10611 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer); 10612 } 10613 10614 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel)); 10615 auto loadType = aLoadState->LoadType(); 10616 10617 if (loadType == LOAD_RELOAD_NORMAL && 10618 StaticPrefs:: 10619 browser_soft_reload_only_force_validate_top_level_document()) { 10620 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel); 10621 if (cachingChannel) { 10622 cachingChannel->SetForceValidateCacheContent(true); 10623 } 10624 } 10625 10626 // figure out if we need to set the post data stream on the channel... 10627 if (aLoadState->PostDataStream()) { 10628 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel = 10629 do_QueryInterface(channel)) { 10630 // XXX it's a bit of a hack to rewind the postdata stream here but 10631 // it has to be done in case the post data is being reused multiple 10632 // times. 10633 nsCOMPtr<nsISeekableStream> postDataSeekable = 10634 do_QueryInterface(aLoadState->PostDataStream()); 10635 if (postDataSeekable) { 10636 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); 10637 NS_ENSURE_SUCCESS(aRv, false); 10638 } 10639 10640 // we really need to have a content type associated with this stream!! 10641 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1); 10642 10643 // Ownership of the stream has transferred to the channel, clear our 10644 // reference. 10645 aLoadState->SetPostDataStream(nullptr); 10646 } 10647 10648 /* If there is a valid postdata *and* it is a History Load, 10649 * set up the cache key on the channel, to retrieve the 10650 * data *only* from the cache. If it is a normal reload, the 10651 * cache is free to go to the server for updated postdata. 10652 */ 10653 if (cacheChannel && aCacheKey != 0) { 10654 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) { 10655 cacheChannel->SetCacheKey(aCacheKey); 10656 uint32_t loadFlags; 10657 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) { 10658 channel->SetLoadFlags(loadFlags | 10659 nsICachingChannel::LOAD_ONLY_FROM_CACHE); 10660 } 10661 } else if (loadType == LOAD_RELOAD_NORMAL) { 10662 cacheChannel->SetCacheKey(aCacheKey); 10663 } 10664 } 10665 } else { 10666 /* If there is no postdata, set the cache key on the channel, and 10667 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel 10668 * will be free to get it from net if it is not found in cache. 10669 * New cache may use it creatively on CGI pages with GET 10670 * method and even on those that say "no-cache" 10671 */ 10672 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL || 10673 loadType == LOAD_RELOAD_CHARSET_CHANGE || 10674 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE || 10675 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) { 10676 if (cacheChannel && aCacheKey != 0) { 10677 cacheChannel->SetCacheKey(aCacheKey); 10678 } 10679 } 10680 } 10681 10682 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) { 10683 // Allow execution against our context if the principals match 10684 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL); 10685 } 10686 10687 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) { 10688 nsString initiatorType; 10689 switch (aLoadInfo->InternalContentPolicyType()) { 10690 case nsIContentPolicy::TYPE_INTERNAL_EMBED: 10691 initiatorType = u"embed"_ns; 10692 break; 10693 case nsIContentPolicy::TYPE_INTERNAL_OBJECT: 10694 initiatorType = u"object"_ns; 10695 break; 10696 default: { 10697 const auto& embedderElementType = 10698 aBrowsingContext->GetEmbedderElementType(); 10699 if (embedderElementType) { 10700 initiatorType = *embedderElementType; 10701 } 10702 break; 10703 } 10704 } 10705 10706 if (!initiatorType.IsEmpty()) { 10707 timedChannel->SetInitiatorType(initiatorType); 10708 } 10709 } 10710 10711 nsCOMPtr<nsIURI> rpURI; 10712 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI)); 10713 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI; 10714 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI); 10715 if (originalResultPrincipalURI && 10716 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) { 10717 // Unconditionally override, we want the replay to be equal to what has 10718 // been captured. 10719 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref()); 10720 } 10721 10722 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) { 10723 // The LOAD_REPLACE flag and its handling here will be removed as part 10724 // of bug 1319110. For now preserve its restoration here to not break 10725 // any code expecting it being set specially on redirected channels. 10726 // If the flag has originally been set to change result of 10727 // NS_GetFinalChannelURI it won't have any effect and also won't cause 10728 // any harm. 10729 uint32_t loadFlags; 10730 aRv = channel->GetLoadFlags(&loadFlags); 10731 NS_ENSURE_SUCCESS(aRv, false); 10732 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE); 10733 } 10734 10735 nsCOMPtr<nsIPolicyContainer> policyContainer = aLoadState->PolicyContainer(); 10736 if (nsCOMPtr<nsIContentSecurityPolicy> csp = 10737 PolicyContainer::GetCSP(policyContainer)) { 10738 // Navigational requests that are same origin need to be upgraded in case 10739 // upgrade-insecure-requests is present. Please note that for document 10740 // navigations that bit is re-computed in case we encounter a server 10741 // side redirect so the navigation is not same-origin anymore. 10742 bool upgradeInsecureRequests = false; 10743 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests); 10744 if (upgradeInsecureRequests) { 10745 // only upgrade if the navigation is same origin 10746 nsCOMPtr<nsIPrincipal> resultPrincipal; 10747 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 10748 channel, getter_AddRefs(resultPrincipal)); 10749 NS_ENSURE_SUCCESS(aRv, false); 10750 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR( 10751 aLoadState->TriggeringPrincipal(), resultPrincipal)) { 10752 aLoadInfo->SetUpgradeInsecureRequests(true); 10753 } 10754 } 10755 } 10756 10757 if (policyContainer) { 10758 // For document loads we store the policyContainer that potentially needs to 10759 // be inherited by the new document, e.g. in case we are loading 10760 // an opaque origin like a data: URI. The actual inheritance 10761 // check happens within Document::InitPolicyContainer(). 10762 // Please create an actual copy of the policyContainer (do not share the 10763 // same reference) otherwise modifications done (such as the meta CSP of the 10764 // new doc) in an opaque origin will incorrectly be propagated to the 10765 // embedding document. 10766 RefPtr<PolicyContainer> policyContainerToInherit = new PolicyContainer(); 10767 policyContainerToInherit->InitFromOther( 10768 PolicyContainer::Cast(policyContainer)); 10769 aLoadInfo->SetPolicyContainerToInherit(policyContainerToInherit); 10770 } 10771 10772 channel.forget(aChannel); 10773 return true; 10774 } 10775 10776 bool nsDocShell::ShouldDoInitialAboutBlankSyncLoad( 10777 nsIURI* aURI, nsDocShellLoadState* aLoadState, 10778 nsIPrincipal* aPrincipalToInherit) { 10779 MOZ_ASSERT(mDocumentViewer); 10780 10781 if (!NS_IsAboutBlankAllowQueryAndFragment(aURI)) { 10782 return false; 10783 } 10784 10785 if (aLoadState->IsInitialAboutBlankHandlingProhibited()) { 10786 return false; 10787 } 10788 10789 if (mHasStartedLoadingOtherThanInitialBlankURI || 10790 !mDocumentViewer->GetDocument()->IsUncommittedInitialDocument()) { 10791 return false; 10792 } 10793 10794 if (!aPrincipalToInherit) { 10795 MOZ_ASSERT( 10796 mDocumentViewer->GetDocument()->NodePrincipal()->GetIsNullPrincipal(), 10797 "Load looks like first load but does not want principal inheritance."); 10798 } else { 10799 if (XRE_IsContentProcess() && 10800 !ValidatePrincipalCouldPotentiallyBeLoadedBy( 10801 aPrincipalToInherit, ContentChild::GetSingleton()->GetRemoteType(), 10802 {})) { 10803 // Principal doesn't match our remote type, so the we need the normal 10804 // load path to do a process switch. 10805 return false; 10806 } 10807 10808 // If a page opens about:blank, it will have a content principal. 10809 // If it is then restored after a restart, we might not have initialized 10810 // UsesOAC for it. If this is the case, do a normal load (bug 2004165). 10811 // XXX bug 2005205 tracks removing this workaround. 10812 if (aLoadState->LoadIsFromSessionHistory() && 10813 !mBrowsingContext->Group() 10814 ->UsesOriginAgentCluster(aPrincipalToInherit) 10815 .isSome()) { 10816 return false; 10817 } 10818 } 10819 10820 return true; 10821 } 10822 10823 void nsDocShell::UnsuppressPaintingIfNoNavigationAwayFromAboutBlank( 10824 mozilla::PresShell* aPresShell) { 10825 if (mHasStartedLoadingOtherThanInitialBlankURI || !mDocumentViewer) { 10826 return; 10827 } 10828 Document* doc = mDocumentViewer->GetDocument(); 10829 if (!doc || !doc->IsInitialDocument()) { 10830 return; 10831 } 10832 if (mDocumentViewer->GetPresShell() != aPresShell) { 10833 return; 10834 } 10835 // Our surroundings appear to remain in the same state 10836 // as before posting the runnable. 10837 aPresShell->UnsuppressPainting(); 10838 // The content viewer's mPresShell could have been removed now, see bug 10839 // 378682/421432 10840 if ((aPresShell = mDocumentViewer->GetPresShell())) { 10841 aPresShell->LoadComplete(); 10842 } 10843 } 10844 10845 nsresult nsDocShell::PerformTrustedTypesPreNavigationCheck( 10846 nsDocShellLoadState* aLoadState, nsGlobalWindowInner* aWindow) const { 10847 MOZ_ASSERT(aWindow); 10848 RefPtr<nsIContentSecurityPolicy> csp = 10849 PolicyContainer::GetCSP(aWindow->GetPolicyContainer()); 10850 if (csp->GetRequireTrustedTypesForDirectiveState() == 10851 RequireTrustedTypesForDirectiveState::NONE) { 10852 return NS_OK; 10853 } 10854 10855 // Exempt web extension content scripts from trusted types policies defined by 10856 // the page in which they are running. 10857 if (auto principal = BasePrincipal::Cast(aLoadState->TriggeringPrincipal())) { 10858 if (principal->ContentScriptAddonPolicyCore()) { 10859 return NS_OK; 10860 } 10861 } 10862 10863 // If disposion is enforce for require-trusted-types-for, then we return 10864 // errors in order to block navigation. If it's report-only, errors are 10865 // ignored and the URL is unchanged. 10866 bool shouldBlockOnError = csp->GetRequireTrustedTypesForDirectiveState() == 10867 RequireTrustedTypesForDirectiveState::ENFORCE; 10868 10869 // 2. Let urlString be the result of running the URL serializer on 10870 // request’s url. 10871 nsAutoCString urlString; 10872 aLoadState->URI()->GetSpec(urlString); 10873 10874 // 3. Let encodedScriptSource be the result of removing the leading 10875 // "javascript:" from urlString. 10876 constexpr auto javascriptScheme = "javascript:"_ns; 10877 const nsDependentCSubstring encodedScriptSource = 10878 Substring(urlString, javascriptScheme.Length()); 10879 10880 // 4. Let convertedScriptSource be the result of executing Process value 10881 // with a default policy algorithm 10882 Maybe<nsAutoString> compliantStringHolder; 10883 NS_ConvertUTF8toUTF16 encodedScriptSourceUTF16(encodedScriptSource); 10884 constexpr nsLiteralString sink = u"Location href"_ns; 10885 auto reportPreNavigationCheckViolations = [&csp, &sink, 10886 &encodedScriptSourceUTF16] { 10887 // Report violation the same way as for "Should sink type mismatch 10888 // violation be blocked by Content Security Policy", since that's what 10889 // other browsers do. See 10890 // https://github.com/w3c/trusted-types/issues/584. 10891 auto location = JSCallingLocation::Get(); 10892 TrustedTypeUtils::ReportSinkTypeMismatchViolations( 10893 csp, nullptr /* aCSPEventListener */, location.FileName(), 10894 location.mLine, location.mColumn, sink, kTrustedTypesOnlySinkGroup, 10895 encodedScriptSourceUTF16); 10896 }; 10897 ErrorResult error; 10898 auto convertedScriptSource = 10899 TrustedTypeUtils::GetConvertedScriptSourceForPreNavigationCheck( 10900 *aWindow, encodedScriptSourceUTF16, sink, compliantStringHolder, 10901 error); 10902 error.WouldReportJSException(); 10903 if (error.Failed()) { 10904 reportPreNavigationCheckViolations(); 10905 if (shouldBlockOnError) { 10906 RETURN_NSRESULT_ON_FAILURE(error); 10907 } 10908 error.SuppressException(); 10909 return NS_OK; 10910 } 10911 10912 // 5. Set urlString to be the result of prepending "javascript:" to 10913 // stringified convertedScriptSource. 10914 urlString = javascriptScheme + NS_ConvertUTF16toUTF8(*convertedScriptSource); 10915 10916 // 6. Let newURL be the result of running the URL parser on urlString. 10917 nsCOMPtr<nsIURI> newURL; 10918 nsresult rv = NS_NewURI(getter_AddRefs(newURL), urlString); 10919 if (NS_FAILED(rv)) { 10920 reportPreNavigationCheckViolations(); 10921 return shouldBlockOnError ? rv : NS_OK; 10922 } 10923 10924 // 7. Set request’s url to newURL. 10925 aLoadState->SetURI(newURL); 10926 return NS_OK; 10927 } 10928 10929 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState, 10930 Maybe<uint32_t> aCacheKey, 10931 nsIRequest** aRequest) { 10932 // Double-check that we're still around to load this URI. 10933 if (mIsBeingDestroyed) { 10934 // Return NS_OK despite not doing anything to avoid throwing exceptions 10935 // from nsLocation::SetHref if the unload handler of the existing page 10936 // tears us down. 10937 return NS_OK; 10938 } 10939 10940 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service(); 10941 if (NS_WARN_IF(!uriLoader)) { 10942 return NS_ERROR_UNEXPECTED; 10943 } 10944 10945 // Persist and sync layout history state before we load a new uri, as this 10946 // might be our last chance to do so, in the content process. 10947 PersistLayoutHistoryState(); 10948 SynchronizeLayoutHistoryState(); 10949 10950 nsresult rv; 10951 nsContentPolicyType contentPolicyType = DetermineContentType(); 10952 10953 auto getSourceWindowContext = [this, &aLoadState] { 10954 const MaybeDiscardedBrowsingContext& sourceBC = 10955 aLoadState->SourceBrowsingContext(); 10956 if (!sourceBC.IsNullOrDiscarded()) { 10957 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) { 10958 return wc; 10959 } 10960 } 10961 return mBrowsingContext->GetParentWindowContext(); 10962 }; 10963 10964 if (StaticPrefs::dom_security_trusted_types_enabled() && 10965 aLoadState->URI()->SchemeIs("javascript")) { 10966 if (WindowContext* sourceWindowContext = getSourceWindowContext()) { 10967 RefPtr<nsGlobalWindowInner> window = 10968 sourceWindowContext->GetInnerWindow(); 10969 rv = PerformTrustedTypesPreNavigationCheck(aLoadState, window); 10970 // Default policy might destroy the whole docshell, so check that again. 10971 if (mIsBeingDestroyed) { 10972 return NS_OK; 10973 } 10974 if (NS_FAILED(rv)) { 10975 return NS_ERROR_DOM_SECURITY_ERR; 10976 } 10977 } 10978 } 10979 10980 if (IsSubframe()) { 10981 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME || 10982 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME, 10983 "DoURILoad thinks this is a frame and InternalLoad does not"); 10984 if (StaticPrefs::dom_block_external_protocol_in_iframes()) { 10985 // Only allow URLs able to return data in iframes. 10986 if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) { 10987 // The context to check user-interaction with for the purposes of 10988 // popup-blocking. 10989 // 10990 // We generally want to check the context that initiated the navigation. 10991 WindowContext* sourceWindowContext = getSourceWindowContext(); 10992 MOZ_ASSERT(sourceWindowContext); 10993 10994 // FIXME: We can't check user-interaction against an OOP window. This is 10995 // the next best thing we can really do. The load state keeps whether 10996 // the navigation had a user interaction in process 10997 // (aLoadState->HasValidUserGestureActivation()), but we can't really 10998 // consume it, which we want to prevent popup-spamming from the same 10999 // click event. 11000 WindowContext* context = 11001 sourceWindowContext->IsInProcess() 11002 ? sourceWindowContext 11003 : mBrowsingContext->GetCurrentWindowContext(); 11004 const bool popupBlocked = [&] { 11005 const bool active = mBrowsingContext->IsActive(); 11006 11007 // For same-origin-with-top windows, we grant a single free popup 11008 // without user activation, see bug 1680721. 11009 // 11010 // We consume the flag now even if there's no user activation. 11011 const bool hasFreePass = [&] { 11012 if (!active || 11013 !(context->IsInProcess() && context->SameOriginWithTop())) { 11014 return false; 11015 } 11016 nsGlobalWindowInner* win = 11017 context->TopWindowContext()->GetInnerWindow(); 11018 return win && win->TryOpenExternalProtocolIframe(); 11019 }(); 11020 11021 if (context->IsInProcess() && 11022 context->ConsumeTransientUserGestureActivation()) { 11023 // If the user has interacted with the page, consume it. 11024 return false; 11025 } 11026 11027 // TODO(emilio): Can we remove this check? It seems like what prompted 11028 // this code (bug 1514547) should be covered by transient user 11029 // activation, see bug 1514547. 11030 if (active && 11031 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) { 11032 return false; 11033 } 11034 11035 if (sourceWindowContext->CanShowPopup()) { 11036 return false; 11037 } 11038 11039 if (hasFreePass) { 11040 return false; 11041 } 11042 11043 return true; 11044 }(); 11045 11046 // No error must be returned when iframes are blocked. 11047 if (popupBlocked) { 11048 nsAutoString message; 11049 nsresult rv = nsContentUtils::GetLocalizedString( 11050 nsContentUtils::eDOM_PROPERTIES, 11051 "ExternalProtocolFrameBlockedNoUserActivation", message); 11052 if (NS_SUCCEEDED(rv)) { 11053 nsContentUtils::ReportToConsoleByWindowID( 11054 message, nsIScriptError::warningFlag, "DOM"_ns, 11055 context->InnerWindowId()); 11056 } 11057 return NS_OK; 11058 } 11059 } 11060 } 11061 11062 // Only allow view-source scheme in top-level docshells. view-source is 11063 // the only scheme to which this applies at the moment due to potential 11064 // timing attacks to read data from cross-origin iframes. If this widens 11065 // we should add a protocol flag for whether the scheme is allowed in 11066 // frames and use something like nsNetUtil::NS_URIChainHasFlags. 11067 nsCOMPtr<nsIURI> tempURI = aLoadState->URI(); 11068 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI); 11069 while (nestedURI) { 11070 // view-source should always be an nsINestedURI, loop and check the 11071 // scheme on this and all inner URIs that are also nested URIs. 11072 if (tempURI->SchemeIs("view-source")) { 11073 return NS_ERROR_UNKNOWN_PROTOCOL; 11074 } 11075 nestedURI->GetInnerURI(getter_AddRefs(tempURI)); 11076 nestedURI = do_QueryInterface(tempURI); 11077 } 11078 } else { 11079 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT, 11080 "DoURILoad thinks this is a document and InternalLoad does not"); 11081 } 11082 11083 // We want to inherit aLoadState->PrincipalToInherit() when: 11084 // 1. ChannelShouldInheritPrincipal returns true. 11085 // 2. aLoadState->URI() is not data: URI, or data: URI is not 11086 // configured as unique opaque origin. 11087 bool inheritPrincipal = false; 11088 11089 nsCOMPtr<nsIURI> uri = aLoadState->URI(); 11090 if (aLoadState->PrincipalToInherit()) { 11091 bool isSrcdoc = 11092 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC); 11093 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal( 11094 aLoadState->PrincipalToInherit(), uri, 11095 true, // aInheritForAboutBlank 11096 isSrcdoc); 11097 11098 inheritPrincipal = inheritAttrs && !uri->SchemeIs("data"); 11099 } 11100 11101 MOZ_ASSERT_IF(NS_IsAboutBlankAllowQueryAndFragment(uri) && 11102 aLoadState->PrincipalToInherit(), 11103 inheritPrincipal); 11104 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570 11105 const bool doInitialSyncLoad = ShouldDoInitialAboutBlankSyncLoad( 11106 uri, aLoadState, aLoadState->PrincipalToInherit()); 11107 11108 if (!doInitialSyncLoad) { 11109 // https://wicg.github.io/document-picture-in-picture/#close-on-navigate 11110 if (Document* doc = GetExtantDocument()) { 11111 NS_DispatchToMainThread(NS_NewRunnableFunction( 11112 "Close PIP window on navigate", [doc = RefPtr(doc)]() { 11113 doc->CloseAnyAssociatedDocumentPiPWindows(); 11114 })); 11115 } 11116 if (GetBrowsingContext()->GetIsDocumentPiP()) { 11117 return NS_OK; 11118 } 11119 } 11120 11121 // FIXME We still have a ton of codepaths that don't pass through 11122 // DocumentLoadListener, so probably need to create session history info 11123 // in more places. 11124 if (aLoadState->GetLoadingSessionHistoryInfo()) { 11125 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo()); 11126 } else if (doInitialSyncLoad && mozilla::SessionHistoryInParent()) { 11127 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel 11128 // loads have it, and later history behavior depends on it existing. 11129 UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>( 11130 uri, aLoadState->TriggeringPrincipal(), 11131 aLoadState->PrincipalToInherit(), 11132 aLoadState->PartitionedPrincipalToInherit(), 11133 aLoadState->PolicyContainer(), mContentTypeHint); 11134 entry->SetTransient(); 11135 mozilla::dom::LoadingSessionHistoryInfo info(*entry); 11136 info.mContiguousEntries.AppendElement(*entry); 11137 SetLoadingSessionHistoryInfo(info, true); 11138 } 11139 11140 // open a channel for the url 11141 11142 // If we have a pending channel, use the channel we've already created here. 11143 // We don't need to set up load flags for our channel, as it has already been 11144 // created. 11145 11146 if (nsCOMPtr<nsIChannel> channel = 11147 aLoadState->GetPendingRedirectedChannel()) { 11148 // If we have a request outparameter, shove our channel into it. 11149 if (aRequest) { 11150 nsCOMPtr<nsIRequest> outRequest = channel; 11151 outRequest.forget(aRequest); 11152 } 11153 11154 mHasStartedLoadingOtherThanInitialBlankURI = true; 11155 return OpenRedirectedChannel(aLoadState); 11156 } 11157 11158 // There are two cases we care about: 11159 // * Top-level load: In this case, loadingNode is null, but loadingWindow 11160 // is our mScriptGlobal. We pass null for loadingPrincipal in this case. 11161 // * Subframe load: loadingWindow is null, but loadingNode is the frame 11162 // element for the load. loadingPrincipal is the NodePrincipal of the 11163 // frame element. 11164 nsCOMPtr<nsINode> loadingNode; 11165 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow; 11166 nsCOMPtr<nsIPrincipal> loadingPrincipal; 11167 nsCOMPtr<nsISupports> topLevelLoadingContext; 11168 11169 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) { 11170 loadingNode = nullptr; 11171 loadingPrincipal = nullptr; 11172 loadingWindow = mScriptGlobal; 11173 if (XRE_IsContentProcess()) { 11174 // In e10s the child process doesn't have access to the element that 11175 // contains the browsing context (because that element is in the chrome 11176 // process). 11177 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild(); 11178 topLevelLoadingContext = ToSupports(browserChild); 11179 } else { 11180 // This is for loading non-e10s tabs and toplevel windows of various 11181 // sorts. 11182 // For the toplevel window cases, requestingElement will be null. 11183 nsCOMPtr<Element> requestingElement = 11184 loadingWindow->GetFrameElementInternal(); 11185 topLevelLoadingContext = requestingElement; 11186 } 11187 } else { 11188 loadingWindow = nullptr; 11189 loadingNode = mScriptGlobal->GetFrameElementInternal(); 11190 if (loadingNode) { 11191 // If we have a loading node, then use that as our loadingPrincipal. 11192 loadingPrincipal = loadingNode->NodePrincipal(); 11193 #ifdef DEBUG 11194 // Get the docshell type for requestingElement. 11195 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc(); 11196 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell(); 11197 // requestingElement docshell type = current docshell type. 11198 MOZ_ASSERT( 11199 mItemType == elementDocShell->ItemType(), 11200 "subframes should have the same docshell type as their parent"); 11201 #endif 11202 } else { 11203 if (mIsBeingDestroyed) { 11204 // If this isn't a top-level load and mScriptGlobal's frame element is 11205 // null, then the element got removed from the DOM while we were trying 11206 // to load this resource. This docshell is scheduled for destruction 11207 // already, so bail out here. 11208 return NS_OK; 11209 } 11210 // If we are not being destroyed and we do not have access to the loading 11211 // node, then we are a remote subframe. Set the loading principal 11212 // to be a null principal and then set it correctly in the parent. 11213 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr); 11214 } 11215 } 11216 11217 if (!aLoadState->TriggeringPrincipal()) { 11218 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal"); 11219 return NS_ERROR_FAILURE; 11220 } 11221 11222 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags(); 11223 nsSecurityFlags securityFlags = 11224 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL; 11225 11226 if (mLoadType == LOAD_ERROR_PAGE) { 11227 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE; 11228 } 11229 11230 if (inheritPrincipal) { 11231 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL; 11232 } 11233 11234 // Must never have a parent for TYPE_DOCUMENT loads 11235 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT, 11236 !mBrowsingContext->GetParent()); 11237 // Subdocuments must have a parent 11238 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT, 11239 mBrowsingContext->GetParent()); 11240 mBrowsingContext->SetTriggeringAndInheritPrincipals( 11241 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(), 11242 aLoadState->GetLoadIdentifier()); 11243 RefPtr<LoadInfo> loadInfo; 11244 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) { 11245 loadInfo = 11246 new LoadInfo(loadingWindow, uri, aLoadState->TriggeringPrincipal(), 11247 topLevelLoadingContext, securityFlags, sandboxFlags); 11248 } else { 11249 loadInfo = MOZ_TRY(LoadInfo::Create( 11250 loadingPrincipal, aLoadState->TriggeringPrincipal(), loadingNode, 11251 securityFlags, contentPolicyType, Maybe<mozilla::dom::ClientInfo>(), 11252 Maybe<mozilla::dom::ServiceWorkerDescriptor>(), sandboxFlags)); 11253 } 11254 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext(); 11255 11256 if (doInitialSyncLoad) { 11257 // Stay on the eagerly created document and adjust it to match what we would 11258 // be loading. 11259 return CompleteInitialAboutBlankLoad(aLoadState, loadInfo); 11260 } 11261 mHasStartedLoadingOtherThanInitialBlankURI = true; 11262 11263 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess()) { 11264 if (context->HasValidTransientUserGestureActivation()) { 11265 aLoadState->SetHasValidUserGestureActivation(true); 11266 aLoadState->SetTextDirectiveUserActivation(true); 11267 } 11268 if (!aLoadState->TriggeringWindowId()) { 11269 aLoadState->SetTriggeringWindowId(context->Id()); 11270 } 11271 if (!aLoadState->TriggeringStorageAccess()) { 11272 Document* contextDoc = context->GetExtantDoc(); 11273 if (contextDoc) { 11274 aLoadState->SetTriggeringStorageAccess( 11275 contextDoc->UsingStorageAccess()); 11276 } 11277 } 11278 } 11279 11280 // in case this docshell load was triggered by a valid transient user gesture, 11281 // or also the load originates from external, then we pass that information on 11282 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers. 11283 if (aLoadState->HasValidUserGestureActivation() || 11284 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) { 11285 loadInfo->SetHasValidUserGestureActivation(true); 11286 aLoadState->SetTextDirectiveUserActivation(true); 11287 } 11288 11289 loadInfo->SetTextDirectiveUserActivation( 11290 aLoadState->GetTextDirectiveUserActivation()); 11291 11292 loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId()); 11293 loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess()); 11294 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags()); 11295 net::ClassificationFlags flags = aLoadState->TriggeringClassificationFlags(); 11296 loadInfo->SetTriggeringFirstPartyClassificationFlags(flags.firstPartyFlags); 11297 loadInfo->SetTriggeringThirdPartyClassificationFlags(flags.thirdPartyFlags); 11298 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh()); 11299 11300 uint32_t cacheKey = 0; 11301 if (aCacheKey) { 11302 cacheKey = *aCacheKey; 11303 } else if (mozilla::SessionHistoryInParent()) { 11304 if (mLoadingEntry) { 11305 cacheKey = mLoadingEntry->mInfo.GetCacheKey(); 11306 } else if (mActiveEntry) { // for reload cases 11307 cacheKey = mActiveEntry->GetCacheKey(); 11308 } 11309 } else { 11310 if (mLSHE) { 11311 cacheKey = mLSHE->GetCacheKey(); 11312 } else if (mOSHE) { // for reload cases 11313 cacheKey = mOSHE->GetCacheKey(); 11314 } 11315 } 11316 11317 bool uriModified; 11318 if (mLSHE || mLoadingEntry) { 11319 if (mLoadingEntry) { 11320 uriModified = mLoadingEntry->mInfo.GetURIWasModified(); 11321 } else { 11322 uriModified = mLSHE->GetURIWasModified(); 11323 } 11324 } else { 11325 uriModified = false; 11326 } 11327 11328 bool isEmbeddingBlockedError = false; 11329 if (mFailedChannel) { 11330 nsresult status; 11331 mFailedChannel->GetStatus(&status); 11332 isEmbeddingBlockedError = status == NS_ERROR_XFO_VIOLATION || 11333 status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION; 11334 } 11335 11336 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags( 11337 mBrowsingContext, uriModified, Some(isEmbeddingBlockedError)); 11338 11339 nsCOMPtr<nsIChannel> channel; 11340 if (DocumentChannel::CanUseDocumentChannel(uri)) { 11341 channel = DocumentChannel::CreateForDocument( 11342 aLoadState, loadInfo, loadFlags, this, cacheKey, uriModified, 11343 isEmbeddingBlockedError); 11344 MOZ_ASSERT(channel); 11345 11346 // Disable keyword fixup when using DocumentChannel, since 11347 // DocumentLoadListener will handle this for us (in the parent process). 11348 mAllowKeywordFixup = false; 11349 } else if (!CreateAndConfigureRealChannelForLoadState( 11350 mBrowsingContext, aLoadState, loadInfo, this, this, 11351 GetOriginAttributes(), loadFlags, cacheKey, rv, 11352 getter_AddRefs(channel))) { 11353 return rv; 11354 } 11355 11356 // Make sure to give the caller a channel if we managed to create one 11357 // This is important for correct error page/session history interaction 11358 if (aRequest) { 11359 NS_ADDREF(*aRequest = channel); 11360 } 11361 11362 const nsACString& typeHint = aLoadState->TypeHint(); 11363 if (!typeHint.IsVoid()) { 11364 mContentTypeHint = typeHint; 11365 } else { 11366 mContentTypeHint.Truncate(); 11367 } 11368 11369 // Load attributes depend on load type... 11370 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) { 11371 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we 11372 // only want to force cache load for this channel, not the whole 11373 // loadGroup. 11374 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel); 11375 if (cachingChannel) { 11376 cachingChannel->SetAllowStaleCacheContent(true); 11377 } 11378 } 11379 11380 uint32_t openFlags = 11381 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType); 11382 return OpenInitializedChannel(channel, uriLoader, openFlags); 11383 } 11384 11385 nsresult nsDocShell::CompleteInitialAboutBlankLoad( 11386 nsDocShellLoadState* aLoadState, nsILoadInfo* aLoadInfo) { 11387 nsresult rv; 11388 // Match the DocumentChannel case where the default for third-partiness 11389 // differs from the default in LoadInfo construction here. 11390 // toolkit/components/antitracking/test/browser/browser_aboutblank.js 11391 // fails without this. 11392 BrowsingContext* top = mBrowsingContext->Top(); 11393 if (top == mBrowsingContext) { 11394 // If we're at the top, this must be a window.open()ed 11395 // window, and we can't be third-party relative to ourselves. 11396 aLoadInfo->SetIsThirdPartyContextToTopWindow(false); 11397 } else { 11398 if (Document* topDoc = top->GetDocument()) { 11399 bool thirdParty = false; 11400 (void)topDoc->GetPrincipal()->IsThirdPartyPrincipal( 11401 aLoadState->PrincipalToInherit(), &thirdParty); 11402 aLoadInfo->SetIsThirdPartyContextToTopWindow(thirdParty); 11403 } else { 11404 // If top is in a different process, we have to be third-party relative 11405 // to it. 11406 aLoadInfo->SetIsThirdPartyContextToTopWindow(true); 11407 } 11408 } 11409 11410 if (!mDocumentViewer) { 11411 MOZ_ASSERT(false, "How did the viewer go away?"); 11412 return NS_ERROR_FAILURE; 11413 } 11414 RefPtr<Document> doc = mDocumentViewer->GetDocument(); 11415 MOZ_LOG(gDocShellLog, LogLevel::Debug, 11416 ("nsDocShell[%p]::DoURILoad sync about:blank onto initial " 11417 "about:blank. Document[%p]\n", 11418 this, doc.get())); 11419 if (!doc) { 11420 MOZ_ASSERT(false, "How did the document go away?"); 11421 return NS_ERROR_FAILURE; 11422 } 11423 11424 const bool principalMissmatch = 11425 aLoadState->PrincipalToInherit() && 11426 !aLoadState->PrincipalToInherit()->Equals(doc->GetPrincipal()); 11427 MOZ_ASSERT_IF(!aLoadState->PrincipalToInherit(), 11428 doc->GetPrincipal()->GetIsNullPrincipal()); 11429 11430 // The channel would sandbox aLoadState->PrincipalToInherit(). Even if 11431 // the document already has a null principal, we don't know if it's the right 11432 // sandboxed one. So be safe and clobber. 11433 const uint32_t sandboxFlags = 11434 mBrowsingContext->GetHasLoadedNonInitialDocument() 11435 ? mBrowsingContext->GetSandboxFlags() 11436 : mBrowsingContext->GetInitialSandboxFlags(); 11437 const bool shouldBeSandboxed = sandboxFlags & SANDBOXED_ORIGIN; 11438 MOZ_ASSERT_IF(shouldBeSandboxed, aLoadState->PrincipalToInherit()); 11439 11440 // Clobber document before completing the synchronous load if it doesn't have 11441 // the right principal (bug 1979032) 11442 if (principalMissmatch || shouldBeSandboxed) { 11443 nsIPrincipal* principal = aLoadState->PrincipalToInherit(); 11444 nsIPrincipal* partitionedPrincipal = 11445 aLoadState->PartitionedPrincipalToInherit(); 11446 if (!partitionedPrincipal) { 11447 partitionedPrincipal = principal; 11448 } 11449 11450 // This will sandbox the principals as needed 11451 rv = CreateAboutBlankDocumentViewer( 11452 principal, partitionedPrincipal, aLoadState->PolicyContainer(), 11453 doc->GetDocBaseURI(), /* aIsInitialDocument */ true); 11454 NS_ENSURE_SUCCESS(rv, rv); 11455 11456 doc = mDocumentViewer->GetDocument(); 11457 MOZ_ASSERT(doc); 11458 MOZ_LOG(gDocShellLog, LogLevel::Warning, 11459 ("nsDocShell[%p] sync about:blank principals don't match, create " 11460 "new document. Document[%p] \n", 11461 this, doc.get())); 11462 } 11463 11464 MOZ_ASSERT(doc->IsInitialDocument(), 11465 "How come the doc is no longer the initial one?"); 11466 11467 MOZ_ASSERT(doc->GetReadyStateEnum() == Document::READYSTATE_COMPLETE); 11468 MOZ_ASSERT(!mIsLoadingDocument); 11469 11470 doc->ApplyCspFromLoadInfo(aLoadInfo); 11471 doc->ApplySettingsFromCSP(false); 11472 doc->RecomputeResistFingerprinting(); 11473 11474 rv = doc->GetWindowContext()->SetIsOriginalFrameSource( 11475 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC)); 11476 NS_ENSURE_SUCCESS(rv, rv); 11477 11478 nsPIDOMWindowInner* innerWindow = doc->GetInnerWindow(); 11479 if (innerWindow) { 11480 mozilla::dom::ClientSource* clientSource = 11481 nsGlobalWindowInner::Cast(innerWindow)->GetClientSource(); 11482 // See if we don't have a controller but the parent has gained a 11483 // controller. 11484 if (clientSource && clientSource->GetController().isNothing()) { 11485 MaybeInheritController( 11486 clientSource, 11487 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker( 11488 this) 11489 ? doc->PartitionedPrincipal() 11490 : doc->GetPrincipal()); 11491 } 11492 } 11493 11494 // Get the load event fired for the initial about:blank without starting 11495 // a real load from a channel. We still need a channel object even though 11496 // we don't care about reading from the channel. 11497 nsCOMPtr<nsIChannel> aboutBlankChannel; 11498 rv = NS_NewChannelInternal(getter_AddRefs(aboutBlankChannel), 11499 aLoadState->URI(), aLoadInfo, nullptr, mLoadGroup, 11500 nullptr, nsIChannel::LOAD_DOCUMENT_URI); 11501 if (NS_FAILED(rv)) { 11502 return rv; 11503 } 11504 if (!aboutBlankChannel) { 11505 return NS_ERROR_FAILURE; 11506 } 11507 11508 MOZ_ASSERT(!mIsLoadingDocument); 11509 MOZ_ASSERT(!mDocumentRequest); 11510 11511 // Call OnStartRequest so that nsDocLoader sets mIsLoadingDocument and fire 11512 // state start 11513 OnStartRequest(aboutBlankChannel); 11514 11515 MOZ_ASSERT(mIsLoadingDocument); 11516 MOZ_ASSERT(mDocumentRequest == aboutBlankChannel); 11517 MOZ_ASSERT(!doc->InitialAboutBlankLoadCompleting()); 11518 11519 doc->BeginInitialAboutBlankLoadCompleting(aboutBlankChannel); 11520 auto resetLoadCompleting = 11521 MakeScopeExit([&] { doc->EndInitialAboutBlankLoadCompleting(); }); 11522 11523 mCurrentURI = aLoadState->URI(); 11524 doc->SetDocumentURI(aLoadState->URI()); 11525 11526 // Normal documents fire the location change at content viewer creation. 11527 // The initial about:blank does not do that at content viewer creation, 11528 // so that the UI isn't bothered about the initial about:blank if there's 11529 // immediate navigation away. However, now that the initial about:blank is 11530 // going to remain in this docshell, we need to let to the UI know about it 11531 // (at least in the top-level case). 11532 FireOnLocationChange(this, aboutBlankChannel, aLoadState->URI(), 0); 11533 11534 if (SessionHistoryInParent()) { 11535 MoveLoadingToActiveEntry(false, 0, nullptr); 11536 } 11537 11538 doc->BeginLoad(); 11539 11540 nsContentUtils::AddScriptRunner( 11541 new nsDocElementCreatedNotificationRunner(doc)); 11542 // When scripts are not blocked (are they ever blocked here?), the runnable 11543 // runs immediately, so let's check if this docshell got destroyed or the 11544 // document got swapped. Unclear if this ever happens; this is a defensive 11545 // check. 11546 if (mIsBeingDestroyed || !mDocumentViewer || 11547 doc != mDocumentViewer->GetDocument()) { 11548 return NS_OK; 11549 } 11550 11551 // Initialize the presShell here in the window.open() case. 11552 RefPtr<PresShell> presShell = doc->GetPresShell(); 11553 if (presShell && !presShell->DidInitialize()) { 11554 rv = presShell->Initialize(); 11555 NS_ENSURE_SUCCESS(rv, rv); 11556 } 11557 11558 doc->SetScrollToRef(doc->GetDocumentURI()); 11559 11560 OnStopRequest(aboutBlankChannel, NS_OK); 11561 11562 doc->EndLoad(); 11563 // Can't assert any postcondition, because the load event 11564 // handler may have started loading something new in this 11565 // docshell. 11566 11567 return NS_OK; 11568 } 11569 11570 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure, 11571 const char* aFromRawSegment, 11572 uint32_t aToOffset, uint32_t aCount, 11573 uint32_t* aWriteCount) { 11574 // aFromSegment now contains aCount bytes of data. 11575 11576 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure); 11577 buf->Append(aFromRawSegment, aCount); 11578 11579 // Indicate that we have consumed all of aFromSegment 11580 *aWriteCount = aCount; 11581 return NS_OK; 11582 } 11583 11584 /* static */ nsresult nsDocShell::AddHeadersToChannel( 11585 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) { 11586 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel); 11587 NS_ENSURE_STATE(httpChannel); 11588 11589 uint32_t numRead; 11590 nsAutoCString headersString; 11591 nsresult rv = aHeadersData->ReadSegments( 11592 AppendSegmentToString, &headersString, UINT32_MAX, &numRead); 11593 NS_ENSURE_SUCCESS(rv, rv); 11594 11595 // used during the manipulation of the String from the InputStream 11596 nsAutoCString headerName; 11597 nsAutoCString headerValue; 11598 int32_t crlf; 11599 int32_t colon; 11600 11601 // 11602 // Iterate over the headersString: for each "\r\n" delimited chunk, 11603 // add the value as a header to the nsIHttpChannel 11604 // 11605 11606 static const char kWhitespace[] = "\b\t\r\n "; 11607 while (true) { 11608 crlf = headersString.Find("\r\n"); 11609 if (crlf == kNotFound) { 11610 return NS_OK; 11611 } 11612 11613 const nsACString& oneHeader = StringHead(headersString, crlf); 11614 11615 colon = oneHeader.FindChar(':'); 11616 if (colon == kNotFound) { 11617 return NS_ERROR_UNEXPECTED; 11618 } 11619 11620 headerName = StringHead(oneHeader, colon); 11621 headerValue = Substring(oneHeader, colon + 1); 11622 11623 headerName.Trim(kWhitespace); 11624 headerValue.Trim(kWhitespace); 11625 11626 headersString.Cut(0, crlf + 2); 11627 11628 // 11629 // FINALLY: we can set the header! 11630 // 11631 11632 rv = httpChannel->SetRequestHeader(headerName, headerValue, true); 11633 NS_ENSURE_SUCCESS(rv, rv); 11634 } 11635 11636 MOZ_ASSERT_UNREACHABLE("oops"); 11637 return NS_ERROR_UNEXPECTED; 11638 } 11639 11640 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags( 11641 BrowsingContext* aBrowsingContext, uint32_t aLoadType, 11642 bool aIsDocumentLoad) { 11643 MOZ_ASSERT(aBrowsingContext); 11644 11645 uint32_t openFlags = 0; 11646 if (aLoadType == LOAD_LINK) { 11647 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED; 11648 } 11649 if (!aBrowsingContext->GetAllowContentRetargeting()) { 11650 openFlags |= nsIURILoader::DONT_RETARGET; 11651 } 11652 11653 if (!aIsDocumentLoad) { 11654 openFlags |= nsIURILoader::IS_OBJECT_EMBED; 11655 11656 // Unless the pref is set, object/embed loads always specify DONT_RETARGET. 11657 // See bug 1868001 for details. 11658 if (!StaticPrefs::dom_navigation_object_embed_allow_retargeting()) { 11659 openFlags |= nsIURILoader::DONT_RETARGET; 11660 } 11661 } 11662 11663 return openFlags; 11664 } 11665 11666 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel, 11667 nsIURILoader* aURILoader, 11668 uint32_t aOpenFlags) { 11669 nsresult rv = NS_OK; 11670 11671 // If anything fails here, make sure to clear our initial ClientSource. 11672 auto cleanupInitialClient = 11673 MakeScopeExit([&] { mInitialClientSource.reset(); }); 11674 11675 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow(); 11676 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE); 11677 11678 MaybeCreateInitialClientSource(); 11679 11680 // Let the client channel helper know if we are using DocumentChannel, 11681 // since redirects get handled in the parent process in that case. 11682 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel); 11683 if (docChannel && XRE_IsContentProcess()) { 11684 // Tell the content process nsDocumentOpenInfo to not try to do 11685 // any sort of targeting. 11686 aOpenFlags |= nsIURILoader::DONT_RETARGET; 11687 } 11688 11689 // Since we are loading a document we need to make sure the proper reserved 11690 // and initial client data is stored on the nsILoadInfo. The 11691 // ClientChannelHelper does this and ensures that it is propagated properly 11692 // on redirects. We pass no reserved client here so that the helper will 11693 // create the reserved ClientSource if necessary. 11694 Maybe<ClientInfo> noReservedClient; 11695 if (docChannel) { 11696 // When using DocumentChannel, all redirect handling is done in the parent, 11697 // so we just need the child variant to watch for the internal redirect 11698 // to the final channel. 11699 rv = AddClientChannelHelperInChild(aChannel, 11700 GetMainThreadSerialEventTarget()); 11701 docChannel->SetInitialClientInfo(GetInitialClientInfo()); 11702 } else { 11703 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient), 11704 GetInitialClientInfo(), 11705 GetMainThreadSerialEventTarget()); 11706 } 11707 NS_ENSURE_SUCCESS(rv, rv); 11708 11709 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this); 11710 NS_ENSURE_SUCCESS(rv, rv); 11711 11712 // We're about to load a new page and it may take time before necko 11713 // gives back any data, so main thread might have a chance to process a 11714 // collector slice 11715 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL); 11716 11717 // Success. Keep the initial ClientSource if it exists. 11718 cleanupInitialClient.release(); 11719 11720 return NS_OK; 11721 } 11722 11723 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) { 11724 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel(); 11725 MOZ_ASSERT(channel); 11726 11727 // If anything fails here, make sure to clear our initial ClientSource. 11728 auto cleanupInitialClient = 11729 MakeScopeExit([&] { mInitialClientSource.reset(); }); 11730 11731 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow(); 11732 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE); 11733 11734 MaybeCreateInitialClientSource(); 11735 11736 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 11737 11738 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get()); 11739 if (loadInfo->GetExternalContentPolicyType() == 11740 ExtContentPolicy::TYPE_DOCUMENT) { 11741 li->UpdateBrowsingContextID(mBrowsingContext->Id()); 11742 } else if (loadInfo->GetExternalContentPolicyType() == 11743 ExtContentPolicy::TYPE_SUBDOCUMENT) { 11744 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id()); 11745 } 11746 11747 // If we did a process switch, then we should have an existing allocated 11748 // ClientInfo, so we just need to allocate a corresponding ClientSource. 11749 CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget()); 11750 11751 uint32_t documentOpenInfoFlags = nsIURILoader::DONT_RETARGET; 11752 if (loadInfo->GetExternalContentPolicyType() == 11753 ExtContentPolicy::TYPE_OBJECT) { 11754 documentOpenInfoFlags |= nsIURILoader::IS_OBJECT_EMBED; 11755 } 11756 11757 RefPtr<nsDocumentOpenInfo> loader = 11758 new nsDocumentOpenInfo(this, documentOpenInfoFlags, nullptr); 11759 channel->SetLoadGroup(mLoadGroup); 11760 11761 MOZ_ALWAYS_SUCCEEDS(loader->Prepare()); 11762 11763 nsresult rv = NS_OK; 11764 if (XRE_IsParentProcess()) { 11765 // If we're in the parent, the we don't have an nsIChildChannel, just 11766 // the original channel, which is already open in this process. 11767 11768 // DocumentLoadListener expects to get an nsIParentChannel, so 11769 // we create a wrapper around the channel and nsIStreamListener 11770 // that forwards functionality as needed, and then we register 11771 // it under the provided identifier. 11772 RefPtr<ParentChannelWrapper> wrapper = 11773 new ParentChannelWrapper(channel, loader); 11774 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId()); 11775 11776 mLoadGroup->AddRequest(channel, nullptr); 11777 } else if (nsCOMPtr<nsIChildChannel> childChannel = 11778 do_QueryInterface(channel)) { 11779 // Our channel was redirected from another process, so doesn't need to 11780 // be opened again. However, it does need its listener hooked up 11781 // correctly. 11782 rv = childChannel->CompleteRedirectSetup(loader); 11783 } else { 11784 // It's possible for the redirected channel to not implement 11785 // nsIChildChannel and be entirely local (like srcdoc). In that case we 11786 // can just open the local instance and it will work. 11787 rv = channel->AsyncOpen(loader); 11788 } 11789 if (rv == NS_ERROR_NO_CONTENT) { 11790 return NS_OK; 11791 } 11792 NS_ENSURE_SUCCESS(rv, rv); 11793 11794 // Success. Keep the initial ClientSource if it exists. 11795 cleanupInitialClient.release(); 11796 return NS_OK; 11797 } 11798 11799 // https://html.spec.whatwg.org/#scrolling-to-a-fragment 11800 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef, 11801 nsACString& aNewHash, uint32_t aLoadType) { 11802 if (!mCurrentURI) { 11803 return NS_OK; 11804 } 11805 11806 RefPtr<PresShell> presShell = GetPresShell(); 11807 if (!presShell) { 11808 // If we failed to get the shell, or if there is no shell, 11809 // nothing left to do here. 11810 return NS_OK; 11811 } 11812 11813 ScrollContainerFrame* rootScroll = presShell->GetRootScrollContainerFrame(); 11814 if (rootScroll) { 11815 rootScroll->ClearDidHistoryRestore(); 11816 } 11817 11818 // If it's a load from history, we don't have any anchor jumping to do. 11819 // Scrollbar position will be restored by the caller based on positions stored 11820 // in session history. 11821 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL; 11822 // If the load contains text directives, try to apply them. This may fail if 11823 // the load is a same-document load that was initiated before the document was 11824 // fully loaded and the target is not yet included in the DOM tree. 11825 // For this case, the `uninvokedTextDirectives` are not cleared, so that 11826 // `Document::ScrollToRef()` can re-apply the text directive. 11827 // `Document::ScrollToRef()` is (presumably) the second "async" call mentioned 11828 // in sec. 7.4.2.3.3 in the HTML spec, "Fragment navigations": 11829 // https://html.spec.whatwg.org/#scroll-to-fragid:~:text=This%20algorithm%20will%20be%20called%20twice 11830 11831 const RefPtr fragmentDirective = GetDocument()->FragmentDirective(); 11832 const nsTArray<RefPtr<nsRange>> textDirectiveRanges = 11833 fragmentDirective->FindTextFragmentsInDocument(); 11834 fragmentDirective->HighlightTextDirectives(textDirectiveRanges); 11835 const bool scrollToTextDirective = 11836 !textDirectiveRanges.IsEmpty() && 11837 fragmentDirective->IsTextDirectiveAllowedToBeScrolledTo(); 11838 const RefPtr<nsRange> textDirectiveToScroll = 11839 scrollToTextDirective ? textDirectiveRanges[0] : nullptr; 11840 11841 // If we have no new anchor, we do not want to scroll, unless there is a 11842 // current anchor and we are doing a history load. So return if we have no 11843 // new anchor, and there is no current anchor or the load is not a history 11844 // load. 11845 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef && 11846 !scrollToTextDirective) { 11847 return NS_OK; 11848 } 11849 11850 // Both the new and current URIs refer to the same page. We can now 11851 // browse to the hash stored in the new URI. 11852 11853 if (aNewHash.IsEmpty() && !scrollToTextDirective) { 11854 // 2. If fragment is the empty string, then return the special value top of 11855 // the document. 11856 // 11857 // Tell the shell it's at an anchor without scrolling. 11858 presShell->GoToAnchor(u""_ns, nullptr, false); 11859 11860 if (scroll) { 11861 // Scroll to the top of the page. Ignore the return value; failure to 11862 // scroll here (e.g. if there is no root scrollframe) is not grounds for 11863 // canceling the load! 11864 SetCurScrollPosEx(0, 0); 11865 } 11866 11867 return NS_OK; 11868 } 11869 11870 // 3. Let potentialIndicatedElement be the result of finding a potential 11871 // indicated element given document and fragment. 11872 NS_ConvertUTF8toUTF16 uStr(aNewHash); 11873 11874 MOZ_ASSERT(!uStr.IsEmpty() || scrollToTextDirective); 11875 11876 auto rv = presShell->GoToAnchor(uStr, textDirectiveToScroll, scroll, 11877 ScrollFlags::ScrollSmoothAuto); 11878 11879 // 4. If potentialIndicatedElement is not null, then return 11880 // potentialIndicatedElement. 11881 if (NS_SUCCEEDED(rv)) { 11882 return NS_OK; 11883 } 11884 11885 // 5. Let fragmentBytes be the result of percent-decoding fragment. 11886 nsAutoCString fragmentBytes; 11887 const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(), 11888 /* aFlags = */ 0, fragmentBytes); 11889 11890 if (!unescaped) { 11891 // Another attempt is only necessary if characters were unescaped. 11892 return NS_OK; 11893 } 11894 11895 if (fragmentBytes.IsEmpty()) { 11896 // When aNewHash contains "%00", the unescaped string may be empty, and 11897 // GoToAnchor asserts if we ask it to scroll to an empty ref. 11898 presShell->GoToAnchor(u""_ns, nullptr, false); 11899 return NS_OK; 11900 } 11901 11902 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on 11903 // fragmentBytes. 11904 nsAutoString decodedFragment; 11905 rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment); 11906 NS_ENSURE_SUCCESS(rv, rv); 11907 11908 // 7. Set potentialIndicatedElement to the result of finding a potential 11909 // indicated element given document and decodedFragment. 11910 // 11911 // Ignore the return value of GoToAnchor, since it will return an error if 11912 // there is no such anchor in the document, which is actually a success 11913 // condition for us (we want to update the session history with the new URI no 11914 // matter whether we actually scrolled somewhere). 11915 presShell->GoToAnchor(decodedFragment, nullptr, scroll, 11916 ScrollFlags::ScrollSmoothAuto); 11917 11918 return NS_OK; 11919 } 11920 11921 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel, 11922 nsIPrincipal* aTriggeringPrincipal, 11923 nsIPrincipal* aPrincipalToInherit, 11924 nsIPrincipal* aPartitionedPrincipalToInherit, 11925 nsIPolicyContainer* aPolicyContainer, 11926 bool aAddToGlobalHistory, bool aCloneSHChildren) { 11927 MOZ_ASSERT(aURI, "uri is null"); 11928 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set"); 11929 11930 MOZ_ASSERT(!aPrincipalToInherit || 11931 (aPrincipalToInherit && aTriggeringPrincipal)); 11932 11933 #if defined(DEBUG) 11934 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) { 11935 nsAutoCString chanName; 11936 if (aChannel) { 11937 aChannel->GetName(chanName); 11938 } else { 11939 chanName.AssignLiteral("<no channel>"); 11940 } 11941 11942 MOZ_LOG(gDocShellLog, LogLevel::Debug, 11943 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this, 11944 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType)); 11945 } 11946 #endif 11947 11948 bool equalUri = false; 11949 11950 // Get the post data and the HTTP response code from the channel. 11951 uint32_t responseStatus = 0; 11952 nsCOMPtr<nsIInputStream> inputStream; 11953 if (aChannel) { 11954 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel)); 11955 11956 // Check if the HTTPChannel is hiding under a multiPartChannel 11957 if (!httpChannel) { 11958 GetHttpChannel(aChannel, getter_AddRefs(httpChannel)); 11959 } 11960 11961 if (httpChannel) { 11962 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel)); 11963 if (uploadChannel) { 11964 uploadChannel->GetUploadStream(getter_AddRefs(inputStream)); 11965 } 11966 11967 // If the response status indicates an error, unlink this session 11968 // history entry from any entries sharing its document. 11969 nsresult rv = httpChannel->GetResponseStatus(&responseStatus); 11970 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) { 11971 mLSHE->AbandonBFCacheEntry(); 11972 // FIXME Do the same for mLoadingEntry 11973 } 11974 } 11975 } 11976 11977 // Determine if this type of load should update history. 11978 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType); 11979 11980 // We don't update session history on reload unless we're loading 11981 // an iframe in shift-reload case. 11982 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType); 11983 11984 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the 11985 // root browsing context. 11986 // FIXME If session history in the parent is enabled then we only do this if 11987 // the session history object is in process, otherwise we can't really 11988 // use the mLSHE anyway. Once session history is only stored in the 11989 // parent then this code will probably be removed anyway. 11990 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 11991 if (!rootSH) { 11992 updateSHistory = false; 11993 updateGHistory = false; // XXX Why global history too? 11994 } 11995 11996 // Check if the url to be loaded is the same as the one already loaded. 11997 if (mCurrentURI) { 11998 aURI->Equals(mCurrentURI, &equalUri); 11999 } 12000 12001 #ifdef DEBUG 12002 bool shAvailable = (rootSH != nullptr); 12003 12004 // XXX This log message is almost useless because |updateSHistory| 12005 // and |updateGHistory| are not correct at this point. 12006 12007 MOZ_LOG(gDocShellLog, LogLevel::Debug, 12008 (" shAvailable=%i updateSHistory=%i updateGHistory=%i" 12009 " equalURI=%i\n", 12010 shAvailable, updateSHistory, updateGHistory, equalUri)); 12011 #endif 12012 12013 /* If the url to be loaded is the same as the one already there, 12014 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or 12015 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that 12016 * AddToSessionHistory() won't mess with the current SHEntry and 12017 * if this page has any frame children, it also will be handled 12018 * properly. see bug 83684 12019 * 12020 * NB: If mOSHE is null but we have a current URI, then it probably 12021 * means that we must be at the transient about:blank content viewer; 12022 * we should let the normal load continue, since there's nothing to 12023 * replace. Sometimes this happens after a session restore (eg process 12024 * switch) and mCurrentURI is not about:blank; we assume we can let the load 12025 * continue (Bug 1301399). 12026 * 12027 * XXX Hopefully changing the loadType at this time will not hurt 12028 * anywhere. The other way to take care of sequentially repeating 12029 * frameset pages is to add new methods to nsIDocShellTreeItem. 12030 * Hopefully I don't have to do that. 12031 */ 12032 if (equalUri && 12033 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) && 12034 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK || 12035 mLoadType == LOAD_STOP_CONTENT) && 12036 !inputStream) { 12037 mLoadType = LOAD_NORMAL_REPLACE; 12038 } 12039 12040 // If this is a refresh to the currently loaded url, we don't 12041 // have to update session or global history. 12042 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) { 12043 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing()); 12044 } 12045 12046 /* If the user pressed shift-reload, cache will create a new cache key 12047 * for the page. Save the new cacheKey in Session History. 12048 * see bug 90098 12049 */ 12050 if (aChannel && IsForceReloadType(mLoadType)) { 12051 MOZ_ASSERT(!updateSHistory || IsSubframe(), 12052 "We shouldn't be updating session history for forced" 12053 " reloads unless we're in a newly created iframe!"); 12054 12055 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel)); 12056 uint32_t cacheKey = 0; 12057 // Get the Cache Key and store it in SH. 12058 if (cacheChannel) { 12059 cacheChannel->GetCacheKey(&cacheKey); 12060 } 12061 // If we already have a loading history entry, store the new cache key 12062 // in it. Otherwise, since we're doing a reload and won't be updating 12063 // our history entry, store the cache key in our current history entry. 12064 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey); 12065 12066 if (!mozilla::SessionHistoryInParent()) { 12067 // Since we're force-reloading, clear all the sub frame history. 12068 ClearFrameHistory(mLSHE); 12069 ClearFrameHistory(mOSHE); 12070 } 12071 } 12072 12073 if (!mozilla::SessionHistoryInParent()) { 12074 // Clear subframe history on refresh. 12075 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in 12076 // this case. One should re-validate after bug 1331865 fixed. 12077 if (mLoadType == LOAD_REFRESH) { 12078 ClearFrameHistory(mLSHE); 12079 ClearFrameHistory(mOSHE); 12080 } 12081 12082 if (updateSHistory) { 12083 // Update session history if necessary... 12084 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) { 12085 /* This is a fresh page getting loaded for the first time 12086 *.Create a Entry for it and add it to SH, if this is the 12087 * rootDocShell 12088 */ 12089 (void)AddToSessionHistory( 12090 aURI, aChannel, aTriggeringPrincipal, aPrincipalToInherit, 12091 aPartitionedPrincipalToInherit, aPolicyContainer, aCloneSHChildren, 12092 getter_AddRefs(mLSHE)); 12093 } 12094 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) { 12095 // Even if we don't add anything to SHistory, ensure the current index 12096 // points to the same SHEntry as our mLSHE. 12097 12098 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex( 12099 mLSHE); 12100 } 12101 } 12102 12103 // If this is a POST request, we do not want to include this in global 12104 // history. 12105 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory && 12106 !net::ChannelIsPost(aChannel)) { 12107 nsCOMPtr<nsIURI> previousURI; 12108 uint32_t previousFlags = 0; 12109 12110 if (mLoadType & LOAD_CMD_RELOAD) { 12111 // On a reload request, we don't set redirecting flags. 12112 previousURI = aURI; 12113 } else { 12114 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags); 12115 } 12116 12117 AddURIVisit(aURI, previousURI, previousFlags, responseStatus); 12118 } 12119 12120 // If this was a history load or a refresh, or it was a history load but 12121 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index 12122 // in session history. 12123 if (!mozilla::SessionHistoryInParent() && rootSH && 12124 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) || 12125 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) { 12126 mPreviousEntryIndex = rootSH->Index(); 12127 if (!mozilla::SessionHistoryInParent()) { 12128 rootSH->LegacySHistory()->UpdateIndex(); 12129 } 12130 mLoadedEntryIndex = rootSH->Index(); 12131 MOZ_LOG(gPageCacheLog, LogLevel::Verbose, 12132 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex, 12133 mLoadedEntryIndex)); 12134 } 12135 12136 // aCloneSHChildren exactly means "we are not loading a new document". 12137 uint32_t locationFlags = 12138 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0; 12139 12140 bool onLocationChangeNeeded = 12141 SetCurrentURI(aURI, aChannel, false, 12142 /* aIsInitialAboutBlank */ false, locationFlags); 12143 // Make sure to store the referrer from the channel, if any 12144 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel)); 12145 if (httpChannel) { 12146 mReferrerInfo = httpChannel->GetReferrerInfo(); 12147 } 12148 return onLocationChangeNeeded; 12149 } 12150 12151 Maybe<Wireframe> nsDocShell::GetWireframe() { 12152 const bool collectWireFrame = 12153 mozilla::SessionHistoryInParent() && 12154 StaticPrefs::browser_history_collectWireframes() && 12155 mBrowsingContext->IsTopContent() && mActiveEntry; 12156 12157 if (!collectWireFrame) { 12158 return Nothing(); 12159 } 12160 12161 RefPtr<Document> doc = mDocumentViewer->GetDocument(); 12162 Nullable<Wireframe> wireframe; 12163 doc->GetWireframeWithoutFlushing(false, wireframe); 12164 if (wireframe.IsNull()) { 12165 return Nothing(); 12166 } 12167 return Some(wireframe.Value()); 12168 } 12169 12170 bool nsDocShell::CollectWireframe() { 12171 Maybe<Wireframe> wireframe = GetWireframe(); 12172 if (wireframe.isNothing()) { 12173 return false; 12174 } 12175 12176 if (XRE_IsParentProcess()) { 12177 SessionHistoryEntry* entry = 12178 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 12179 if (entry) { 12180 entry->SetWireframe(wireframe); 12181 } 12182 } else { 12183 (void)ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe( 12184 mBrowsingContext, wireframe.ref()); 12185 } 12186 12187 return true; 12188 } 12189 12190 //***************************************************************************** 12191 // nsDocShell: Session History 12192 //***************************************************************************** 12193 12194 NS_IMETHODIMP 12195 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle, 12196 const nsAString& aURL, bool aReplace, JSContext* aCx) { 12197 MOZ_LOG(gSHLog, LogLevel::Debug, 12198 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this, 12199 NS_ConvertUTF16toUTF8(aTitle).get(), 12200 NS_ConvertUTF16toUTF8(aURL).get(), aReplace)); 12201 // Implements History.pushState and History.replaceState 12202 12203 // Here's what we do, roughly in the order specified by HTML5. The specific 12204 // steps we are executing are at 12205 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>, 12206 // <https://html.spec.whatwg.org/#shared-history-push/replace-state-steps>, 12207 // and 12208 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>. 12209 // This function basically implements #dom-history-pushstate and 12210 // UpdateURLAndHistory implements #url-and-history-update-steps. 12211 // 12212 // A. Serialize aData using structured clone. This is #dom-history-pushstate 12213 // step 5. 12214 // B. If the third argument is present, #dom-history-pushstate step 7. 12215 // 7.1. Resolve the url, relative to our document. 12216 // 7.2. If (a) fails, raise a SECURITY_ERR 12217 // 7.4. Compare the resulting absolute URL to the document's address. If 12218 // any part of the URLs difer other than the <path>, <query>, and 12219 // <fragment> components, raise a SECURITY_ERR and abort. 12220 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3: 12221 // Remove from the session history all entries after the current entry, 12222 // as we would after a regular navigation, and save the current 12223 // entry's scroll position (bug 590573). 12224 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate, 12225 // either add a state object entry to the session history after the 12226 // current entry with the following properties, or modify the current 12227 // session history entry to set 12228 // a. cloned data as the state object, 12229 // b. if the third argument was present, the absolute URL found in 12230 // step 2 12231 // Also clear the new history entry's POST data (see bug 580069). 12232 // E. If aReplace is false (i.e. we're doing a pushState instead of a 12233 // replaceState), notify bfcache that we've navigated to a new page. 12234 // F. If the third argument is present, set the document's current address 12235 // to the absolute URL found in step B. This is 12236 // #url-and-history-update-steps step 4. 12237 // 12238 // It's important that this function not run arbitrary scripts after step A 12239 // and before completing step E. For example, if a script called 12240 // history.back() before we completed step E, bfcache might destroy an 12241 // active content viewer. Since EvictOutOfRangeDocumentViewers at the end of 12242 // step E might run script, we can't just put a script blocker around the 12243 // critical section. 12244 // 12245 // Note that we completely ignore the aTitle parameter. 12246 12247 nsresult rv; 12248 12249 RefPtr<Document> document = GetDocument(); 12250 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE); 12251 12252 // Don't clobber the load type of an existing network load. 12253 Maybe<AutoRestore<uint32_t>> loadTypeResetter; 12254 if (StaticPrefs:: 12255 docshell_shistory_sameDocumentNavigationOverridesLoadType() && 12256 !document->NodePrincipal()->IsURIInPrefList( 12257 "docshell.shistory.sameDocumentNavigationOverridesLoadType." 12258 "forceDisable")) { 12259 loadTypeResetter.emplace(mLoadType); 12260 } 12261 12262 // pushState effectively becomes replaceState when we've started a network 12263 // load but haven't adopted its document yet. This mirrors what we do with 12264 // changes to the hash at this stage of the game. 12265 if (JustStartedNetworkLoad()) { 12266 if (!loadTypeResetter.isSome()) { 12267 loadTypeResetter.emplace(mLoadType); 12268 } 12269 aReplace = true; 12270 } 12271 12272 // Step A: Serialize aData using structured clone. 12273 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate 12274 // step 5. 12275 nsCOMPtr<nsIStructuredCloneContainer> scContainer; 12276 12277 // scContainer->Init might cause arbitrary JS to run, and this code might 12278 // navigate the page we're on, potentially to a different origin! (bug 12279 // 634834) To protect against this, we abort if our principal changes due 12280 // to the InitFromJSVal() call. 12281 { 12282 RefPtr<Document> origDocument = GetDocument(); 12283 if (!origDocument) { 12284 return NS_ERROR_DOM_SECURITY_ERR; 12285 } 12286 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal(); 12287 12288 scContainer = new nsStructuredCloneContainer(); 12289 rv = scContainer->InitFromJSVal(aData, aCx); 12290 NS_ENSURE_SUCCESS(rv, rv); 12291 12292 RefPtr<Document> newDocument = GetDocument(); 12293 if (!newDocument) { 12294 return NS_ERROR_DOM_SECURITY_ERR; 12295 } 12296 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal(); 12297 12298 bool principalsEqual = false; 12299 origPrincipal->Equals(newPrincipal, &principalsEqual); 12300 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR); 12301 } 12302 12303 // Check that the state object isn't too long. 12304 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize(); 12305 if (maxStateObjSize < 0) { 12306 maxStateObjSize = 0; 12307 } 12308 12309 uint64_t scSize; 12310 rv = scContainer->GetSerializedNBytes(&scSize); 12311 NS_ENSURE_SUCCESS(rv, rv); 12312 12313 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE); 12314 12315 // Step B: Resolve aURL. 12316 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate 12317 // step 7. 12318 bool equalURIs = true; 12319 nsCOMPtr<nsIURI> currentURI; 12320 if (mCurrentURI) { 12321 currentURI = nsIOService::CreateExposableURI(mCurrentURI); 12322 } else { 12323 currentURI = mCurrentURI; 12324 } 12325 nsCOMPtr<nsIURI> newURI; 12326 if (aURL.Length() == 0) { 12327 newURI = currentURI; 12328 } else { 12329 // 7.1: Resolve aURL relative to mURI 12330 12331 nsIURI* docBaseURI = document->GetDocBaseURI(); 12332 if (!docBaseURI) { 12333 return NS_ERROR_FAILURE; 12334 } 12335 12336 nsAutoCString spec; 12337 docBaseURI->GetSpec(spec); 12338 12339 rv = NS_NewURI(getter_AddRefs(newURI), aURL, 12340 document->GetDocumentCharacterSet(), docBaseURI); 12341 12342 // 7.2: If 2a fails, raise a SECURITY_ERR 12343 if (NS_FAILED(rv)) { 12344 return NS_ERROR_DOM_SECURITY_ERR; 12345 } 12346 12347 if (!document->CanRewriteURL(newURI)) { 12348 return NS_ERROR_DOM_SECURITY_ERR; 12349 } 12350 12351 if (currentURI) { 12352 currentURI->Equals(newURI, &equalURIs); 12353 } else { 12354 equalURIs = false; 12355 } 12356 12357 } // end of same-origin check 12358 12359 // https://html.spec.whatwg.org/#shared-history-push/replace-state-steps 12360 // Step 8 12361 if (nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow()) { 12362 if (RefPtr<Navigation> navigation = window->Navigation()) { 12363 bool shouldContinue = navigation->FirePushReplaceReloadNavigateEvent( 12364 aCx, aReplace ? NavigationType::Replace : NavigationType::Push, 12365 newURI, 12366 /* aIsSameDocument */ true, 12367 /* aUserInvolvement */ Nothing(), 12368 /* aSourceElement */ nullptr, /* aFormDataEntryList */ nullptr, 12369 /* aNavigationAPIState */ nullptr, scContainer); 12370 12371 // Step 9 12372 if (!shouldContinue) { 12373 return NS_OK; 12374 } 12375 } 12376 } 12377 12378 // Step 10 12379 // Run #url-and-history-update-steps 12380 rv = UpdateURLAndHistory(document, newURI, scContainer, 12381 aReplace ? NavigationHistoryBehavior::Replace 12382 : NavigationHistoryBehavior::Push, 12383 currentURI, equalURIs); 12384 NS_ENSURE_SUCCESS(rv, rv); 12385 12386 return NS_OK; 12387 } 12388 12389 nsresult nsDocShell::UpdateURLAndHistory( 12390 Document* aDocument, nsIURI* aNewURI, nsIStructuredCloneContainer* aData, 12391 NavigationHistoryBehavior aHistoryHandling, nsIURI* aCurrentURI, 12392 bool aEqualURIs) { 12393 // Implements 12394 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps 12395 MOZ_ASSERT(aHistoryHandling != NavigationHistoryBehavior::Auto); 12396 bool isReplace = aHistoryHandling == NavigationHistoryBehavior::Replace; 12397 12398 // If we have a pending title change, handle it before creating a new entry. 12399 aDocument->DoNotifyPossibleTitleChange(); 12400 12401 // Step 2, if aReplace is false: Create a new entry in the session 12402 // history. This will erase all SHEntries after the new entry and make this 12403 // entry the current one. This operation may modify mOSHE, which we need 12404 // later, so we keep a reference here. 12405 NS_ENSURE_TRUE(mOSHE || mActiveEntry || isReplace, NS_ERROR_FAILURE); 12406 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE; 12407 12408 // If this push/replaceState changed the document's current URI and the new 12409 // URI differs from the old URI in more than the hash, or if the old 12410 // SHEntry's URI was modified in this way by a push/replaceState call 12411 // set URIWasModified to true for the current SHEntry (bug 669671). 12412 bool sameExceptHashes = true; 12413 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes); 12414 bool uriWasModified; 12415 if (sameExceptHashes) { 12416 if (mozilla::SessionHistoryInParent()) { 12417 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified(); 12418 } else { 12419 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified(); 12420 } 12421 } else { 12422 uriWasModified = true; 12423 } 12424 12425 mLoadType = LOAD_PUSHSTATE; 12426 12427 nsCOMPtr<nsISHEntry> newSHEntry; 12428 if (!isReplace) { 12429 // Step 2. 12430 12431 // Step 2.2, "Remove any tasks queued by the history traversal task 12432 // source that are associated with any Document objects in the 12433 // top-level browsing context's document family." This is very hard in 12434 // SessionHistoryInParent since we can't synchronously access the 12435 // pending navigations that are already sent to the parent. We can 12436 // abort any AsyncGo navigations that are waiting to be sent. If we 12437 // send a message to the parent, it would be processed after any 12438 // navigations previously sent. So long as we consider the "history 12439 // traversal task source" to be the list in this process we match the 12440 // spec. If we move the entire list to the parent, we can handle the 12441 // aborting of loads there, but we don't have a way to synchronously 12442 // remove entries as we do here for non-SHIP. 12443 RefPtr<ChildSHistory> shistory = GetRootSessionHistory(); 12444 if (shistory) { 12445 shistory->RemovePendingHistoryNavigations(); 12446 } 12447 12448 nsPoint scrollPos = GetCurScrollPos(); 12449 12450 bool scrollRestorationIsManual; 12451 if (mozilla::SessionHistoryInParent()) { 12452 // FIXME Need to save the current scroll position on mActiveEntry. 12453 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual(); 12454 } else { 12455 // Save the current scroll position (bug 590573). Step 2.3. 12456 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y); 12457 12458 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual(); 12459 } 12460 12461 nsCOMPtr<nsIPolicyContainer> policyContainer = 12462 aDocument->GetPolicyContainer(); 12463 12464 if (mozilla::SessionHistoryInParent()) { 12465 MOZ_LOG(gSHLog, LogLevel::Debug, 12466 ("nsDocShell %p UpdateActiveEntry (not replacing)", this)); 12467 12468 nsString title(mActiveEntry->GetTitle()); 12469 nsCOMPtr<nsIReferrerInfo> referrerInfo = mActiveEntry->GetReferrerInfo(); 12470 12471 UpdateActiveEntry(false, 12472 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI, 12473 /* aOriginalURI = */ nullptr, 12474 /* aReferrerInfo = */ referrerInfo, 12475 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(), 12476 policyContainer, title, scrollRestorationIsManual, 12477 aData, uriWasModified); 12478 } else { 12479 // Since we're not changing which page we have loaded, pass 12480 // true for aCloneChildren. 12481 nsresult rv = AddToSessionHistory( 12482 aNewURI, nullptr, 12483 aDocument->NodePrincipal(), // triggeringPrincipal 12484 nullptr, nullptr, policyContainer, true, getter_AddRefs(newSHEntry)); 12485 NS_ENSURE_SUCCESS(rv, rv); 12486 12487 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE); 12488 12489 // Session history entries created by pushState inherit scroll restoration 12490 // mode from the current entry. 12491 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual); 12492 12493 // Set the new SHEntry's title (bug 655273). 12494 nsString title; 12495 mOSHE->GetTitle(title); 12496 newSHEntry->SetTitle(title); 12497 12498 nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo(); 12499 newSHEntry->SetReferrerInfo(referrerInfo); 12500 12501 // Link the new SHEntry to the old SHEntry's BFCache entry, since the 12502 // two entries correspond to the same document. 12503 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE), 12504 NS_ERROR_FAILURE); 12505 12506 // AddToSessionHistory may not modify mOSHE. In case it doesn't, 12507 // we'll just set mOSHE here. 12508 mOSHE = newSHEntry; 12509 } 12510 } else if (mozilla::SessionHistoryInParent()) { 12511 MOZ_LOG(gSHLog, LogLevel::Debug, 12512 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p", 12513 this, mActiveEntry.get())); 12514 // Setting the resultPrincipalURI to nullptr is fine here: it will cause 12515 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI 12516 // in our case. We could also set it to aNewURI, with the same result. 12517 // We don't use aTitle here, see bug 544535. 12518 nsString title; 12519 nsCOMPtr<nsIReferrerInfo> referrerInfo; 12520 if (mActiveEntry) { 12521 title = mActiveEntry->GetTitle(); 12522 referrerInfo = mActiveEntry->GetReferrerInfo(); 12523 } else { 12524 referrerInfo = nullptr; 12525 } 12526 UpdateActiveEntry( 12527 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI, 12528 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(), 12529 aDocument->GetPolicyContainer(), title, 12530 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData, 12531 uriWasModified); 12532 } else { 12533 // Step 3. 12534 newSHEntry = mOSHE; 12535 12536 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this)); 12537 // Since we're not changing which page we have loaded, pass 12538 // true for aCloneChildren. 12539 if (!newSHEntry) { 12540 nsresult rv = AddToSessionHistory( 12541 aNewURI, nullptr, 12542 aDocument->NodePrincipal(), // triggeringPrincipal 12543 nullptr, nullptr, aDocument->GetPolicyContainer(), true, 12544 getter_AddRefs(newSHEntry)); 12545 NS_ENSURE_SUCCESS(rv, rv); 12546 mOSHE = newSHEntry; 12547 } 12548 12549 nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo(); 12550 12551 newSHEntry->SetURI(aNewURI); 12552 newSHEntry->SetOriginalURI(aNewURI); 12553 // We replaced the URI of the entry, clear the unstripped URI as it 12554 // shouldn't be used for reloads anymore. 12555 newSHEntry->SetUnstrippedURI(nullptr); 12556 // Setting the resultPrincipalURI to nullptr is fine here: it will cause 12557 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI 12558 // in our case. We could also set it to aNewURI, with the same result. 12559 newSHEntry->SetResultPrincipalURI(nullptr); 12560 newSHEntry->SetLoadReplace(false); 12561 newSHEntry->SetReferrerInfo(referrerInfo); 12562 } 12563 12564 if (!mozilla::SessionHistoryInParent()) { 12565 // Step 2.4 and 3: Modify new/original session history entry and clear its 12566 // POST data, if there is any. 12567 newSHEntry->SetStateData(aData); 12568 newSHEntry->SetPostData(nullptr); 12569 12570 newSHEntry->SetURIWasModified(uriWasModified); 12571 12572 // Step E as described at the top of AddState: If aReplace is false, 12573 // indicating that we're doing a pushState rather than a replaceState, 12574 // notify bfcache that we've added a page to the history so it can evict 12575 // content viewers if appropriate. Otherwise call ReplaceEntry so that we 12576 // notify nsIHistoryListeners that an entry was replaced. We may not have a 12577 // root session history if this call is coming from a document.open() in a 12578 // docshell subtree that disables session history. 12579 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); 12580 if (rootSH) { 12581 rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry, 12582 isReplace); 12583 } 12584 } 12585 12586 // Step 4: If the document's URI changed, update document's URI and update 12587 // global history. 12588 // 12589 // We need to call FireOnLocationChange so that the browser's address bar 12590 // gets updated and the back button is enabled, but we only need to 12591 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI, 12592 // since SetCurrentURI will call FireOnLocationChange for us. 12593 // 12594 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass 12595 // nullptr for aRequest param to FireOnLocationChange(...). Such an update 12596 // notification is allowed only when we know docshell is not loading a new 12597 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise, 12598 // FireOnLocationChange(...) breaks security UI. 12599 // 12600 // If the docshell is shutting down, don't update the document URI, as we 12601 // can't load into a docshell that is being destroyed. 12602 if (!aEqualURIs && !mIsBeingDestroyed) { 12603 aDocument->SetDocumentURI(aNewURI); 12604 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true, 12605 /* aIsInitialAboutBlank */ false, 12606 GetSameDocumentNavigationFlags(aNewURI)); 12607 12608 AddURIVisit(aNewURI, aCurrentURI, 0); 12609 12610 // AddURIVisit doesn't set the title for the new URI in global history, 12611 // so do that here. 12612 UpdateGlobalHistoryTitle(aNewURI); 12613 12614 // Inform the favicon service that our old favicon applies to this new 12615 // URI. 12616 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing()); 12617 } else { 12618 FireDummyOnLocationChange(); 12619 } 12620 aDocument->SetStateObject(aData); 12621 12622 if (RefPtr navigation = aDocument->GetInnerWindow()->Navigation()) { 12623 MOZ_LOG(gNavigationAPILog, LogLevel::Debug, 12624 ("nsDocShell %p triggering a navigation event for a same-document " 12625 "navigation from UpdateURLAndHistory -> isReplace: %s", 12626 this, isReplace ? "true" : "false")); 12627 // Step 11: Update the navigation API entries for a same-document 12628 // navigation given document's relevant global object's navigation API, 12629 // newEntry, and historyHandling. 12630 navigation->UpdateEntriesForSameDocumentNavigation( 12631 mActiveEntry.get(), 12632 isReplace ? NavigationType::Replace : NavigationType::Push); 12633 } 12634 12635 return NS_OK; 12636 } 12637 12638 NS_IMETHODIMP 12639 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) { 12640 if (mozilla::SessionHistoryInParent()) { 12641 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(); 12642 return NS_OK; 12643 } 12644 12645 *aIsManual = false; 12646 if (mOSHE) { 12647 return mOSHE->GetScrollRestorationIsManual(aIsManual); 12648 } 12649 12650 return NS_OK; 12651 } 12652 12653 NS_IMETHODIMP 12654 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) { 12655 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual); 12656 12657 return NS_OK; 12658 } 12659 12660 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry( 12661 nsISHEntry* aSHEntry, bool aIsManual) { 12662 if (aSHEntry) { 12663 aSHEntry->SetScrollRestorationIsManual(aIsManual); 12664 } 12665 12666 if (mActiveEntry && mBrowsingContext) { 12667 mActiveEntry->SetScrollRestorationIsManual(aIsManual); 12668 if (XRE_IsParentProcess()) { 12669 SessionHistoryEntry* entry = 12670 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 12671 if (entry) { 12672 entry->SetScrollRestorationIsManual(aIsManual); 12673 } 12674 } else { 12675 (void)ContentChild::GetSingleton() 12676 ->SendSessionHistoryEntryScrollRestorationIsManual(mBrowsingContext, 12677 aIsManual); 12678 } 12679 } 12680 } 12681 12682 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry, 12683 uint32_t aCacheKey) { 12684 if (aSHEntry) { 12685 aSHEntry->SetCacheKey(aCacheKey); 12686 } 12687 12688 if (mActiveEntry && mBrowsingContext) { 12689 mActiveEntry->SetCacheKey(aCacheKey); 12690 if (XRE_IsParentProcess()) { 12691 SessionHistoryEntry* entry = 12692 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 12693 if (entry) { 12694 entry->SetCacheKey(aCacheKey); 12695 } 12696 } else { 12697 (void)ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey( 12698 mBrowsingContext, aCacheKey); 12699 } 12700 } 12701 } 12702 12703 /* static */ 12704 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) { 12705 // I believe none of the about: urls should go in the history. But then 12706 // that could just be me... If the intent is only deny about:blank then we 12707 // should just do a spec compare, rather than two gets of the scheme and 12708 // then the path. -Gagan 12709 nsresult rv; 12710 nsAutoCString buf; 12711 12712 rv = aURI->GetScheme(buf); 12713 if (NS_FAILED(rv)) { 12714 return false; 12715 } 12716 12717 if (buf.EqualsLiteral("about")) { 12718 rv = aURI->GetPathQueryRef(buf); 12719 if (NS_FAILED(rv)) { 12720 return false; 12721 } 12722 12723 if (buf.EqualsLiteral("blank")) { 12724 return false; 12725 } 12726 // We only want to add about:newtab if it's not privileged, and 12727 // if it is not configured to show the blank page. 12728 if (buf.EqualsLiteral("newtab")) { 12729 if (!StaticPrefs::browser_newtabpage_enabled()) { 12730 return false; 12731 } 12732 12733 NS_ENSURE_TRUE(aChannel, false); 12734 nsCOMPtr<nsIPrincipal> resultPrincipal; 12735 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 12736 aChannel, getter_AddRefs(resultPrincipal)); 12737 NS_ENSURE_SUCCESS(rv, false); 12738 return !resultPrincipal->IsSystemPrincipal(); 12739 } 12740 } 12741 12742 return true; 12743 } 12744 12745 nsresult nsDocShell::AddToSessionHistory( 12746 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal, 12747 nsIPrincipal* aPrincipalToInherit, 12748 nsIPrincipal* aPartitionedPrincipalToInherit, 12749 nsIPolicyContainer* aPolicyContainer, bool aCloneChildren, 12750 nsISHEntry** aNewEntry) { 12751 MOZ_ASSERT(aURI, "uri is null"); 12752 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set"); 12753 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent()); 12754 12755 #if defined(DEBUG) 12756 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) { 12757 nsAutoCString chanName; 12758 if (aChannel) { 12759 aChannel->GetName(chanName); 12760 } else { 12761 chanName.AssignLiteral("<no channel>"); 12762 } 12763 12764 MOZ_LOG(gDocShellLog, LogLevel::Debug, 12765 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this, 12766 aURI->GetSpecOrDefault().get(), chanName.get())); 12767 } 12768 #endif 12769 12770 nsresult rv = NS_OK; 12771 nsCOMPtr<nsISHEntry> entry; 12772 12773 /* 12774 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use 12775 * the existing SH entry in the page and replace the url and 12776 * other vitalities. 12777 */ 12778 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) && 12779 !mBrowsingContext->IsTop()) { 12780 // This is a subframe 12781 entry = mOSHE; 12782 if (entry) { 12783 entry->ClearEntry(); 12784 } 12785 } 12786 12787 // Create a new entry if necessary. 12788 if (!entry) { 12789 entry = new nsSHEntry(); 12790 } 12791 12792 // Get the post data & referrer 12793 nsCOMPtr<nsIInputStream> inputStream; 12794 nsCOMPtr<nsIURI> originalURI; 12795 nsCOMPtr<nsIURI> resultPrincipalURI; 12796 nsCOMPtr<nsIURI> unstrippedURI; 12797 bool loadReplace = false; 12798 nsCOMPtr<nsIReferrerInfo> referrerInfo; 12799 uint32_t cacheKey = 0; 12800 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal; 12801 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit; 12802 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit = 12803 aPartitionedPrincipalToInherit; 12804 nsCOMPtr<nsIPolicyContainer> policyContainer = aPolicyContainer; 12805 bool expired = false; // by default the page is not expired 12806 bool discardLayoutState = false; 12807 nsCOMPtr<nsICacheInfoChannel> cacheChannel; 12808 bool userActivation = false; 12809 12810 if (aChannel) { 12811 cacheChannel = do_QueryInterface(aChannel); 12812 12813 /* If there is a caching channel, get the Cache Key and store it 12814 * in SH. 12815 */ 12816 if (cacheChannel) { 12817 cacheChannel->GetCacheKey(&cacheKey); 12818 } 12819 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel)); 12820 12821 // Check if the httpChannel is hiding under a multipartChannel 12822 if (!httpChannel) { 12823 GetHttpChannel(aChannel, getter_AddRefs(httpChannel)); 12824 } 12825 if (httpChannel) { 12826 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel)); 12827 if (uploadChannel) { 12828 uploadChannel->GetUploadStream(getter_AddRefs(inputStream)); 12829 } 12830 httpChannel->GetOriginalURI(getter_AddRefs(originalURI)); 12831 uint32_t loadFlags; 12832 aChannel->GetLoadFlags(&loadFlags); 12833 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE; 12834 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo)); 12835 MOZ_ASSERT(NS_SUCCEEDED(rv)); 12836 12837 discardLayoutState = ShouldDiscardLayoutState(httpChannel); 12838 } 12839 12840 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 12841 if (!triggeringPrincipal) { 12842 triggeringPrincipal = loadInfo->TriggeringPrincipal(); 12843 } 12844 if (!policyContainer) { 12845 policyContainer = loadInfo->GetPolicyContainerToInherit(); 12846 } 12847 12848 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)); 12849 12850 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI)); 12851 12852 userActivation = loadInfo->GetHasValidUserGestureActivation(); 12853 12854 // For now keep storing just the principal in the SHEntry. 12855 if (!principalToInherit) { 12856 if (loadInfo->GetLoadingSandboxed()) { 12857 if (loadInfo->GetLoadingPrincipal()) { 12858 principalToInherit = NullPrincipal::CreateWithInheritedAttributes( 12859 loadInfo->GetLoadingPrincipal()); 12860 } else { 12861 // get the OriginAttributes 12862 OriginAttributes attrs; 12863 loadInfo->GetOriginAttributes(&attrs); 12864 principalToInherit = NullPrincipal::Create(attrs); 12865 } 12866 } else { 12867 principalToInherit = loadInfo->PrincipalToInherit(); 12868 } 12869 } 12870 12871 if (!partitionedPrincipalToInherit) { 12872 // XXXehsan is it correct to fall back to the principal to inherit in all 12873 // cases? For example, what about the cases where we are using the load 12874 // info's principal to inherit? Do we need to add a similar concept to 12875 // load info for partitioned principal? 12876 partitionedPrincipalToInherit = principalToInherit; 12877 } 12878 } 12879 12880 nsAutoString srcdoc; 12881 bool srcdocEntry = false; 12882 nsCOMPtr<nsIURI> baseURI; 12883 12884 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel); 12885 if (inStrmChan) { 12886 bool isSrcdocChannel; 12887 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel); 12888 if (isSrcdocChannel) { 12889 inStrmChan->GetSrcdocData(srcdoc); 12890 srcdocEntry = true; 12891 inStrmChan->GetBaseURI(getter_AddRefs(baseURI)); 12892 } else { 12893 srcdoc.SetIsVoid(true); 12894 } 12895 } 12896 /* If cache got a 'no-store', ask SH not to store 12897 * HistoryLayoutState. By default, SH will set this 12898 * flag to true and save HistoryLayoutState. 12899 */ 12900 bool saveLayoutState = !discardLayoutState; 12901 12902 if (cacheChannel) { 12903 // Check if the page has expired from cache 12904 uint32_t expTime = 0; 12905 cacheChannel->GetCacheTokenExpirationTime(&expTime); 12906 uint32_t now = PRTimeToSeconds(PR_Now()); 12907 if (expTime <= now) { 12908 expired = true; 12909 } 12910 } 12911 12912 // Title is set in nsDocShell::SetTitle() 12913 entry->Create(aURI, // uri 12914 u""_ns, // Title 12915 inputStream, // Post data stream 12916 cacheKey, // CacheKey 12917 mContentTypeHint, // Content-type 12918 triggeringPrincipal, // Channel or provided principal 12919 principalToInherit, partitionedPrincipalToInherit, 12920 policyContainer, HistoryID(), GetCreatedDynamically(), 12921 originalURI, resultPrincipalURI, unstrippedURI, loadReplace, 12922 referrerInfo, srcdoc, srcdocEntry, baseURI, saveLayoutState, 12923 expired, userActivation); 12924 12925 if (mBrowsingContext->IsTop() && GetSessionHistory()) { 12926 Maybe<int32_t> previousEntryIndex; 12927 Maybe<int32_t> loadedEntryIndex; 12928 12929 if (mBrowsingContext->IsTop() && 12930 !ShouldAddToSessionHistory(aURI, aChannel)) { 12931 entry->SetTransient(); 12932 } 12933 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory( 12934 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType, 12935 &previousEntryIndex, &loadedEntryIndex); 12936 12937 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history"); 12938 if (previousEntryIndex.isSome()) { 12939 mPreviousEntryIndex = previousEntryIndex.value(); 12940 } 12941 if (loadedEntryIndex.isSome()) { 12942 mLoadedEntryIndex = loadedEntryIndex.value(); 12943 } 12944 12945 // aCloneChildren implies that we are retaining the same document, thus we 12946 // need to signal to the top WC that the new SHEntry may receive a fresh 12947 // user interaction flag. 12948 if (aCloneChildren) { 12949 WindowContext* topWc = mBrowsingContext->GetTopWindowContext(); 12950 if (topWc && !topWc->IsDiscarded()) { 12951 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false)); 12952 } 12953 } 12954 } else { 12955 // This is a subframe, make sure that this new SHEntry will be 12956 // marked with user interaction. 12957 WindowContext* topWc = mBrowsingContext->GetTopWindowContext(); 12958 if (topWc && !topWc->IsDiscarded()) { 12959 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false)); 12960 } 12961 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) { 12962 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(), 12963 aCloneChildren); 12964 } 12965 } 12966 12967 // Return the new SH entry... 12968 if (aNewEntry) { 12969 *aNewEntry = nullptr; 12970 if (NS_SUCCEEDED(rv)) { 12971 entry.forget(aNewEntry); 12972 } 12973 } 12974 12975 return rv; 12976 } 12977 12978 void nsDocShell::UpdateActiveEntry( 12979 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI, 12980 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo, 12981 nsIPrincipal* aTriggeringPrincipal, nsIPolicyContainer* aPolicyContainer, 12982 const nsAString& aTitle, bool aScrollRestorationIsManual, 12983 nsIStructuredCloneContainer* aData, bool aURIWasModified) { 12984 MOZ_ASSERT(mozilla::SessionHistoryInParent()); 12985 MOZ_ASSERT(aURI, "uri is null"); 12986 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE, 12987 "This code only deals with pushState"); 12988 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace); 12989 12990 MOZ_LOG(gSHLog, LogLevel::Debug, 12991 ("Creating an active entry on nsDocShell %p to %s", this, 12992 aURI->GetSpecOrDefault().get())); 12993 12994 // Even if we're replacing an existing entry we create new a 12995 // SessionHistoryInfo. In the parent process we'll keep the existing 12996 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the 12997 // entry keeps identity but its data is replaced. 12998 bool replace = aReplace && mActiveEntry; 12999 13000 if (!replace) { 13001 CollectWireframe(); 13002 } 13003 13004 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release()); 13005 if (previousActiveEntry) { 13006 // Link this entry to the previous active entry. 13007 mActiveEntry = MakeUnique<SessionHistoryInfo>(*previousActiveEntry, aURI); 13008 } else { 13009 mActiveEntry = MakeUnique<SessionHistoryInfo>( 13010 aURI, aTriggeringPrincipal, nullptr, nullptr, aPolicyContainer, 13011 mContentTypeHint); 13012 } 13013 mActiveEntry->SetOriginalURI(aOriginalURI); 13014 mActiveEntry->SetUnstrippedURI(nullptr); 13015 mActiveEntry->SetReferrerInfo(aReferrerInfo); 13016 mActiveEntry->SetTitle(aTitle); 13017 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData)); 13018 mActiveEntry->SetURIWasModified(aURIWasModified); 13019 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual); 13020 13021 if (replace) { 13022 mActiveEntry->NavigationKey() = previousActiveEntry->NavigationKey(); 13023 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get()); 13024 } else { 13025 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext(); 13026 // FIXME We should probably just compute mChildOffset in the parent 13027 // instead of passing it over IPC here. 13028 mBrowsingContext->SetActiveSessionHistoryEntry( 13029 aPreviousScrollPos, mActiveEntry.get(), previousActiveEntry.get(), 13030 mLoadType, 13031 /* aCacheKey = */ 0); 13032 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex? 13033 } 13034 } 13035 13036 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType, 13037 bool aUserActivation) { 13038 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE); 13039 13040 nsresult rv; 13041 RefPtr<nsDocShellLoadState> loadState; 13042 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState)); 13043 NS_ENSURE_SUCCESS(rv, rv); 13044 13045 // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if 13046 // that's the only thing holding a ref to aEntry that will cause aEntry to 13047 // die while we're loading it. So hold a strong ref to aEntry here, just 13048 // in case. 13049 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry); 13050 13051 loadState->SetHasValidUserGestureActivation( 13052 loadState->HasValidUserGestureActivation() || aUserActivation); 13053 13054 loadState->SetTextDirectiveUserActivation( 13055 loadState->GetTextDirectiveUserActivation() || aUserActivation); 13056 13057 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE); 13058 } 13059 13060 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry, 13061 uint32_t aLoadType, bool aUserActivation, 13062 bool aNotifiedBeforeUnloadListeners) { 13063 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo(); 13064 loadState->SetHasValidUserGestureActivation( 13065 loadState->HasValidUserGestureActivation() || aUserActivation); 13066 13067 loadState->SetTextDirectiveUserActivation( 13068 loadState->GetTextDirectiveUserActivation() || aUserActivation); 13069 13070 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners); 13071 13072 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry); 13073 } 13074 13075 void nsDocShell::MaybeFireTraverseHistory(nsDocShellLoadState* aLoadState) { 13076 if (!Navigation::IsAPIEnabled()) { 13077 return; 13078 } 13079 13080 BrowsingContext* browsingContext = GetBrowsingContext(); 13081 if (!browsingContext || browsingContext->IsTop()) { 13082 return; 13083 } 13084 13085 if (!mActiveEntry || !aLoadState->GetLoadingSessionHistoryInfo()) { 13086 return; 13087 } 13088 if (mActiveEntry->NavigationKey() == 13089 aLoadState->GetLoadingSessionHistoryInfo()->mInfo.NavigationKey()) { 13090 return; 13091 } 13092 13093 nsCOMPtr activeURI = mActiveEntry->GetURIOrInheritedForAboutBlank(); 13094 nsCOMPtr<nsIURI> loadingURI = aLoadState->GetLoadingSessionHistoryInfo() 13095 ->mInfo.GetURIOrInheritedForAboutBlank(); 13096 if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI( 13097 activeURI, loadingURI, 13098 /*reportError=*/false, 13099 /*fromPrivateWindow=*/false))) { 13100 return; 13101 } 13102 13103 if (RefPtr window = GetActiveWindow()) { 13104 if (RefPtr navigation = window->Navigation()) { 13105 if (AutoJSAPI jsapi; jsapi.Init(window)) { 13106 // This should send the correct user involvment. See bug 1903552. 13107 navigation->FireTraverseNavigateEvent( 13108 jsapi.cx(), aLoadState->GetLoadingSessionHistoryInfo()->mInfo, 13109 Nothing()); 13110 } 13111 } 13112 } 13113 } 13114 13115 nsIDocumentViewer::PermitUnloadResult 13116 nsDocShell::MaybeFireTraversableTraverseHistory( 13117 const SessionHistoryInfo& aInfo, 13118 Maybe<UserNavigationInvolvement> aUserInvolvement) { 13119 MOZ_DIAGNOSTIC_ASSERT(GetBrowsingContext()); 13120 MOZ_DIAGNOSTIC_ASSERT(GetBrowsingContext()->IsTop()); 13121 13122 SetOngoingNavigation(Some(OngoingNavigation::Traversal)); 13123 13124 nsIDocumentViewer::PermitUnloadResult finalStatus = 13125 nsIDocumentViewer::eContinue; 13126 if (RefPtr<nsPIDOMWindowInner> activeWindow = GetActiveWindow()) { 13127 if (RefPtr navigation = activeWindow->Navigation()) { 13128 if (AutoJSAPI jsapi; jsapi.Init(activeWindow)) { 13129 bool shouldContinue = navigation->FireTraverseNavigateEvent( 13130 jsapi.cx(), aInfo, aUserInvolvement); 13131 13132 if (!shouldContinue) { 13133 finalStatus = nsIDocumentViewer::eCanceledByNavigate; 13134 } 13135 } 13136 } 13137 } 13138 13139 return finalStatus; 13140 } 13141 13142 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState, 13143 uint32_t aLoadType, 13144 bool aLoadingCurrentEntry) { 13145 if (!IsNavigationAllowed()) { 13146 return NS_OK; 13147 } 13148 13149 // We are setting load type afterwards so we don't have to 13150 // send it in an IPC message 13151 aLoadState->SetLoadType(aLoadType); 13152 13153 SetOngoingNavigation(Some(OngoingNavigation::Traversal)); 13154 13155 nsresult rv; 13156 if (aLoadState->URI()->SchemeIs("javascript")) { 13157 // We're loading a URL that will execute script from inside asyncOpen. 13158 // Replace the current document with about:blank now to prevent 13159 // anything from the current document from leaking into any JavaScript 13160 // code in the URL. 13161 // Don't cache the presentation if we're going to just reload the 13162 // current entry. Caching would lead to trying to save the different 13163 // content viewers in the same nsISHEntry object. 13164 rv = CreateAboutBlankDocumentViewer( 13165 aLoadState->PrincipalToInherit(), 13166 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr, 13167 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry); 13168 13169 if (NS_FAILED(rv)) { 13170 // The creation of the intermittent about:blank content 13171 // viewer failed for some reason (potentially because the 13172 // user prevented it). Interrupt the history load. 13173 return NS_OK; 13174 } 13175 13176 if (!aLoadState->TriggeringPrincipal()) { 13177 // Ensure that we have a triggeringPrincipal. Otherwise javascript: 13178 // URIs will pick it up from the about:blank page we just loaded, 13179 // and we don't really want even that in this case. 13180 nsCOMPtr<nsIPrincipal> principal = 13181 NullPrincipal::Create(GetOriginAttributes()); 13182 aLoadState->SetTriggeringPrincipal(principal); 13183 } 13184 } 13185 13186 /* If there is a valid postdata *and* the user pressed 13187 * reload or shift-reload, take user's permission before we 13188 * repost the data to the server. 13189 */ 13190 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) { 13191 bool repost; 13192 rv = ConfirmRepost(&repost); 13193 if (NS_FAILED(rv)) { 13194 return rv; 13195 } 13196 13197 // If the user pressed cancel in the dialog, return. We're done here. 13198 if (!repost) { 13199 return NS_BINDING_ABORTED; 13200 } 13201 } 13202 13203 // If there is no valid triggeringPrincipal, we deny the load 13204 MOZ_ASSERT(aLoadState->TriggeringPrincipal(), 13205 "need a valid triggeringPrincipal to load from history"); 13206 if (!aLoadState->TriggeringPrincipal()) { 13207 return NS_ERROR_FAILURE; 13208 } 13209 13210 MaybeFireTraverseHistory(aLoadState); 13211 13212 return InternalLoad(aLoadState); // No nsIRequest 13213 } 13214 13215 NS_IMETHODIMP 13216 nsDocShell::PersistLayoutHistoryState() { 13217 nsresult rv = NS_OK; 13218 13219 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) { 13220 bool scrollRestorationIsManual; 13221 if (mozilla::SessionHistoryInParent()) { 13222 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual(); 13223 } else { 13224 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual(); 13225 } 13226 nsCOMPtr<nsILayoutHistoryState> layoutState; 13227 if (RefPtr<PresShell> presShell = GetPresShell()) { 13228 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState)); 13229 } else if (scrollRestorationIsManual) { 13230 // Even if we don't have layout anymore, we may want to reset the 13231 // current scroll state in layout history. 13232 GetLayoutHistoryState(getter_AddRefs(layoutState)); 13233 } 13234 13235 if (scrollRestorationIsManual && layoutState) { 13236 layoutState->ResetScrollState(); 13237 } 13238 } 13239 13240 return rv; 13241 } 13242 13243 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry, 13244 nsISHEntry* aNewEntry) { 13245 if (aOldEntry == mOSHE) { 13246 mOSHE = aNewEntry; 13247 } 13248 13249 if (aOldEntry == mLSHE) { 13250 mLSHE = aNewEntry; 13251 } 13252 } 13253 13254 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE, 13255 const Maybe<nsISHEntry*>& aOSHE) { 13256 // We want to hold on to the reference in mLSHE before we update it. 13257 // Otherwise, SetHistoryEntry could release the last reference to 13258 // the entry while aOSHE is pointing to it. 13259 nsCOMPtr<nsISHEntry> deathGripOldLSHE; 13260 if (aLSHE.isSome()) { 13261 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value()); 13262 MOZ_ASSERT(mLSHE.get() == aLSHE.value()); 13263 } 13264 nsCOMPtr<nsISHEntry> deathGripOldOSHE; 13265 if (aOSHE.isSome()) { 13266 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value()); 13267 MOZ_ASSERT(mOSHE.get() == aOSHE.value()); 13268 } 13269 } 13270 13271 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry( 13272 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) { 13273 // We need to sync up the docshell and session history trees for 13274 // subframe navigation. If the load was in a subframe, we forward up to 13275 // the root docshell, which will then recursively sync up all docshells 13276 // to their corresponding entries in the new session history tree. 13277 // If we don't do this, then we can cache a content viewer on the wrong 13278 // cloned entry, and subsequently restore it at the wrong time. 13279 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top(); 13280 if (topBC->IsDiscarded()) { 13281 topBC = nullptr; 13282 } 13283 RefPtr<BrowsingContext> currBC = 13284 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext; 13285 if (topBC && *aPtr) { 13286 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC); 13287 } 13288 nsCOMPtr<nsISHEntry> entry(aEntry); 13289 entry.swap(*aPtr); 13290 return entry.forget(); 13291 } 13292 13293 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() { 13294 RefPtr<ChildSHistory> childSHistory = 13295 mBrowsingContext->Top()->GetChildSessionHistory(); 13296 return childSHistory.forget(); 13297 } 13298 13299 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel, 13300 nsIHttpChannel** aReturn) { 13301 NS_ENSURE_ARG_POINTER(aReturn); 13302 if (!aChannel) { 13303 return NS_ERROR_FAILURE; 13304 } 13305 13306 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel)); 13307 if (multiPartChannel) { 13308 nsCOMPtr<nsIChannel> baseChannel; 13309 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel)); 13310 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel)); 13311 *aReturn = httpChannel; 13312 NS_IF_ADDREF(*aReturn); 13313 } 13314 return NS_OK; 13315 } 13316 13317 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) { 13318 // By default layout State will be saved. 13319 if (!aChannel) { 13320 return false; 13321 } 13322 13323 // figure out if SH should be saving layout state 13324 bool noStore = false; 13325 (void)aChannel->IsNoStoreResponse(&noStore); 13326 return noStore; 13327 } 13328 13329 NS_IMETHODIMP 13330 nsDocShell::GetEditor(nsIEditor** aEditor) { 13331 NS_ENSURE_ARG_POINTER(aEditor); 13332 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal(); 13333 htmlEditor.forget(aEditor); 13334 return NS_OK; 13335 } 13336 13337 NS_IMETHODIMP 13338 nsDocShell::SetEditor(nsIEditor* aEditor) { 13339 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr; 13340 // If TextEditor comes, throw an error. 13341 if (aEditor && !htmlEditor) { 13342 return NS_ERROR_INVALID_ARG; 13343 } 13344 return SetHTMLEditorInternal(htmlEditor); 13345 } 13346 13347 HTMLEditor* nsDocShell::GetHTMLEditorInternal() { 13348 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr; 13349 } 13350 13351 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) { 13352 if (!aHTMLEditor && !mEditorData) { 13353 return NS_OK; 13354 } 13355 13356 nsresult rv = EnsureEditorData(); 13357 if (NS_FAILED(rv)) { 13358 return rv; 13359 } 13360 13361 return mEditorData->SetHTMLEditor(aHTMLEditor); 13362 } 13363 13364 NS_IMETHODIMP 13365 nsDocShell::GetEditable(bool* aEditable) { 13366 NS_ENSURE_ARG_POINTER(aEditable); 13367 *aEditable = mEditorData && mEditorData->GetEditable(); 13368 return NS_OK; 13369 } 13370 13371 NS_IMETHODIMP 13372 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) { 13373 NS_ENSURE_ARG_POINTER(aHasEditingSession); 13374 13375 if (mEditorData) { 13376 *aHasEditingSession = !!mEditorData->GetEditingSession(); 13377 } else { 13378 *aHasEditingSession = false; 13379 } 13380 13381 return NS_OK; 13382 } 13383 13384 NS_IMETHODIMP 13385 nsDocShell::MakeEditable(bool aInWaitForUriLoad) { 13386 nsresult rv = EnsureEditorData(); 13387 if (NS_FAILED(rv)) { 13388 return rv; 13389 } 13390 13391 return mEditorData->MakeEditable(aInWaitForUriLoad); 13392 } 13393 13394 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) { 13395 bool needToAddURIVisit = true; 13396 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel)); 13397 if (props) { 13398 (void)props->GetPropertyAsBool(u"docshell.needToAddURIVisit"_ns, 13399 &needToAddURIVisit); 13400 } 13401 13402 return needToAddURIVisit; 13403 } 13404 13405 /* static */ void nsDocShell::ExtractLastVisit( 13406 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) { 13407 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel)); 13408 if (!props) { 13409 return; 13410 } 13411 13412 nsresult rv; 13413 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv)); 13414 if (NS_SUCCEEDED(rv)) { 13415 uri.forget(aURI); 13416 13417 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns, 13418 aChannelRedirectFlags); 13419 13420 NS_WARNING_ASSERTION( 13421 NS_SUCCEEDED(rv), 13422 "Could not fetch previous flags, URI will be treated like referrer"); 13423 13424 } else { 13425 // There is no last visit for this channel, so this must be the first 13426 // link. Link the visit to the referrer of this request, if any. 13427 // Treat referrer as null if there is an error getting it. 13428 NS_GetReferrerFromChannel(aChannel, aURI); 13429 } 13430 } 13431 13432 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI, 13433 uint32_t aChannelRedirectFlags) { 13434 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel)); 13435 if (!props || !aURI) { 13436 return; 13437 } 13438 13439 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI); 13440 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns, 13441 aChannelRedirectFlags); 13442 } 13443 13444 /* static */ void nsDocShell::InternalAddURIVisit( 13445 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags, 13446 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext, 13447 nsIWidget* aWidget, uint32_t aLoadType, bool aWasUpgraded) { 13448 MOZ_ASSERT(aURI, "Visited URI is null!"); 13449 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY, 13450 "Do not add error or bypass pages to global history"); 13451 13452 bool usePrivateBrowsing = false; 13453 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing); 13454 13455 // Only content-type docshells save URI visits. Also don't do 13456 // anything here if we're not supposed to use global history. 13457 if (!aBrowsingContext->IsContent() || 13458 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) { 13459 return; 13460 } 13461 13462 nsCOMPtr<IHistory> history = components::History::Service(); 13463 13464 if (history) { 13465 uint32_t visitURIFlags = 0; 13466 13467 if (aBrowsingContext->IsTop()) { 13468 visitURIFlags |= IHistory::TOP_LEVEL; 13469 } 13470 13471 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) { 13472 visitURIFlags |= IHistory::REDIRECT_TEMPORARY; 13473 } else if (aChannelRedirectFlags & 13474 nsIChannelEventSink::REDIRECT_PERMANENT) { 13475 visitURIFlags |= IHistory::REDIRECT_PERMANENT; 13476 } else { 13477 MOZ_ASSERT(!aChannelRedirectFlags, 13478 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set " 13479 "if any flags in aChannelRedirectFlags is set."); 13480 } 13481 13482 if (aResponseStatus >= 300 && aResponseStatus < 400) { 13483 visitURIFlags |= IHistory::REDIRECT_SOURCE; 13484 if (aResponseStatus == 301 || aResponseStatus == 308) { 13485 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT; 13486 } 13487 } 13488 // Errors 400-501 and 505 are considered unrecoverable, in the sense a 13489 // simple retry attempt by the user is unlikely to solve them. 13490 // 408 is special cased, since may actually indicate a temporary 13491 // connection problem. 13492 else if (aResponseStatus != 408 && 13493 ((aResponseStatus >= 400 && aResponseStatus <= 501) || 13494 aResponseStatus == 505)) { 13495 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR; 13496 } 13497 13498 if (aWasUpgraded) { 13499 visitURIFlags |= 13500 IHistory::REDIRECT_SOURCE | IHistory::REDIRECT_SOURCE_UPGRADED; 13501 } 13502 13503 (void)history->VisitURI(aWidget, aURI, aPreviousURI, visitURIFlags, 13504 aBrowsingContext->BrowserId()); 13505 } 13506 } 13507 13508 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI, 13509 uint32_t aChannelRedirectFlags, 13510 uint32_t aResponseStatus) { 13511 nsPIDOMWindowOuter* outer = GetWindow(); 13512 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer); 13513 13514 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags, 13515 aResponseStatus, mBrowsingContext, widget, mLoadType, 13516 false); 13517 } 13518 13519 //***************************************************************************** 13520 // nsDocShell: Helper Routines 13521 //***************************************************************************** 13522 13523 NS_IMETHODIMP 13524 nsDocShell::SetLoadType(uint32_t aLoadType) { 13525 mLoadType = aLoadType; 13526 return NS_OK; 13527 } 13528 13529 NS_IMETHODIMP 13530 nsDocShell::GetLoadType(uint32_t* aLoadType) { 13531 *aLoadType = mLoadType; 13532 return NS_OK; 13533 } 13534 13535 nsresult nsDocShell::ConfirmRepost(bool* aRepost) { 13536 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) { 13537 *aRepost = true; 13538 return NS_OK; 13539 } 13540 13541 nsCOMPtr<nsIPromptCollection> prompter = 13542 do_GetService("@mozilla.org/embedcomp/prompt-collection;1"); 13543 if (!prompter) { 13544 return NS_ERROR_NOT_AVAILABLE; 13545 } 13546 13547 return prompter->ConfirmRepost(mBrowsingContext, aRepost); 13548 } 13549 13550 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt, 13551 nsIStringBundle** aStringBundle) { 13552 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt), 13553 NS_ERROR_FAILURE); 13554 13555 nsCOMPtr<nsIStringBundleService> stringBundleService = 13556 mozilla::components::StringBundle::Service(); 13557 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE); 13558 13559 NS_ENSURE_SUCCESS( 13560 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle), 13561 NS_ERROR_FAILURE); 13562 13563 return NS_OK; 13564 } 13565 13566 ScrollContainerFrame* nsDocShell::GetRootScrollContainerFrame() { 13567 PresShell* presShell = GetPresShell(); 13568 NS_ENSURE_TRUE(presShell, nullptr); 13569 13570 return presShell->GetRootScrollContainerFrame(); 13571 } 13572 13573 nsresult nsDocShell::EnsureScriptEnvironment() { 13574 if (mScriptGlobal) { 13575 return NS_OK; 13576 } 13577 13578 if (mIsBeingDestroyed) { 13579 return NS_ERROR_NOT_AVAILABLE; 13580 } 13581 13582 #ifdef DEBUG 13583 NS_ASSERTION(!mInEnsureScriptEnv, 13584 "Infinite loop! Calling EnsureScriptEnvironment() from " 13585 "within EnsureScriptEnvironment()!"); 13586 13587 // Yeah, this isn't re-entrant safe, but that's ok since if we 13588 // re-enter this method, we'll infinitely loop... 13589 AutoRestore<bool> boolSetter(mInEnsureScriptEnv); 13590 mInEnsureScriptEnv = true; 13591 #endif 13592 13593 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner)); 13594 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE); 13595 13596 uint32_t chromeFlags; 13597 browserChrome->GetChromeFlags(&chromeFlags); 13598 13599 // If our window is modal and we're not opened as chrome, make 13600 // this window a modal content window. 13601 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome); 13602 MOZ_ASSERT(mScriptGlobal); 13603 13604 // Ensure the script object is set up to run script. 13605 return mScriptGlobal->EnsureScriptEnvironment(); 13606 } 13607 13608 nsresult nsDocShell::EnsureEditorData() { 13609 MOZ_ASSERT(!mIsBeingDestroyed); 13610 13611 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor(); 13612 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) { 13613 // We shouldn't recreate the editor data if it already exists, or 13614 // we're shutting down, or we already have a detached editor data 13615 // stored in the session history. We should only have one editordata 13616 // per docshell. 13617 mEditorData = MakeUnique<nsDocShellEditorData>(this); 13618 } 13619 13620 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE; 13621 } 13622 13623 nsresult nsDocShell::EnsureFind() { 13624 if (!mFind) { 13625 mFind = new nsWebBrowserFind(); 13626 } 13627 13628 // we promise that the nsIWebBrowserFind that we return has been set 13629 // up to point to the focused, or content window, so we have to 13630 // set that up each time. 13631 13632 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject(); 13633 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED); 13634 13635 // default to our window 13636 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO); 13637 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch; 13638 nsFocusManager::GetFocusedDescendant(ourWindow, 13639 nsFocusManager::eIncludeAllDescendants, 13640 getter_AddRefs(windowToSearch)); 13641 13642 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind); 13643 if (!findInFrames) { 13644 return NS_ERROR_NO_INTERFACE; 13645 } 13646 13647 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow); 13648 if (NS_FAILED(rv)) { 13649 return rv; 13650 } 13651 rv = findInFrames->SetCurrentSearchFrame(windowToSearch); 13652 if (NS_FAILED(rv)) { 13653 return rv; 13654 } 13655 13656 return NS_OK; 13657 } 13658 13659 NS_IMETHODIMP 13660 nsDocShell::IsBeingDestroyed(bool* aDoomed) { 13661 NS_ENSURE_ARG(aDoomed); 13662 *aDoomed = mIsBeingDestroyed; 13663 return NS_OK; 13664 } 13665 13666 NS_IMETHODIMP 13667 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) { 13668 NS_ENSURE_ARG(aResult); 13669 *aResult = mIsExecutingOnLoadHandler; 13670 return NS_OK; 13671 } 13672 13673 NS_IMETHODIMP 13674 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) { 13675 nsCOMPtr<nsILayoutHistoryState> state; 13676 if (mozilla::SessionHistoryInParent()) { 13677 if (mActiveEntry) { 13678 state = mActiveEntry->GetLayoutHistoryState(); 13679 } 13680 } else { 13681 if (mOSHE) { 13682 state = mOSHE->GetLayoutHistoryState(); 13683 } 13684 } 13685 state.forget(aLayoutHistoryState); 13686 return NS_OK; 13687 } 13688 13689 NS_IMETHODIMP 13690 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) { 13691 if (mOSHE) { 13692 mOSHE->SetLayoutHistoryState(aLayoutHistoryState); 13693 } 13694 if (mActiveEntry) { 13695 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState); 13696 } 13697 return NS_OK; 13698 } 13699 13700 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy( 13701 nsIInterfaceRequestor* aRequestor) { 13702 if (aRequestor) { 13703 mWeakPtr = do_GetWeakReference(aRequestor); 13704 } 13705 } 13706 13707 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() { 13708 mWeakPtr = nullptr; 13709 } 13710 13711 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor) 13712 13713 NS_IMETHODIMP 13714 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID, 13715 void** aSink) { 13716 NS_ENSURE_ARG_POINTER(aSink); 13717 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr); 13718 if (ifReq) { 13719 return ifReq->GetInterface(aIID, aSink); 13720 } 13721 *aSink = nullptr; 13722 return NS_NOINTERFACE; 13723 } 13724 13725 //***************************************************************************** 13726 // nsDocShell::nsIAuthPromptProvider 13727 //***************************************************************************** 13728 13729 NS_IMETHODIMP 13730 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID, 13731 void** aResult) { 13732 // a priority prompt request will override a false mAllowAuth setting 13733 bool priorityPrompt = (aPromptReason == PROMPT_PROXY); 13734 13735 if (!mAllowAuth && !priorityPrompt) { 13736 return NS_ERROR_NOT_AVAILABLE; 13737 } 13738 13739 // we're either allowing auth, or it's a proxy request 13740 nsresult rv; 13741 nsCOMPtr<nsIPromptFactory> wwatch = 13742 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); 13743 NS_ENSURE_SUCCESS(rv, rv); 13744 13745 rv = EnsureScriptEnvironment(); 13746 NS_ENSURE_SUCCESS(rv, rv); 13747 13748 // Get the an auth prompter for our window so that the parenting 13749 // of the dialogs works as it should when using tabs. 13750 13751 return wwatch->GetPrompt(mScriptGlobal, aIID, 13752 reinterpret_cast<void**>(aResult)); 13753 } 13754 13755 //***************************************************************************** 13756 // nsDocShell::nsILoadContext 13757 //***************************************************************************** 13758 13759 NS_IMETHODIMP 13760 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) { 13761 CallGetInterface(this, aWindow); 13762 return NS_OK; 13763 } 13764 13765 NS_IMETHODIMP 13766 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) { 13767 return mBrowsingContext->GetTopWindow(aWindow); 13768 } 13769 13770 NS_IMETHODIMP 13771 nsDocShell::GetTopFrameElement(Element** aElement) { 13772 return mBrowsingContext->GetTopFrameElement(aElement); 13773 } 13774 13775 NS_IMETHODIMP 13776 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) { 13777 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection); 13778 } 13779 13780 NS_IMETHODIMP 13781 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) { 13782 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection); 13783 } 13784 13785 NS_IMETHODIMP 13786 nsDocShell::GetIsContent(bool* aIsContent) { 13787 *aIsContent = (mItemType == typeContent); 13788 return NS_OK; 13789 } 13790 13791 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) { 13792 MOZ_ASSERT(aURI, "Must have a URI!"); 13793 13794 if (!mFiredUnloadEvent) { 13795 return true; 13796 } 13797 13798 if (!mLoadingURI) { 13799 return false; 13800 } 13801 13802 bool isPrivateWin = false; 13803 Document* doc = GetDocument(); 13804 if (doc) { 13805 isPrivateWin = 13806 doc->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing(); 13807 } 13808 13809 nsCOMPtr<nsIScriptSecurityManager> secMan = 13810 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); 13811 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI( 13812 aURI, mLoadingURI, false, isPrivateWin)); 13813 } 13814 13815 // 13816 // Routines for selection and clipboard 13817 // 13818 nsresult nsDocShell::GetControllerForCommand(const char* aCommand, 13819 nsIController** aResult) { 13820 NS_ENSURE_ARG_POINTER(aResult); 13821 *aResult = nullptr; 13822 13823 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE); 13824 13825 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot(); 13826 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE); 13827 13828 return root->GetControllerForCommand(aCommand, false /* for any window */, 13829 aResult); 13830 } 13831 13832 NS_IMETHODIMP 13833 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) { 13834 NS_ENSURE_ARG_POINTER(aResult); 13835 *aResult = false; 13836 13837 nsresult rv = NS_ERROR_FAILURE; 13838 13839 nsCOMPtr<nsIController> controller; 13840 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller)); 13841 if (controller) { 13842 rv = controller->IsCommandEnabled(aCommand, aResult); 13843 } 13844 13845 return rv; 13846 } 13847 13848 NS_IMETHODIMP 13849 nsDocShell::DoCommand(const char* aCommand) { 13850 nsresult rv = NS_ERROR_FAILURE; 13851 13852 nsCOMPtr<nsIController> controller; 13853 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller)); 13854 if (controller) { 13855 rv = controller->DoCommand(aCommand); 13856 } 13857 13858 return rv; 13859 } 13860 13861 NS_IMETHODIMP 13862 nsDocShell::DoCommandWithParams(const char* aCommand, 13863 nsICommandParams* aParams) { 13864 nsCOMPtr<nsIController> controller; 13865 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller)); 13866 if (NS_WARN_IF(NS_FAILED(rv))) { 13867 return rv; 13868 } 13869 13870 nsCOMPtr<nsICommandController> commandController = 13871 do_QueryInterface(controller, &rv); 13872 if (NS_WARN_IF(NS_FAILED(rv))) { 13873 return rv; 13874 } 13875 13876 return commandController->DoCommandWithParams(aCommand, aParams); 13877 } 13878 13879 nsresult nsDocShell::EnsureCommandHandler() { 13880 if (!mCommandManager) { 13881 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) { 13882 mCommandManager = new nsCommandManager(domWindow); 13883 } 13884 } 13885 return mCommandManager ? NS_OK : NS_ERROR_FAILURE; 13886 } 13887 13888 // link handling 13889 13890 class OnLinkClickEvent : public Runnable { 13891 public: 13892 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent, 13893 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied, 13894 nsIPrincipal* aTriggeringPrincipal); 13895 13896 NS_IMETHOD Run() override { 13897 // We need to set up an AutoJSAPI here for the following reason: When we 13898 // do OnLinkClickSync we'll eventually end up in 13899 // nsGlobalWindow::OpenInternal which only does popup blocking if 13900 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that 13901 // we don't look like native code as far as LegacyIsCallerNativeCode() is 13902 // concerned. (Bug 1930445) 13903 AutoJSAPI jsapi; 13904 if (jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) { 13905 mLoadState->SetSourceElement(mContent->AsElement()); 13906 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied, 13907 mTriggeringPrincipal); 13908 } 13909 return NS_OK; 13910 } 13911 13912 private: 13913 RefPtr<nsDocShell> mHandler; 13914 nsCOMPtr<nsIContent> mContent; 13915 RefPtr<nsDocShellLoadState> mLoadState; 13916 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal; 13917 bool mNoOpenerImplied; 13918 }; 13919 13920 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent, 13921 nsDocShellLoadState* aLoadState, 13922 bool aNoOpenerImplied, 13923 nsIPrincipal* aTriggeringPrincipal) 13924 : mozilla::Runnable("OnLinkClickEvent"), 13925 mHandler(aHandler), 13926 mContent(aContent), 13927 mLoadState(aLoadState), 13928 mTriggeringPrincipal(aTriggeringPrincipal), 13929 mNoOpenerImplied(aNoOpenerImplied) {} 13930 13931 nsresult nsDocShell::OnLinkClick( 13932 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec, 13933 const nsAString& aFileName, nsIInputStream* aPostDataStream, 13934 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, 13935 UserNavigationInvolvement aUserInvolvement, 13936 nsIPrincipal* aTriggeringPrincipal, nsIPolicyContainer* aPolicyContainer) { 13937 #ifndef ANDROID 13938 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal"); 13939 #endif 13940 NS_ASSERTION(NS_IsMainThread(), "wrong thread"); 13941 13942 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) { 13943 return NS_OK; 13944 } 13945 13946 // On history navigation through Back/Forward buttons, don't execute 13947 // automatic JavaScript redirection such as |anchorElement.click()| or 13948 // |formElement.submit()|. 13949 // 13950 // XXX |formElement.submit()| bypasses this checkpoint because it calls 13951 // nsDocShell::OnLinkClickSync(...) instead. 13952 if (ShouldBlockLoadingForBackButton()) { 13953 return NS_OK; 13954 } 13955 13956 if (aContent->IsEditable()) { 13957 return NS_OK; 13958 } 13959 13960 Document* ownerDoc = aContent->OwnerDoc(); 13961 if (nsContentUtils::IsExternalProtocol(aURI)) { 13962 ownerDoc->EnsureNotEnteringAndExitFullscreen(); 13963 } 13964 13965 bool noOpenerImplied = false; 13966 nsAutoString target(aTargetSpec); 13967 if (aFileName.IsVoid() && 13968 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) { 13969 target = u"_blank"; 13970 if (!aTargetSpec.Equals(target)) { 13971 noOpenerImplied = true; 13972 } 13973 } 13974 13975 // https://html.spec.whatwg.org/#downloading-hyperlinks 13976 // Step 6, step 6.1, step 6.2 13977 // aFileName not being void implies a download attribute, since we've already 13978 // checked if the attribute is present in `nsContentUtils::TriggerLinkClick` 13979 // and made it void otherwise. 13980 if (!aFileName.IsVoid() && 13981 aUserInvolvement != UserNavigationInvolvement::BrowserUI) { 13982 if (nsCOMPtr<nsPIDOMWindowInner> window = ownerDoc->GetInnerWindow()) { 13983 if (RefPtr<Navigation> navigation = window->Navigation()) { 13984 AutoJSAPI jsapi; 13985 if (jsapi.Init(window)) { 13986 RefPtr element = aContent->AsElement(); 13987 // Step 6.4 13988 bool shouldContinue = navigation->FireDownloadRequestNavigateEvent( 13989 jsapi.cx(), aURI, aUserInvolvement, element, aFileName); 13990 13991 // Step 6.5 13992 if (!shouldContinue) { 13993 return NS_OK; 13994 } 13995 } 13996 } 13997 } 13998 } 13999 14000 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI); 14001 loadState->SetTarget(target); 14002 loadState->SetFileName(aFileName); 14003 loadState->SetPostDataStream(aPostDataStream); 14004 loadState->SetHeadersStream(aHeadersDataStream); 14005 loadState->SetFirstParty(true); 14006 loadState->SetTriggeringPrincipal( 14007 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal()); 14008 loadState->SetPrincipalToInherit(aContent->NodePrincipal()); 14009 loadState->SetPolicyContainer( 14010 aPolicyContainer ? aPolicyContainer : aContent->GetPolicyContainer()); 14011 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput()); 14012 14013 const bool hasValidUserGestureActivation = 14014 ownerDoc->HasValidTransientUserGestureActivation(); 14015 loadState->SetHasValidUserGestureActivation(hasValidUserGestureActivation); 14016 loadState->SetTextDirectiveUserActivation( 14017 ownerDoc->ConsumeTextDirectiveUserActivation() || 14018 hasValidUserGestureActivation); 14019 loadState->SetUserNavigationInvolvement(aUserInvolvement); 14020 loadState->SetTriggeringClassificationFlags( 14021 ownerDoc->GetScriptTrackingFlags()); 14022 loadState->SetHistoryBehavior(NavigationHistoryBehavior::Auto); 14023 14024 nsCOMPtr<nsIRunnable> ev = new OnLinkClickEvent( 14025 this, aContent, loadState, noOpenerImplied, aTriggeringPrincipal); 14026 return Dispatch(ev.forget()); 14027 } 14028 14029 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget, 14030 nsIURI* aLinkURI, nsIContent* aContent, 14031 bool aIsUserTriggered) { 14032 if (aLinkURI->SchemeIs("javascript")) { 14033 return false; 14034 } 14035 14036 // External links from within app tabs should always open in new tabs 14037 // instead of replacing the app tab's page (Bug 575561) 14038 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to 14039 // get either host, just return false to use the original target. 14040 nsAutoCString linkHost; 14041 if (NS_FAILED(aLinkURI->GetHost(linkHost))) { 14042 return false; 14043 } 14044 14045 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows 14046 // privileged code to change the default targeting behaviour. In particular, 14047 // if a user-initiated link click for the (or targetting the) top-level frame 14048 // is detected, we default the target to "_blank" to give it a new 14049 // top-level BrowsingContext. 14050 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered && 14051 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) || 14052 aOriginalTarget == u"_top"_ns)) { 14053 return true; 14054 } 14055 14056 // Don't modify non-default targets. 14057 if (!aOriginalTarget.IsEmpty()) { 14058 return false; 14059 } 14060 14061 // Only check targets that are in extension panels or app tabs. 14062 // (isAppTab will be false for app tab subframes). 14063 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup(); 14064 if (!mmGroup.EqualsLiteral("webext-browsers") && 14065 !mBrowsingContext->IsAppTab()) { 14066 return false; 14067 } 14068 14069 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject(); 14070 if (!docURI) { 14071 return false; 14072 } 14073 14074 nsAutoCString docHost; 14075 if (NS_FAILED(docURI->GetHost(docHost))) { 14076 return false; 14077 } 14078 14079 if (linkHost.Equals(docHost)) { 14080 return false; 14081 } 14082 14083 // Special case: ignore "www" prefix if it is part of host string 14084 return linkHost.Length() < docHost.Length() 14085 ? !docHost.Equals("www."_ns + linkHost) 14086 : !linkHost.Equals("www."_ns + docHost); 14087 } 14088 14089 static bool ElementCanHaveNoopener(nsIContent* aContent) { 14090 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in 14091 // the HTML, XHTML, or SVG namespace. 14092 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area, 14093 nsGkAtoms::form) || 14094 aContent->IsSVGElement(nsGkAtoms::a); 14095 } 14096 14097 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent, 14098 nsDocShellLoadState* aLoadState, 14099 bool aNoOpenerImplied, 14100 nsIPrincipal* aTriggeringPrincipal) { 14101 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) { 14102 return NS_OK; 14103 } 14104 14105 // XXX When the linking node was HTMLFormElement, it is synchronous event. 14106 // That is, the caller of this method is not |OnLinkClickEvent::Run()| 14107 // but |HTMLFormElement::SubmitSubmission(...)|. 14108 if (aContent->IsHTMLElement(nsGkAtoms::form) && 14109 ShouldBlockLoadingForBackButton()) { 14110 return NS_OK; 14111 } 14112 14113 if (aContent->IsEditable()) { 14114 return NS_OK; 14115 } 14116 14117 // if the triggeringPrincipal is not passed explicitly, then we 14118 // fall back to using doc->NodePrincipal() as the triggeringPrincipal. 14119 nsCOMPtr<nsIPrincipal> triggeringPrincipal = 14120 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal(); 14121 14122 { 14123 // defer to an external protocol handler if necessary... 14124 nsCOMPtr<nsIExternalProtocolService> extProtService = 14125 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID); 14126 if (extProtService) { 14127 nsAutoCString scheme; 14128 aLoadState->URI()->GetScheme(scheme); 14129 if (!scheme.IsEmpty()) { 14130 // if the URL scheme does not correspond to an exposed protocol, then 14131 // we need to hand this link click over to the external protocol 14132 // handler. 14133 bool isExposed; 14134 nsresult rv = 14135 extProtService->IsExposedProtocol(scheme.get(), &isExposed); 14136 if (NS_SUCCEEDED(rv) && !isExposed) { 14137 return extProtService->LoadURI( 14138 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext, 14139 /* aTriggeredExternally */ 14140 false, 14141 /* aHasValidUserGestureActivation */ 14142 aContent->OwnerDoc()->HasValidTransientUserGestureActivation(), 14143 /* aNewWindowTarget */ false); 14144 } 14145 } 14146 } 14147 } 14148 uint32_t triggeringSandboxFlags = 0; 14149 uint64_t triggeringWindowId = 0; 14150 bool triggeringStorageAccess = false; 14151 if (mBrowsingContext) { 14152 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags(); 14153 triggeringWindowId = aContent->OwnerDoc()->InnerWindowID(); 14154 triggeringStorageAccess = aContent->OwnerDoc()->UsingStorageAccess(); 14155 } 14156 14157 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE; 14158 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent); 14159 bool triggeringPrincipalIsSystemPrincipal = 14160 aLoadState->TriggeringPrincipal()->IsSystemPrincipal(); 14161 if (elementCanHaveNoopener) { 14162 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement()); 14163 nsAutoString relString; 14164 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString); 14165 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok( 14166 relString); 14167 14168 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank"); 14169 bool explicitOpenerSet = false; 14170 14171 // The opener behaviour follows a hierarchy, such that if a higher 14172 // priority behaviour is specified, it always takes priority. That 14173 // priority is currently: norefrerer > noopener > opener > default 14174 14175 while (tok.hasMoreTokens()) { 14176 const nsAString& token = tok.nextToken(); 14177 if (token.LowerCaseEqualsLiteral("noreferrer")) { 14178 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER | 14179 INTERNAL_LOAD_FLAGS_NO_OPENER; 14180 // noreferrer cannot be overwritten by a 'rel=opener'. 14181 explicitOpenerSet = true; 14182 break; 14183 } 14184 14185 if (token.LowerCaseEqualsLiteral("noopener")) { 14186 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER; 14187 explicitOpenerSet = true; 14188 } 14189 14190 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() && 14191 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) { 14192 explicitOpenerSet = true; 14193 } 14194 } 14195 14196 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() && 14197 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) { 14198 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER; 14199 } 14200 14201 if (aNoOpenerImplied) { 14202 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER; 14203 } 14204 } 14205 14206 // Get the owner document of the link that was clicked, this will be 14207 // the document that the link is in, or the last document that the 14208 // link was in. From that document, we'll get the URI to use as the 14209 // referrer, since the current URI in this docshell may be a 14210 // new document that we're in the process of loading. 14211 RefPtr<Document> referrerDoc = aContent->OwnerDoc(); 14212 14213 // Now check that the referrerDoc's inner window is the current inner 14214 // window for mScriptGlobal. If it's not, then we don't want to 14215 // follow this link. 14216 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow(); 14217 if (!mScriptGlobal || !referrerInner || 14218 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) { 14219 // We're no longer the current inner window 14220 return NS_OK; 14221 } 14222 14223 // referrer could be null here in some odd cases, but that's ok, 14224 // we'll just load the link w/o sending a referrer in those cases. 14225 14226 uint32_t loadType = LOAD_LINK; 14227 if (aLoadState->IsFormSubmission()) { 14228 if (aLoadState->Target().IsEmpty()) { 14229 // We set the right load type here for form submissions with an empty 14230 // target. Form submission with a non-empty target are handled in 14231 // nsDocShell::PerformRetargeting after we've selected the correct target 14232 // BC. 14233 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState); 14234 } 14235 } else { 14236 // Link click can be triggered inside an onload handler, and we don't want 14237 // to add history entry in this case. 14238 bool inOnLoadHandler = false; 14239 GetIsExecutingOnLoadHandler(&inOnLoadHandler); 14240 if (inOnLoadHandler) { 14241 loadType = LOAD_NORMAL_REPLACE; 14242 } 14243 } 14244 14245 nsCOMPtr<nsIReferrerInfo> referrerInfo = 14246 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement()) 14247 : new ReferrerInfo(*referrerDoc); 14248 14249 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags); 14250 aLoadState->SetTriggeringWindowId(triggeringWindowId); 14251 aLoadState->SetTriggeringStorageAccess(triggeringStorageAccess); 14252 aLoadState->SetReferrerInfo(referrerInfo); 14253 aLoadState->SetInternalLoadFlags(flags); 14254 aLoadState->SetLoadType(loadType); 14255 aLoadState->SetSourceBrowsingContext(mBrowsingContext); 14256 14257 nsresult rv = InternalLoad(aLoadState); 14258 14259 if (NS_SUCCEEDED(rv)) { 14260 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(), 14261 referrerInfo); 14262 } 14263 14264 return rv; 14265 } 14266 14267 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI, 14268 const nsAString& aTargetSpec) { 14269 if (aContent->IsEditable()) { 14270 return NS_OK; 14271 } 14272 14273 nsresult rv = NS_ERROR_FAILURE; 14274 14275 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner); 14276 if (!browserChrome) { 14277 return rv; 14278 } 14279 14280 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI); 14281 nsAutoCString spec; 14282 rv = exposableURI->GetDisplaySpec(spec); 14283 NS_ENSURE_SUCCESS(rv, rv); 14284 14285 NS_ConvertUTF8toUTF16 uStr(spec); 14286 14287 // The speculative connect used to go through the predictor, but we don't 14288 // need all that just to initiate a speculative connect. 14289 if ((StaticPrefs::network_predictor_enable_hover_on_ssl() && 14290 mCurrentURI->SchemeIs("https")) || 14291 mCurrentURI->SchemeIs("http")) { 14292 if (nsCOMPtr<nsISpeculativeConnect> specService = 14293 mozilla::components::IO::Service()) { 14294 // This would be a navigation, so if this is cross origin the speculative 14295 // connection needs to have the origin of the URL not the current page. 14296 nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal( 14297 aURI, aContent->NodePrincipal()->OriginAttributesRef()); 14298 14299 specService->SpeculativeConnect(aURI, principal, nullptr, false); 14300 } 14301 } 14302 14303 rv = browserChrome->SetLinkStatus(uStr); 14304 return rv; 14305 } 14306 14307 nsresult nsDocShell::OnLeaveLink() { 14308 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner)); 14309 nsresult rv = NS_ERROR_FAILURE; 14310 14311 if (browserChrome) { 14312 rv = browserChrome->SetLinkStatus(u""_ns); 14313 } 14314 return rv; 14315 } 14316 14317 bool nsDocShell::ShouldBlockLoadingForBackButton() { 14318 if (!(mLoadType & LOAD_CMD_HISTORY) || 14319 UserActivation::IsHandlingUserInput() || 14320 !Preferences::GetBool("accessibility.blockjsredirection")) { 14321 return false; 14322 } 14323 14324 bool canGoForward = false; 14325 GetCanGoForward(&canGoForward); 14326 return canGoForward; 14327 } 14328 14329 //---------------------------------------------------------------------- 14330 // Web Shell Services API 14331 14332 // This functions is only called when a new charset is detected in loading a 14333 // document. 14334 nsresult nsDocShell::CharsetChangeReloadDocument( 14335 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) { 14336 // XXX hack. keep the aCharset and aSource wait to pick it up 14337 nsCOMPtr<nsIDocumentViewer> viewer; 14338 NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer)), NS_ERROR_FAILURE); 14339 if (viewer) { 14340 int32_t source; 14341 (void)viewer->GetReloadEncodingAndSource(&source); 14342 if (aSource > source) { 14343 viewer->SetReloadEncodingAndSource(aEncoding, aSource); 14344 if (eCharsetReloadRequested != mCharsetReloadState) { 14345 mCharsetReloadState = eCharsetReloadRequested; 14346 switch (mLoadType) { 14347 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE: 14348 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE | 14349 LOAD_FLAGS_BYPASS_PROXY); 14350 case LOAD_RELOAD_BYPASS_CACHE: 14351 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE); 14352 default: 14353 return Reload(LOAD_FLAGS_CHARSET_CHANGE); 14354 } 14355 } 14356 } 14357 } 14358 // return failure if this request is not accepted due to mCharsetReloadState 14359 return NS_ERROR_DOCSHELL_REQUEST_REJECTED; 14360 } 14361 14362 nsresult nsDocShell::CharsetChangeStopDocumentLoad() { 14363 if (eCharsetReloadRequested != mCharsetReloadState) { 14364 Stop(nsIWebNavigation::STOP_ALL); 14365 return NS_OK; 14366 } 14367 // return failer if this request is not accepted due to mCharsetReloadState 14368 return NS_ERROR_DOCSHELL_REQUEST_REJECTED; 14369 } 14370 14371 NS_IMETHODIMP nsDocShell::ExitPrintPreview() { 14372 #if NS_PRINT_PREVIEW 14373 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mDocumentViewer); 14374 return viewer->ExitPrintPreview(); 14375 #else 14376 return NS_OK; 14377 #endif 14378 } 14379 14380 /* [infallible] */ 14381 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell( 14382 bool* aIsTopLevelContentDocShell) { 14383 *aIsTopLevelContentDocShell = false; 14384 14385 if (mItemType == typeContent) { 14386 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent(); 14387 } 14388 14389 return NS_OK; 14390 } 14391 14392 // Implements nsILoadContext.originAttributes 14393 NS_IMETHODIMP 14394 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx, 14395 JS::MutableHandle<JS::Value> aVal) { 14396 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal); 14397 } 14398 14399 // Implements nsIDocShell.GetOriginAttributes() 14400 NS_IMETHODIMP 14401 nsDocShell::GetOriginAttributes(JSContext* aCx, 14402 JS::MutableHandle<JS::Value> aVal) { 14403 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal); 14404 } 14405 14406 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal, 14407 nsIURI* aURI) { 14408 MOZ_ASSERT(aPrincipal); 14409 MOZ_ASSERT(aURI); 14410 14411 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) { 14412 return false; 14413 } 14414 14415 nsCOMPtr<nsIDocShellTreeItem> parent; 14416 GetInProcessSameTypeParent(getter_AddRefs(parent)); 14417 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr; 14418 nsPIDOMWindowInner* parentInner = 14419 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr; 14420 14421 StorageAccess storage = 14422 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner); 14423 14424 // If the partitioned service worker is enabled, service worker is allowed to 14425 // control the window if partition is enabled. 14426 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) { 14427 RefPtr<Document> doc = parentInner->GetExtantDoc(); 14428 14429 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) { 14430 return true; 14431 } 14432 } 14433 14434 return storage == StorageAccess::eAllow; 14435 } 14436 14437 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) { 14438 MOZ_ASSERT(!mIsBeingDestroyed); 14439 return mBrowsingContext->SetOriginAttributes(aAttrs); 14440 } 14441 14442 NS_IMETHODIMP 14443 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) { 14444 RefPtr<nsDocShell> self = this; 14445 RefPtr<ChildProcessChannelListener> cpcl = 14446 ChildProcessChannelListener::GetSingleton(); 14447 14448 // Call into InternalLoad with the pending channel when it is received. 14449 cpcl->RegisterCallback( 14450 aIdentifier, [self, aHistoryIndex]( 14451 nsDocShellLoadState* aLoadState, 14452 nsTArray<Endpoint<extensions::PStreamFilterParent>>&& 14453 aStreamFilterEndpoints, 14454 nsDOMNavigationTiming* aTiming) { 14455 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel()); 14456 if (NS_WARN_IF(self->mIsBeingDestroyed)) { 14457 aLoadState->GetPendingRedirectedChannel()->CancelWithReason( 14458 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns); 14459 return NS_BINDING_ABORTED; 14460 } 14461 14462 self->mLoadType = aLoadState->LoadType(); 14463 nsCOMPtr<nsIURI> previousURI; 14464 uint32_t previousFlags = 0; 14465 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(), 14466 getter_AddRefs(previousURI), &previousFlags); 14467 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(), 14468 previousURI, previousFlags); 14469 14470 if (aTiming) { 14471 self->mTiming = new nsDOMNavigationTiming(self, aTiming); 14472 self->mBlankTiming = false; 14473 } 14474 14475 // If we're performing a history load, locate the correct history entry, 14476 // and set the relevant bits on our loadState. 14477 if (aHistoryIndex >= 0 && self->GetSessionHistory() && 14478 !mozilla::SessionHistoryInParent()) { 14479 nsCOMPtr<nsISHistory> legacySHistory = 14480 self->GetSessionHistory()->LegacySHistory(); 14481 14482 nsCOMPtr<nsISHEntry> entry; 14483 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex, 14484 getter_AddRefs(entry)); 14485 if (NS_SUCCEEDED(rv)) { 14486 legacySHistory->InternalSetRequestedIndex(aHistoryIndex); 14487 aLoadState->SetLoadType(LOAD_HISTORY); 14488 aLoadState->SetSHEntry(entry); 14489 } 14490 } 14491 14492 // Prohibit initial about:blank handling e.g. for when a cross-process 14493 // iframe loads about:blank and becomes same-process. Conceptually, the 14494 // browsing context isn't new despite the docshell being newly created. 14495 aLoadState->ProhibitInitialAboutBlankHandling(); 14496 14497 self->InternalLoad(aLoadState); 14498 14499 if (aLoadState->GetOriginalURIString().isSome()) { 14500 // Save URI string in case it's needed later when 14501 // sending to search engine service in EndPageLoad() 14502 self->mOriginalUriString = *aLoadState->GetOriginalURIString(); 14503 } 14504 14505 for (auto& endpoint : aStreamFilterEndpoints) { 14506 extensions::StreamFilterParent::Attach( 14507 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint)); 14508 } 14509 14510 // If the channel isn't pending, then it means that InternalLoad 14511 // never connected it, and we shouldn't try to continue. This 14512 // can happen even if InternalLoad returned NS_OK. 14513 bool pending = false; 14514 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending); 14515 NS_ASSERTION(pending, "We should have connected the pending channel!"); 14516 if (!pending) { 14517 return NS_BINDING_ABORTED; 14518 } 14519 return NS_OK; 14520 }); 14521 return NS_OK; 14522 } 14523 14524 NS_IMETHODIMP 14525 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes, 14526 JSContext* aCx) { 14527 OriginAttributes attrs; 14528 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { 14529 return NS_ERROR_INVALID_ARG; 14530 } 14531 14532 return SetOriginAttributes(attrs); 14533 } 14534 14535 NS_IMETHODIMP 14536 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) { 14537 if (PresShell* presShell = GetPresShell()) { 14538 *aOut = presShell->AsyncPanZoomEnabled(); 14539 return NS_OK; 14540 } 14541 14542 // If we don't have a presShell, fall back to the default platform value of 14543 // whether or not APZ is enabled. 14544 *aOut = gfxPlatform::AsyncPanZoomEnabled(); 14545 return NS_OK; 14546 } 14547 14548 bool nsDocShell::HasUnloadedParent() { 14549 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc; 14550 wc = wc->GetParentWindowContext()) { 14551 if (!wc->IsCurrent() || wc->IsDiscarded() || 14552 wc->GetBrowsingContext()->IsDiscarded()) { 14553 // If a parent is OOP and the parent WindowContext is no 14554 // longer current, we can assume the parent was unloaded. 14555 return true; 14556 } 14557 14558 if (wc->GetBrowsingContext()->IsInProcess() && 14559 (!wc->GetBrowsingContext()->GetDocShell() || 14560 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) { 14561 return true; 14562 } 14563 } 14564 return false; 14565 } 14566 14567 /* static */ 14568 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) { 14569 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE || 14570 aLoadType & LOAD_CMD_HISTORY); 14571 } 14572 14573 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) { 14574 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) { 14575 return; 14576 } 14577 14578 // Global history is interested into sub-frame visits only for link-coloring 14579 // purposes, thus title updates are skipped for those. 14580 // 14581 // Moreover, some iframe documents (such as the ones created via 14582 // document.open()) inherit the document uri of the caller, which would cause 14583 // us to override a previously set page title with one from the subframe. 14584 if (IsSubframe()) { 14585 return; 14586 } 14587 14588 if (nsCOMPtr<IHistory> history = components::History::Service()) { 14589 history->SetURITitle(aURI, mTitle); 14590 } 14591 } 14592 14593 bool nsDocShell::IsInvisible() { return mInvisible; } 14594 14595 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; } 14596 14597 /* static */ 14598 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider, 14599 const nsString& aKeyword) { 14600 if (aProvider.IsEmpty()) { 14601 return; 14602 } 14603 nsresult rv; 14604 nsCOMPtr<nsISupportsString> isupportsString = 14605 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); 14606 NS_ENSURE_SUCCESS_VOID(rv); 14607 14608 rv = isupportsString->SetData(aProvider); 14609 NS_ENSURE_SUCCESS_VOID(rv); 14610 14611 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService(); 14612 if (obsSvc) { 14613 // Note that "keyword-search" refers to a search via the url 14614 // bar, not a bookmarks keyword search. 14615 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get()); 14616 } 14617 } 14618 14619 NS_IMETHODIMP 14620 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel, 14621 bool* aShouldIntercept) { 14622 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel, 14623 aShouldIntercept); 14624 } 14625 14626 NS_IMETHODIMP 14627 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) { 14628 return mInterceptController->ChannelIntercepted(aChannel); 14629 } 14630 14631 bool nsDocShell::InFrameSwap() { 14632 RefPtr<nsDocShell> shell = this; 14633 do { 14634 if (shell->mInFrameSwap) { 14635 return true; 14636 } 14637 shell = shell->GetInProcessParentDocshell(); 14638 } while (shell); 14639 return false; 14640 } 14641 14642 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() { 14643 return std::move(mInitialClientSource); 14644 } 14645 14646 NS_IMETHODIMP 14647 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) { 14648 if (!NS_SUCCEEDED(EnsureEditorData())) { 14649 return NS_ERROR_FAILURE; 14650 } 14651 14652 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take(); 14653 return *aEditSession ? NS_OK : NS_ERROR_FAILURE; 14654 } 14655 14656 NS_IMETHODIMP 14657 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) { 14658 *aBrowserChild = GetBrowserChild().take(); 14659 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE; 14660 } 14661 14662 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() { 14663 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild); 14664 return tc.forget(); 14665 } 14666 14667 nsCommandManager* nsDocShell::GetCommandManager() { 14668 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr); 14669 return mCommandManager; 14670 } 14671 14672 NS_IMETHODIMP_(void) 14673 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) { 14674 mBrowsingContext->GetOriginAttributes(aAttrs); 14675 } 14676 14677 HTMLEditor* nsIDocShell::GetHTMLEditor() { 14678 nsDocShell* docShell = static_cast<nsDocShell*>(this); 14679 return docShell->GetHTMLEditorInternal(); 14680 } 14681 14682 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) { 14683 nsDocShell* docShell = static_cast<nsDocShell*>(this); 14684 return docShell->SetHTMLEditorInternal(aHTMLEditor); 14685 } 14686 14687 #define MATRIX_LENGTH 20 14688 14689 NS_IMETHODIMP 14690 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) { 14691 if (aMatrix.Length() == MATRIX_LENGTH) { 14692 mColorMatrix.reset(new gfx::Matrix5x4()); 14693 static_assert( 14694 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components), 14695 "Size mismatch for our memcpy"); 14696 memcpy(mColorMatrix->components, aMatrix.Elements(), 14697 sizeof(mColorMatrix->components)); 14698 } else if (aMatrix.Length() == 0) { 14699 mColorMatrix.reset(); 14700 } else { 14701 return NS_ERROR_INVALID_ARG; 14702 } 14703 14704 PresShell* presShell = GetPresShell(); 14705 if (!presShell) { 14706 return NS_ERROR_FAILURE; 14707 } 14708 14709 nsIFrame* frame = presShell->GetRootFrame(); 14710 if (!frame) { 14711 return NS_ERROR_FAILURE; 14712 } 14713 14714 frame->SchedulePaint(); 14715 14716 return NS_OK; 14717 } 14718 14719 NS_IMETHODIMP 14720 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) { 14721 if (mColorMatrix) { 14722 aMatrix.SetLength(MATRIX_LENGTH); 14723 static_assert( 14724 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components), 14725 "Size mismatch for our memcpy"); 14726 memcpy(aMatrix.Elements(), mColorMatrix->components, 14727 MATRIX_LENGTH * sizeof(float)); 14728 } 14729 14730 return NS_OK; 14731 } 14732 14733 #undef MATRIX_LENGTH 14734 14735 NS_IMETHODIMP 14736 nsDocShell::GetIsForceReloading(bool* aForceReload) { 14737 *aForceReload = IsForceReloading(); 14738 return NS_OK; 14739 } 14740 14741 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); } 14742 14743 NS_IMETHODIMP 14744 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) { 14745 *aBrowsingContext = do_AddRef(mBrowsingContext).take(); 14746 return NS_OK; 14747 } 14748 14749 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; } 14750 14751 bool nsDocShell::GetIsAttemptingToNavigate() { 14752 // XXXbz the document.open spec says to abort even if there's just a 14753 // queued navigation task, sort of. It's not clear whether browsers 14754 // actually do that, and we didn't use to do it, so for now let's 14755 // not do that. 14756 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this. 14757 if (mDocumentRequest) { 14758 // There's definitely a navigation in progress. 14759 return true; 14760 } 14761 14762 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND 14763 // until the script runs, which means they're not sending loadgroup 14764 // notifications and hence not getting set as mDocumentRequest. Look through 14765 // our loadgroup for document-level javascript: loads. 14766 if (!mLoadGroup) { 14767 return false; 14768 } 14769 14770 nsCOMPtr<nsISimpleEnumerator> requests; 14771 mLoadGroup->GetRequests(getter_AddRefs(requests)); 14772 bool hasMore = false; 14773 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) { 14774 nsCOMPtr<nsISupports> elem; 14775 requests->GetNext(getter_AddRefs(elem)); 14776 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem)); 14777 if (!scriptChannel) { 14778 continue; 14779 } 14780 14781 if (scriptChannel->GetIsDocumentLoad()) { 14782 // This is a javascript: load that might lead to a new document, 14783 // hence a navigation. 14784 return true; 14785 } 14786 } 14787 14788 return mCheckingSessionHistory; 14789 } 14790 14791 mozilla::dom::SessionHistoryInfo* nsDocShell::GetActiveSessionHistoryInfo() 14792 const { 14793 return mActiveEntry.get(); 14794 } 14795 14796 void nsDocShell::SetLoadingSessionHistoryInfo( 14797 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo, 14798 bool aNeedToReportActiveAfterLoadingBecomesActive) { 14799 // FIXME Would like to assert this, but can't yet. 14800 // MOZ_ASSERT(!mLoadingEntry); 14801 MOZ_LOG(gSHLog, LogLevel::Debug, 14802 ("Setting the loading entry on nsDocShell %p to %s", this, 14803 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get())); 14804 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo); 14805 mNeedToReportActiveAfterLoadingBecomesActive = 14806 aNeedToReportActiveAfterLoadingBecomesActive; 14807 } 14808 14809 void nsDocShell::MoveLoadingToActiveEntry(bool aExpired, uint32_t aCacheKey, 14810 nsIURI* aPreviousURI) { 14811 MOZ_ASSERT(mozilla::SessionHistoryInParent()); 14812 14813 MOZ_LOG(gSHLog, LogLevel::Debug, 14814 ("nsDocShell %p MoveLoadingToActiveEntry", this)); 14815 14816 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release()); 14817 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry; 14818 mActiveEntryIsLoadingFromSessionHistory = 14819 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory; 14820 if (mLoadingEntry) { 14821 MOZ_LOG(gSHLog, LogLevel::Debug, 14822 ("Moving the loading entry to the active entry on nsDocShell %p " 14823 "to %s", 14824 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get())); 14825 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo); 14826 mLoadingEntry.swap(loadingEntry); 14827 if (!mActiveEntryIsLoadingFromSessionHistory) { 14828 if (mNeedToReportActiveAfterLoadingBecomesActive) { 14829 // Needed to pass various history length WPTs. 14830 mBrowsingContext->SetActiveSessionHistoryEntry( 14831 mozilla::Nothing(), mActiveEntry.get(), previousActiveEntry.get(), 14832 mLoadType, 14833 /* aUpdatedCacheKey = */ 0, false); 14834 } 14835 if (!(previousActiveEntry && previousActiveEntry->IsTransient())) { 14836 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext(); 14837 } 14838 } 14839 } 14840 mNeedToReportActiveAfterLoadingBecomesActive = false; 14841 14842 if (mActiveEntry) { 14843 if (aCacheKey != 0) { 14844 mActiveEntry->SetCacheKey(aCacheKey); 14845 } 14846 14847 MOZ_ASSERT(loadingEntry); 14848 uint32_t loadType = 14849 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType; 14850 14851 if (loadingEntry->mLoadId != UINT64_MAX) { 14852 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit 14853 // does require a non-null uri if this is for a refresh load of the same 14854 // URI, but in that case mCurrentURI won't be null here. 14855 mBrowsingContext->SessionHistoryCommit( 14856 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(), 14857 false, aExpired, aCacheKey); 14858 } 14859 14860 // Only update navigation if the new entry will be persisted (i.e., is not 14861 // an about: page). 14862 if (!loadingEntry->mInfo.IsTransient() && GetWindow() && 14863 GetWindow()->GetCurrentInnerWindow()) { 14864 if (RefPtr navigation = 14865 GetWindow()->GetCurrentInnerWindow()->Navigation()) { 14866 navigation->InitializeHistoryEntries(loadingEntry->mContiguousEntries, 14867 mActiveEntry.get()); 14868 14869 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, 14870 "Before creating NavigationActivation, " 14871 "triggeringEntry={}, triggeringType={}", 14872 fmt::ptr(loadingEntry->mTriggeringEntry 14873 .map([](auto& entry) { return &entry; }) 14874 .valueOr(nullptr)), 14875 loadingEntry->mTriggeringNavigationType 14876 .map([](NavigationType type) { 14877 return fmt::format(FMT_STRING("{}"), type); 14878 }) 14879 .valueOr("none")); 14880 if (loadingEntry->mTriggeringEntry && 14881 loadingEntry->mTriggeringNavigationType) { 14882 navigation->CreateNavigationActivationFrom( 14883 &*loadingEntry->mTriggeringEntry, 14884 *loadingEntry->mTriggeringNavigationType); 14885 } 14886 } 14887 } 14888 } 14889 } 14890 14891 static bool IsFaviconLoad(nsIRequest* aRequest) { 14892 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); 14893 if (!channel) { 14894 return false; 14895 } 14896 14897 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo(); 14898 return li && li->InternalContentPolicyType() == 14899 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON; 14900 } 14901 14902 void nsDocShell::RecordSingleChannelId(bool aStartRequest, 14903 nsIRequest* aRequest) { 14904 // Ignore favicon loads, they don't need to block caching. 14905 if (IsFaviconLoad(aRequest)) { 14906 return; 14907 } 14908 14909 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0); 14910 14911 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1; 14912 14913 if (mBrowsingContext->GetCurrentWindowContext()) { 14914 // We have three states: no request, one request with an id and 14915 // eiher one request without an id or multiple requests. Nothing() is no 14916 // request, Some(non-zero) is one request with an id and Some(0) is one 14917 // request without an id or multiple requests. 14918 Maybe<uint64_t> singleChannelId; 14919 if (mRequestForBlockingFromBFCacheCount > 1) { 14920 singleChannelId = Some(0); 14921 } else if (mRequestForBlockingFromBFCacheCount == 1) { 14922 nsCOMPtr<nsIIdentChannel> identChannel; 14923 if (aStartRequest) { 14924 identChannel = do_QueryInterface(aRequest); 14925 } else { 14926 // aChannel is the channel that's being removed, but we need to check if 14927 // the remaining channel in the loadgroup has an id. 14928 nsCOMPtr<nsISimpleEnumerator> requests; 14929 mLoadGroup->GetRequests(getter_AddRefs(requests)); 14930 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) { 14931 if (!IsFaviconLoad(request) && 14932 !!(identChannel = do_QueryInterface(request))) { 14933 break; 14934 } 14935 } 14936 } 14937 14938 if (identChannel) { 14939 singleChannelId = Some(identChannel->ChannelId()); 14940 } else { 14941 singleChannelId = Some(0); 14942 } 14943 } else { 14944 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0); 14945 singleChannelId = Nothing(); 14946 } 14947 14948 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) { 14949 nsAutoCString uri("[no uri]"); 14950 if (mCurrentURI) { 14951 uri = mCurrentURI->GetSpecOrDefault(); 14952 } 14953 if (singleChannelId.isNothing()) { 14954 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose, 14955 ("Loadgroup for %s doesn't have any requests relevant for " 14956 "blocking BFCache", 14957 uri.get())); 14958 } else if (singleChannelId.value() == 0) { 14959 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose, 14960 ("Loadgroup for %s has multiple requests relevant for blocking " 14961 "BFCache", 14962 uri.get())); 14963 } else { 14964 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose, 14965 ("Loadgroup for %s has one request with id %" PRIu64 14966 " relevant for blocking BFCache", 14967 uri.get(), singleChannelId.value())); 14968 } 14969 } 14970 14971 if (mSingleChannelId != singleChannelId) { 14972 mSingleChannelId = singleChannelId; 14973 WindowGlobalChild* wgc = 14974 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild(); 14975 if (wgc) { 14976 wgc->SendSetSingleChannelId(singleChannelId); 14977 } 14978 } 14979 } 14980 } 14981 14982 NS_IMETHODIMP 14983 nsDocShell::OnStartRequest(nsIRequest* aRequest) { 14984 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) { 14985 nsAutoCString uri("[no uri]"); 14986 if (mCurrentURI) { 14987 uri = mCurrentURI->GetSpecOrDefault(); 14988 } 14989 nsAutoCString name; 14990 aRequest->GetName(name); 14991 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose, 14992 ("Adding request %s to loadgroup for %s", name.get(), uri.get())); 14993 } 14994 RecordSingleChannelId(true, aRequest); 14995 return nsDocLoader::OnStartRequest(aRequest); 14996 } 14997 14998 NS_IMETHODIMP 14999 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { 15000 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) { 15001 nsAutoCString uri("[no uri]"); 15002 if (mCurrentURI) { 15003 uri = mCurrentURI->GetSpecOrDefault(); 15004 } 15005 nsAutoCString name; 15006 aRequest->GetName(name); 15007 MOZ_LOG( 15008 gSHIPBFCacheLog, LogLevel::Verbose, 15009 ("Removing request %s from loadgroup for %s", name.get(), uri.get())); 15010 } 15011 RecordSingleChannelId(false, aRequest); 15012 return nsDocLoader::OnStopRequest(aRequest, aStatusCode); 15013 } 15014 15015 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() { 15016 MOZ_RELEASE_ASSERT(XRE_IsContentProcess()); 15017 15018 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) { 15019 nsCOMPtr<nsISimpleEnumerator> requests; 15020 mLoadGroup->GetRequests(getter_AddRefs(requests)); 15021 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) { 15022 RefPtr<DocumentChannel> channel = do_QueryObject(request); 15023 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) { 15024 static_cast<DocumentChannelChild*>(channel.get()) 15025 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED); 15026 } 15027 } 15028 mChannelToDisconnectOnPageHide = 0; 15029 } 15030 } 15031 15032 bool nsDocShell::IsSameDocumentAsActiveEntry( 15033 const mozilla::dom::SessionHistoryInfo& aSHInfo) { 15034 return mActiveEntry ? mActiveEntry->SharesDocumentWith(aSHInfo) : false; 15035 } 15036 15037 // https://html.spec.whatwg.org/#nav-window 15038 nsPIDOMWindowInner* nsDocShell::GetActiveWindow() { 15039 nsPIDOMWindowOuter* outer = GetWindow(); 15040 return outer ? outer->GetCurrentInnerWindow() : nullptr; 15041 } 15042 15043 // https://html.spec.whatwg.org/#inform-the-navigation-api-about-aborting-navigation 15044 void nsDocShell::InformNavigationAPIAboutAbortingNavigation() { 15045 // Step 1 15046 // We really have no idea what this means. 15047 15048 // No ongoing navigations if we don't have a window. 15049 RefPtr<nsPIDOMWindowInner> window = GetActiveWindow(); 15050 if (!window) { 15051 return; 15052 } 15053 15054 // Step 2 15055 RefPtr<Navigation> navigation = window->Navigation(); 15056 if (!navigation) { 15057 return; 15058 } 15059 15060 AutoJSAPI jsapi; 15061 if (!jsapi.Init(navigation->GetOwnerGlobal())) { 15062 return; 15063 } 15064 15065 // Steps 3 & 4 15066 // See https://github.com/whatwg/html/issues/11579 15067 navigation->InnerInformAboutAbortingNavigation(jsapi.cx()); 15068 } 15069 15070 // https://html.spec.whatwg.org/#inform-the-navigation-api-about-child-navigable-destruction 15071 void nsDocShell::InformNavigationAPIAboutChildNavigableDestruction() { 15072 // Step 1 15073 InformNavigationAPIAboutAbortingNavigation(); 15074 15075 // No ongoing navigations if we don't have a window. 15076 RefPtr<nsPIDOMWindowInner> window = GetActiveWindow(); 15077 if (!window) { 15078 return; 15079 } 15080 15081 // Step 2 15082 RefPtr<Navigation> navigation = window->Navigation(); 15083 if (!navigation) { 15084 return; 15085 } 15086 15087 AutoJSAPI jsapi; 15088 if (!jsapi.Init(navigation->GetOwnerGlobal())) { 15089 return; 15090 } 15091 15092 navigation->InformAboutChildNavigableDestruction(jsapi.cx()); 15093 } 15094 15095 // https://html.spec.whatwg.org/#set-the-ongoing-navigation 15096 void nsDocShell::SetOngoingNavigation( 15097 const Maybe<OngoingNavigation>& aOngoingNavigation) { 15098 // We currently only use #set-the-ongoing-navigation to call, 15099 // #inform-the-navigation-api-about-aborting-navigation, but really it should 15100 // be used for more. The spec keeps a piece of state on the navigable: 15101 // https://html.spec.whatwg.org/#ongoing-navigation. Spec uses it for several 15102 // things, for example right here in #set-the-ongoing-navigation to make sure 15103 // that we don't call #inform-the-navigation-api-about-aborting-navigation if 15104 // we're setting it to the same value. We currently only care about aborting 15105 // the currently firing navigate event. Also, in reality, this is very much 15106 // related to nsDocShell::GetIsAttemptingToNavigate() which is what we 15107 // currently use to determine if we need to stop an ongoing navigation in 15108 // Document::Open, whereas the spec checks if the ongoing navigation is a 15109 // NavigationID. 15110 15111 // Step 1, with the exception that we assume setting the ongoing navigation to 15112 // an id always means a fresh id. 15113 if (aOngoingNavigation == mOngoingNavigation && 15114 aOngoingNavigation != Some(OngoingNavigation::NavigationID)) { 15115 return; 15116 } 15117 15118 // Step 2 15119 InformNavigationAPIAboutAbortingNavigation(); 15120 15121 // Step 3 15122 mOngoingNavigation = aOngoingNavigation; 15123 }