Bug 1855360 - Fix the skip-if syntax. a=bustage-fix
[gecko.git] / docshell / base / nsDocShell.cpp
blob0b8212fbf3f81ef6264f17fc8e84f91cde39c0f7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDocShell.h"
9 #include <algorithm>
11 #ifdef XP_WIN
12 # include <process.h>
13 # define getpid _getpid
14 #else
15 # include <unistd.h> // for getpid()
16 #endif
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/AutoRestore.h"
21 #include "mozilla/BasePrincipal.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/CheckedInt.h"
24 #include "mozilla/Components.h"
25 #include "mozilla/DebugOnly.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/ResultExtensions.h"
36 #include "mozilla/SchedulerGroup.h"
37 #include "mozilla/ScopeExit.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"
53 #include "mozilla/Unused.h"
54 #include "mozilla/WidgetUtils.h"
56 #include "mozilla/dom/AutoEntryScript.h"
57 #include "mozilla/dom/ChildProcessChannelListener.h"
58 #include "mozilla/dom/ClientChannelHelper.h"
59 #include "mozilla/dom/ClientHandle.h"
60 #include "mozilla/dom/ClientInfo.h"
61 #include "mozilla/dom/ClientManager.h"
62 #include "mozilla/dom/ClientSource.h"
63 #include "mozilla/dom/ContentChild.h"
64 #include "mozilla/dom/ContentFrameMessageManager.h"
65 #include "mozilla/dom/DocGroup.h"
66 #include "mozilla/dom/Element.h"
67 #include "mozilla/dom/HTMLAnchorElement.h"
68 #include "mozilla/dom/HTMLIFrameElement.h"
69 #include "mozilla/dom/PerformanceNavigation.h"
70 #include "mozilla/dom/PermissionMessageUtils.h"
71 #include "mozilla/dom/PopupBlocker.h"
72 #include "mozilla/dom/ScreenOrientation.h"
73 #include "mozilla/dom/ScriptSettings.h"
74 #include "mozilla/dom/ServiceWorkerInterceptController.h"
75 #include "mozilla/dom/ServiceWorkerUtils.h"
76 #include "mozilla/dom/SessionHistoryEntry.h"
77 #include "mozilla/dom/SessionStorageManager.h"
78 #include "mozilla/dom/SessionStoreChangeListener.h"
79 #include "mozilla/dom/SessionStoreChild.h"
80 #include "mozilla/dom/SessionStoreUtils.h"
81 #include "mozilla/dom/BrowserChild.h"
82 #include "mozilla/dom/ToJSValue.h"
83 #include "mozilla/dom/UserActivation.h"
84 #include "mozilla/dom/ChildSHistory.h"
85 #include "mozilla/dom/nsCSPContext.h"
86 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
87 #include "mozilla/dom/LoadURIOptionsBinding.h"
88 #include "mozilla/dom/JSWindowActorChild.h"
89 #include "mozilla/dom/DocumentBinding.h"
90 #include "mozilla/ipc/ProtocolUtils.h"
91 #include "mozilla/net/DocumentChannel.h"
92 #include "mozilla/net/DocumentChannelChild.h"
93 #include "mozilla/net/ParentChannelWrapper.h"
94 #include "mozilla/net/UrlClassifierFeatureFactory.h"
95 #include "ReferrerInfo.h"
97 #include "nsIAuthPrompt.h"
98 #include "nsIAuthPrompt2.h"
99 #include "nsICachingChannel.h"
100 #include "nsICaptivePortalService.h"
101 #include "nsIChannel.h"
102 #include "nsIChannelEventSink.h"
103 #include "nsIClassOfService.h"
104 #include "nsIConsoleReportCollector.h"
105 #include "nsIContent.h"
106 #include "nsIContentInlines.h"
107 #include "nsIContentSecurityPolicy.h"
108 #include "nsIController.h"
109 #include "nsIDocShellTreeItem.h"
110 #include "nsIDocShellTreeOwner.h"
111 #include "nsIDocumentViewer.h"
112 #include "mozilla/dom/Document.h"
113 #include "nsHTMLDocument.h"
114 #include "nsIDocumentLoaderFactory.h"
115 #include "nsIDOMWindow.h"
116 #include "nsIEditingSession.h"
117 #include "nsIEffectiveTLDService.h"
118 #include "nsIExternalProtocolService.h"
119 #include "nsIFormPOSTActionChannel.h"
120 #include "nsIFrame.h"
121 #include "nsIGlobalObject.h"
122 #include "nsIHttpChannel.h"
123 #include "nsIHttpChannelInternal.h"
124 #include "nsIIDNService.h"
125 #include "nsIInputStreamChannel.h"
126 #include "nsIInterfaceRequestorUtils.h"
127 #include "nsILayoutHistoryState.h"
128 #include "nsILoadInfo.h"
129 #include "nsILoadURIDelegate.h"
130 #include "nsIMultiPartChannel.h"
131 #include "nsINestedURI.h"
132 #include "nsINetworkPredictor.h"
133 #include "nsINode.h"
134 #include "nsINSSErrorsService.h"
135 #include "nsIObserverService.h"
136 #include "nsIOService.h"
137 #include "nsIPrincipal.h"
138 #include "nsIPrivacyTransitionObserver.h"
139 #include "nsIPrompt.h"
140 #include "nsIPromptCollection.h"
141 #include "nsIPromptFactory.h"
142 #include "nsIPublicKeyPinningService.h"
143 #include "nsIReflowObserver.h"
144 #include "nsIScriptChannel.h"
145 #include "nsIScriptObjectPrincipal.h"
146 #include "nsIScriptSecurityManager.h"
147 #include "nsIScrollableFrame.h"
148 #include "nsIScrollObserver.h"
149 #include "nsISupportsPrimitives.h"
150 #include "nsISecureBrowserUI.h"
151 #include "nsISeekableStream.h"
152 #include "nsISelectionDisplay.h"
153 #include "nsISHEntry.h"
154 #include "nsISiteSecurityService.h"
155 #include "nsISocketProvider.h"
156 #include "nsIStringBundle.h"
157 #include "nsIStructuredCloneContainer.h"
158 #include "nsIBrowserChild.h"
159 #include "nsITextToSubURI.h"
160 #include "nsITimedChannel.h"
161 #include "nsITimer.h"
162 #include "nsITransportSecurityInfo.h"
163 #include "nsIUploadChannel.h"
164 #include "nsIURIFixup.h"
165 #include "nsIURIMutator.h"
166 #include "nsIURILoader.h"
167 #include "nsIViewSourceChannel.h"
168 #include "nsIWebBrowserChrome.h"
169 #include "nsIWebBrowserChromeFocus.h"
170 #include "nsIWebBrowserFind.h"
171 #include "nsIWebProgress.h"
172 #include "nsIWidget.h"
173 #include "nsIWindowWatcher.h"
174 #include "nsIWritablePropertyBag2.h"
175 #include "nsIX509Cert.h"
176 #include "nsIXULRuntime.h"
178 #include "nsCommandManager.h"
179 #include "nsPIDOMWindow.h"
180 #include "nsPIWindowRoot.h"
182 #include "IHistory.h"
183 #include "IUrlClassifierUITelemetry.h"
185 #include "nsArray.h"
186 #include "nsArrayUtils.h"
187 #include "nsCExternalHandlerService.h"
188 #include "nsContentDLF.h"
189 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
190 #include "nsContentSecurityManager.h"
191 #include "nsContentSecurityUtils.h"
192 #include "nsContentUtils.h"
193 #include "nsCURILoader.h"
194 #include "nsDocShellCID.h"
195 #include "nsDocShellEditorData.h"
196 #include "nsDocShellEnumerator.h"
197 #include "nsDocShellLoadState.h"
198 #include "nsDocShellLoadTypes.h"
199 #include "nsDOMCID.h"
200 #include "nsDOMNavigationTiming.h"
201 #include "nsDSURIContentListener.h"
202 #include "nsEditingSession.h"
203 #include "nsError.h"
204 #include "nsEscape.h"
205 #include "nsFocusManager.h"
206 #include "nsGlobalWindowInner.h"
207 #include "nsGlobalWindowOuter.h"
208 #include "nsJSEnvironment.h"
209 #include "nsNetCID.h"
210 #include "nsNetUtil.h"
211 #include "nsObjectLoadingContent.h"
212 #include "nsPingListener.h"
213 #include "nsPoint.h"
214 #include "nsQueryObject.h"
215 #include "nsQueryActor.h"
216 #include "nsRect.h"
217 #include "nsRefreshTimer.h"
218 #include "nsSandboxFlags.h"
219 #include "nsSHEntry.h"
220 #include "nsSHistory.h"
221 #include "nsSHEntry.h"
222 #include "nsStructuredCloneContainer.h"
223 #include "nsSubDocumentFrame.h"
224 #include "nsURILoader.h"
225 #include "nsURLHelper.h"
226 #include "nsView.h"
227 #include "nsViewManager.h"
228 #include "nsViewSourceHandler.h"
229 #include "nsWebBrowserFind.h"
230 #include "nsWhitespaceTokenizer.h"
231 #include "nsWidgetsCID.h"
232 #include "nsXULAppAPI.h"
234 #include "ThirdPartyUtil.h"
235 #include "GeckoProfiler.h"
236 #include "mozilla/NullPrincipal.h"
237 #include "Navigator.h"
238 #include "prenv.h"
239 #include "mozilla/ipc/URIUtils.h"
240 #include "sslerr.h"
241 #include "mozpkix/pkix.h"
242 #include "NSSErrorsService.h"
244 #include "nsDocShellTelemetryUtils.h"
246 #ifdef MOZ_PLACES
247 # include "nsIFaviconService.h"
248 # include "mozIPlacesPendingOperation.h"
249 #endif
251 #if NS_PRINT_PREVIEW
252 # include "nsIDocumentViewerPrint.h"
253 # include "nsIWebBrowserPrint.h"
254 #endif
256 using namespace mozilla;
257 using namespace mozilla::dom;
258 using namespace mozilla::net;
260 using mozilla::ipc::Endpoint;
262 // Threshold value in ms for META refresh based redirects
263 #define REFRESH_REDIRECT_TIMER 15000
265 static mozilla::LazyLogModule gCharsetMenuLog("CharsetMenu");
267 #define LOGCHARSETMENU(args) \
268 MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args)
270 #ifdef DEBUG
271 unsigned long nsDocShell::gNumberOfDocShells = 0;
272 static uint64_t gDocshellIDCounter = 0;
274 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
275 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
276 "DocShellAndDOMWindowLeak");
277 #endif
278 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
279 extern mozilla::LazyLogModule gPageCacheLog;
280 mozilla::LazyLogModule gSHLog("SessionHistory");
281 extern mozilla::LazyLogModule gSHIPBFCacheLog;
283 const char kAppstringsBundleURL[] =
284 "chrome://global/locale/appstrings.properties";
286 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext,
287 nsILoadInfo* aLoadInfo) {
288 MOZ_ASSERT(aBrowsingContext);
289 MOZ_ASSERT(aLoadInfo);
291 if (aLoadInfo->GetExternalContentPolicyType() !=
292 ExtContentPolicy::TYPE_DOCUMENT) {
293 return false;
296 return aBrowsingContext->IsTopContent();
299 // True if loading for top level document loading in active tab.
300 static bool IsUrgentStart(BrowsingContext* aBrowsingContext,
301 nsILoadInfo* aLoadInfo, uint32_t aLoadType) {
302 MOZ_ASSERT(aBrowsingContext);
303 MOZ_ASSERT(aLoadInfo);
305 if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) {
306 return false;
309 if (aLoadType &
310 (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) {
311 return true;
314 return aBrowsingContext->IsActive();
317 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
318 uint64_t aContentWindowID)
319 : nsDocLoader(true),
320 mContentWindowID(aContentWindowID),
321 mBrowsingContext(aBrowsingContext),
322 mParentCharset(nullptr),
323 mTreeOwner(nullptr),
324 mScrollbarPref(ScrollbarPreference::Auto),
325 mCharsetReloadState(eCharsetReloadInit),
326 mParentCharsetSource(0),
327 mFrameMargins(-1, -1),
328 mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome),
329 mPreviousEntryIndex(-1),
330 mLoadedEntryIndex(-1),
331 mBusyFlags(BUSY_FLAGS_NONE),
332 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
333 mLoadType(0),
334 mFailedLoadType(0),
335 mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
336 mChannelToDisconnectOnPageHide(0),
337 mCreatingDocument(false),
338 #ifdef DEBUG
339 mInEnsureScriptEnv(false),
340 #endif
341 mInitialized(false),
342 mAllowSubframes(true),
343 mAllowMetaRedirects(true),
344 mAllowImages(true),
345 mAllowMedia(true),
346 mAllowDNSPrefetch(true),
347 mAllowWindowControl(true),
348 mCSSErrorReportingEnabled(false),
349 mAllowAuth(mItemType == typeContent),
350 mAllowKeywordFixup(false),
351 mDisableMetaRefreshWhenInactive(false),
352 mWindowDraggingAllowed(false),
353 mInFrameSwap(false),
354 mFiredUnloadEvent(false),
355 mEODForCurrentDocument(false),
356 mURIResultedInDocument(false),
357 mIsBeingDestroyed(false),
358 mIsExecutingOnLoadHandler(false),
359 mSavingOldViewer(false),
360 mInvisible(false),
361 mHasLoadedNonBlankURI(false),
362 mBlankTiming(false),
363 mTitleValidForCurrentURI(false),
364 mWillChangeProcess(false),
365 mIsNavigating(false),
366 mForcedAutodetection(false),
367 mCheckingSessionHistory(false),
368 mNeedToReportActiveAfterLoadingBecomesActive(false) {
369 // If no outer window ID was provided, generate a new one.
370 if (aContentWindowID == 0) {
371 mContentWindowID = nsContentUtils::GenerateWindowId();
374 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
376 #ifdef DEBUG
377 mDocShellID = gDocshellIDCounter++;
378 // We're counting the number of |nsDocShells| to help find leaks
379 ++gNumberOfDocShells;
380 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
381 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this,
382 gNumberOfDocShells, getpid(), mDocShellID));
383 #endif
386 nsDocShell::~nsDocShell() {
387 // Avoid notifying observers while we're in the dtor.
388 mIsBeingDestroyed = true;
390 Destroy();
392 if (mDocumentViewer) {
393 mDocumentViewer->Close(nullptr);
394 mDocumentViewer->Destroy();
395 mDocumentViewer = nullptr;
398 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
400 #ifdef DEBUG
401 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
402 nsAutoCString url;
403 if (mLastOpenedURI) {
404 url = mLastOpenedURI->GetSpecOrDefault();
406 // Data URLs can be very long, so truncate to avoid flooding the log.
407 const uint32_t maxURLLength = 1000;
408 if (url.Length() > maxURLLength) {
409 url.Truncate(maxURLLength);
413 // We're counting the number of |nsDocShells| to help find leaks
414 --gNumberOfDocShells;
415 MOZ_LOG(
416 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
417 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n",
418 (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get()));
420 #endif
423 bool nsDocShell::Initialize() {
424 if (mInitialized) {
425 // We've already been initialized.
426 return true;
429 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
430 "Unexpected item type in docshell");
432 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
433 mInitialized = true;
435 mDisableMetaRefreshWhenInactive =
436 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
437 mDisableMetaRefreshWhenInactive);
439 if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) {
440 const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
441 : NS_CHROME_WEBNAVIGATION_CREATE;
442 serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr);
445 return true;
448 /* static */
449 already_AddRefed<nsDocShell> nsDocShell::Create(
450 BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
451 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
453 nsresult rv;
454 RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
456 // Initialize the underlying nsDocLoader.
457 rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
458 if (NS_WARN_IF(NS_FAILED(rv))) {
459 return nullptr;
462 // Create our ContentListener
463 ds->mContentListener = new nsDSURIContentListener(ds);
465 // We enable if we're in the parent process in order to support non-e10s
466 // configurations.
467 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
468 // constructor.
469 if (XRE_IsParentProcess()) {
470 ds->mInterceptController = new ServiceWorkerInterceptController();
473 // We want to hold a strong ref to the loadgroup, so it better hold a weak
474 // ref to us... use an InterfaceRequestorProxy to do this.
475 nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
476 ds->mLoadGroup->SetNotificationCallbacks(proxy);
478 // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
479 // It could be nice to directly set up our DocLoader tree?
480 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
481 if (NS_WARN_IF(NS_FAILED(rv))) {
482 return nullptr;
485 // Add |ds| as a progress listener to itself. A little weird, but simpler
486 // than reproducing all the listener-notification logic in overrides of the
487 // various methods via which nsDocLoader can be notified. Note that this
488 // holds an nsWeakPtr to |ds|, so it's ok.
489 rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
490 nsIWebProgress::NOTIFY_STATE_NETWORK |
491 nsIWebProgress::NOTIFY_LOCATION);
492 if (NS_WARN_IF(NS_FAILED(rv))) {
493 return nullptr;
496 // If our BrowsingContext has private browsing enabled, update the number of
497 // private browsing docshells.
498 if (aBrowsingContext->UsePrivateBrowsing()) {
499 ds->NotifyPrivateBrowsingChanged();
502 // If our parent window is present in this process, set up our parent now.
503 RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext();
504 if (parentWC && parentWC->IsInProcess()) {
505 // If we don't have a parent element anymore, we can't finish this load!
506 // How'd we get here?
507 RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement();
508 if (!parentElement) {
509 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
510 return nullptr;
513 // We have an in-process parent window, but don't have a parent nsDocShell?
514 // How'd we get here!
515 nsCOMPtr<nsIDocShell> parentShell =
516 parentElement->OwnerDoc()->GetDocShell();
517 if (!parentShell) {
518 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
519 return nullptr;
521 parentShell->AddChild(ds);
524 // Make |ds| the primary DocShell for the given context.
525 aBrowsingContext->SetDocShell(ds);
527 // Set |ds| default load flags on load group.
528 ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
530 if (XRE_IsParentProcess()) {
531 aBrowsingContext->Canonical()->MaybeAddAsProgressListener(ds);
534 return ds.forget();
537 void nsDocShell::DestroyChildren() {
538 for (auto* child : mChildList.ForwardRange()) {
539 nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child);
540 NS_ASSERTION(shell, "docshell has null child");
542 if (shell) {
543 shell->SetTreeOwner(nullptr);
547 nsDocLoader::DestroyChildren();
550 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
551 mScriptGlobal, mInitialClientSource,
552 mBrowsingContext,
553 mChromeEventHandler)
555 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
556 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
558 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
559 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
560 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
561 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
562 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
563 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
564 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
565 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
566 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
567 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
568 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
569 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
570 mInterceptController)
571 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
573 NS_IMETHODIMP
574 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
575 MOZ_ASSERT(aSink, "null out param");
577 *aSink = nullptr;
579 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
580 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
581 *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
582 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
583 *aSink = mContentListener;
584 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
585 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
586 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
587 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
588 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
589 NS_SUCCEEDED(EnsureScriptEnvironment())) {
590 return mScriptGlobal->QueryInterface(aIID, aSink);
591 } else if (aIID.Equals(NS_GET_IID(Document)) &&
592 NS_SUCCEEDED(EnsureDocumentViewer())) {
593 RefPtr<Document> doc = mDocumentViewer->GetDocument();
594 doc.forget(aSink);
595 return *aSink ? NS_OK : NS_NOINTERFACE;
596 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
597 NS_SUCCEEDED(EnsureScriptEnvironment())) {
598 nsresult rv;
599 nsCOMPtr<nsIWindowWatcher> wwatch =
600 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
601 NS_ENSURE_SUCCESS(rv, rv);
603 // Get the an auth prompter for our window so that the parenting
604 // of the dialogs works as it should when using tabs.
605 nsIPrompt* prompt;
606 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
607 NS_ENSURE_SUCCESS(rv, rv);
609 *aSink = prompt;
610 return NS_OK;
611 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
612 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
613 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
614 ? NS_OK
615 : NS_NOINTERFACE;
616 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
617 // This is deprecated, you should instead directly get
618 // ChildSHistory from the browsing context.
619 MOZ_DIAGNOSTIC_ASSERT(
620 false, "Do not try to get a nsISHistory interface from nsIDocShell");
621 return NS_NOINTERFACE;
622 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
623 nsresult rv = EnsureFind();
624 if (NS_FAILED(rv)) {
625 return rv;
628 *aSink = mFind;
629 NS_ADDREF((nsISupports*)*aSink);
630 return NS_OK;
631 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
632 if (PresShell* presShell = GetPresShell()) {
633 return presShell->QueryInterface(aIID, aSink);
635 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
636 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
637 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
638 if (NS_SUCCEEDED(rv) && treeOwner) {
639 return treeOwner->QueryInterface(aIID, aSink);
641 } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
642 *aSink = GetBrowserChild().take();
643 return *aSink ? NS_OK : NS_ERROR_FAILURE;
644 } else {
645 return nsDocLoader::GetInterface(aIID, aSink);
648 NS_IF_ADDREF(((nsISupports*)*aSink));
649 return *aSink ? NS_OK : NS_NOINTERFACE;
652 NS_IMETHODIMP
653 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
654 // Note: this gets called fairly early (before a pageload actually starts).
655 // We could probably defer this even longer.
656 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
657 static_cast<BrowserChild*>(browserChild.get())
658 ->SetCancelContentJSEpoch(aEpoch);
659 return NS_OK;
662 nsresult nsDocShell::CheckDisallowedJavascriptLoad(
663 nsDocShellLoadState* aLoadState) {
664 if (!net::SchemeIsJavascript(aLoadState->URI())) {
665 return NS_OK;
668 if (nsCOMPtr<nsIPrincipal> targetPrincipal =
669 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
670 if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) {
671 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
673 return NS_OK;
675 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
678 NS_IMETHODIMP
679 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
680 return LoadURI(aLoadState, aSetNavigating, false);
683 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
684 bool aSetNavigating,
685 bool aContinueHandlingSubframeHistory) {
686 MOZ_ASSERT(aLoadState, "Must have a valid load state!");
687 // NOTE: This comparison between what appears to be internal/external load
688 // flags is intentional, as it's ensuring that the caller isn't using any of
689 // the flags reserved for implementations by the `nsIWebNavigation` interface.
690 // In the future, this check may be dropped.
691 MOZ_ASSERT(
692 (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
693 "Should not have these flags set");
694 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
695 "Targeting doesn't occur until InternalLoad");
697 if (!aLoadState->TriggeringPrincipal()) {
698 MOZ_ASSERT(false, "LoadURI must have a triggering principal");
699 return NS_ERROR_FAILURE;
702 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
704 bool oldIsNavigating = mIsNavigating;
705 auto cleanupIsNavigating =
706 MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
707 if (aSetNavigating) {
708 mIsNavigating = true;
711 PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden;
712 if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) {
713 popupState = PopupBlocker::openAllowed;
714 // If we allow popups as part of the navigation, ensure we fake a user
715 // interaction, so that popups can, in fact, be allowed to open.
716 if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) {
717 wc->NotifyUserGestureActivation();
721 AutoPopupStatePusher statePusher(popupState);
723 if (aLoadState->GetCancelContentJSEpoch().isSome()) {
724 SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
727 // Note: we allow loads to get through here even if mFiredUnloadEvent is
728 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
729 // so we pass false as the second parameter to IsNavigationAllowed.
730 // However, we don't allow the page to change location *in the middle of*
731 // firing beforeunload, so we do need to check if *beforeunload* is currently
732 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
733 if (!IsNavigationAllowed(true, false)) {
734 return NS_OK; // JS may not handle returning of an error code
737 nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
738 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) {
739 defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
740 } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) {
741 defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
744 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags));
746 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
747 mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
748 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
751 // LoadType used to be set to a default value here, if no LoadInfo/LoadState
752 // object was passed in. That functionality has been removed as of bug
753 // 1492648. LoadType should now be set up by the caller at the time they
754 // create their nsDocShellLoadState object to pass into LoadURI.
756 MOZ_LOG(
757 gDocShellLeakLog, LogLevel::Debug,
758 ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
759 aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
761 if ((!aLoadState->LoadIsFromSessionHistory() &&
762 !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
763 LOAD_FLAGS_REPLACE_HISTORY)) ||
764 aContinueHandlingSubframeHistory) {
765 // This is possibly a subframe, so handle it accordingly.
767 // If history exists, it will be loaded into the aLoadState object, and the
768 // LoadType will be changed.
769 if (MaybeHandleSubframeHistory(aLoadState,
770 aContinueHandlingSubframeHistory)) {
771 // MaybeHandleSubframeHistory returns true if we need to continue loading
772 // asynchronously.
773 return NS_OK;
777 if (aLoadState->LoadIsFromSessionHistory()) {
778 MOZ_LOG(gSHLog, LogLevel::Debug,
779 ("nsDocShell[%p]: loading from session history", this));
781 if (!mozilla::SessionHistoryInParent()) {
782 nsCOMPtr<nsISHEntry> entry = aLoadState->SHEntry();
783 return LoadHistoryEntry(entry, aLoadState->LoadType(),
784 aLoadState->HasValidUserGestureActivation());
787 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
788 return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(),
789 aLoadState->LoadType(),
790 aLoadState->HasValidUserGestureActivation());
793 // On history navigation via Back/Forward buttons, don't execute
794 // automatic JavaScript redirection such as |location.href = ...| or
795 // |window.open()|
797 // LOAD_NORMAL: window.open(...) etc.
798 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
799 if ((aLoadState->LoadType() == LOAD_NORMAL ||
800 aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
801 ShouldBlockLoadingForBackButton()) {
802 return NS_OK;
805 BrowsingContext::Type bcType = mBrowsingContext->GetType();
807 // Set up the inheriting principal in LoadState.
808 nsresult rv = aLoadState->SetupInheritingPrincipal(
809 bcType, mBrowsingContext->OriginAttributesRef());
810 NS_ENSURE_SUCCESS(rv, rv);
812 rv = aLoadState->SetupTriggeringPrincipal(
813 mBrowsingContext->OriginAttributesRef());
814 NS_ENSURE_SUCCESS(rv, rv);
816 aLoadState->CalculateLoadURIFlags();
818 MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
819 "Typehint should be null when calling InternalLoad from LoadURI");
820 MOZ_ASSERT(aLoadState->FileName().IsVoid(),
821 "FileName should be null when calling InternalLoad from LoadURI");
822 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(),
823 "Shouldn't be loading from an entry when calling InternalLoad "
824 "from LoadURI");
826 // If we have a system triggering principal, we can assume that this load was
827 // triggered by some UI in the browser chrome, such as the URL bar or
828 // bookmark bar. This should count as a user interaction for the current sh
829 // entry, so that the user may navigate back to the current entry, from the
830 // entry that is going to be added as part of this load.
831 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
832 aLoadState->TriggeringPrincipal();
833 if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) {
834 if (mozilla::SessionHistoryInParent()) {
835 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
836 if (topWc && !topWc->IsDiscarded()) {
837 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
839 } else {
840 bool oshe = false;
841 nsCOMPtr<nsISHEntry> currentSHEntry;
842 GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe);
843 if (currentSHEntry) {
844 currentSHEntry->SetHasUserInteraction(true);
849 rv = InternalLoad(aLoadState);
850 NS_ENSURE_SUCCESS(rv, rv);
852 if (aLoadState->GetOriginalURIString().isSome()) {
853 // Save URI string in case it's needed later when
854 // sending to search engine service in EndPageLoad()
855 mOriginalUriString = *aLoadState->GetOriginalURIString();
858 return NS_OK;
861 bool nsDocShell::IsLoadingFromSessionHistory() {
862 return mActiveEntryIsLoadingFromSessionHistory;
865 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
866 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
867 // detect Cancel calls.
868 class StopDetector final : public nsIRequest {
869 public:
870 StopDetector() = default;
872 NS_DECL_ISUPPORTS
873 NS_DECL_NSIREQUEST
875 bool Canceled() { return mCanceled; }
877 private:
878 ~StopDetector() = default;
880 bool mCanceled = false;
883 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
885 NS_IMETHODIMP
886 StopDetector::GetName(nsACString& aResult) {
887 aResult.AssignLiteral("about:stop-detector");
888 return NS_OK;
891 NS_IMETHODIMP
892 StopDetector::IsPending(bool* aRetVal) {
893 *aRetVal = true;
894 return NS_OK;
897 NS_IMETHODIMP
898 StopDetector::GetStatus(nsresult* aStatus) {
899 *aStatus = NS_OK;
900 return NS_OK;
903 NS_IMETHODIMP StopDetector::SetCanceledReason(const nsACString& aReason) {
904 return SetCanceledReasonImpl(aReason);
907 NS_IMETHODIMP StopDetector::GetCanceledReason(nsACString& aReason) {
908 return GetCanceledReasonImpl(aReason);
911 NS_IMETHODIMP StopDetector::CancelWithReason(nsresult aStatus,
912 const nsACString& aReason) {
913 return CancelWithReasonImpl(aStatus, aReason);
916 NS_IMETHODIMP
917 StopDetector::Cancel(nsresult aStatus) {
918 mCanceled = true;
919 return NS_OK;
922 NS_IMETHODIMP
923 StopDetector::Suspend(void) { return NS_OK; }
924 NS_IMETHODIMP
925 StopDetector::Resume(void) { return NS_OK; }
927 NS_IMETHODIMP
928 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
929 *aLoadGroup = nullptr;
930 return NS_OK;
933 NS_IMETHODIMP
934 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
936 NS_IMETHODIMP
937 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
938 *aLoadFlags = nsIRequest::LOAD_NORMAL;
939 return NS_OK;
942 NS_IMETHODIMP
943 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
944 return GetTRRModeImpl(aTRRMode);
947 NS_IMETHODIMP
948 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
949 return SetTRRModeImpl(aTRRMode);
952 NS_IMETHODIMP
953 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
955 bool nsDocShell::MaybeHandleSubframeHistory(
956 nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
957 // First, verify if this is a subframe.
958 // Note, it is ok to rely on docshell here and not browsing context since when
959 // an iframe is created, it has first in-process docshell.
960 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
961 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
962 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
964 if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
965 if (mBrowsingContext && mBrowsingContext->IsTop()) {
966 // This is the root docshell. If we got here while
967 // executing an onLoad Handler,this load will not go
968 // into session history.
969 // XXX Why is this code in a method which deals with iframes!
970 if (aLoadState->IsFormSubmission()) {
971 #ifdef DEBUG
972 if (!mEODForCurrentDocument) {
973 const MaybeDiscarded<BrowsingContext>& targetBC =
974 aLoadState->TargetBrowsingContext();
975 MOZ_ASSERT_IF(GetBrowsingContext() == targetBC.get(),
976 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
978 #endif
979 } else {
980 bool inOnLoadHandler = false;
981 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
982 if (inOnLoadHandler) {
983 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
987 return false;
990 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
991 * loaded through a history mechanism, then get the SH entry for the child
992 * from the parent. This is done to restore frameset navigation while going
993 * back/forward. If the parent was loaded through any other loadType, set the
994 * child's loadType too accordingly, so that session history does not get
995 * confused.
998 // Get the parent's load type
999 uint32_t parentLoadType;
1000 parentDS->GetLoadType(&parentLoadType);
1002 if (!aContinueHandlingSubframeHistory) {
1003 if (mozilla::SessionHistoryInParent()) {
1004 if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
1005 !GetCreatedDynamically()) {
1006 if (XRE_IsContentProcess()) {
1007 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
1008 nsCOMPtr<nsILoadGroup> loadGroup;
1009 GetLoadGroup(getter_AddRefs(loadGroup));
1010 if (contentChild && loadGroup && !mCheckingSessionHistory) {
1011 RefPtr<Document> parentDoc = parentDS->GetDocument();
1012 parentDoc->BlockOnload();
1013 RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
1014 Maybe<uint64_t> currentLoadIdentifier =
1015 mBrowsingContext->GetCurrentLoadIdentifier();
1016 RefPtr<nsDocShellLoadState> loadState = aLoadState;
1017 bool isNavigating = mIsNavigating;
1018 RefPtr<StopDetector> stopDetector = new StopDetector();
1019 loadGroup->AddRequest(stopDetector, nullptr);
1020 // Need to set mCheckingSessionHistory so that
1021 // GetIsAttemptingToNavigate() returns true.
1022 mCheckingSessionHistory = true;
1024 auto resolve =
1025 [currentLoadIdentifier, browsingContext, parentDoc, loadState,
1026 isNavigating, loadGroup, stopDetector](
1027 mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
1028 RefPtr<nsDocShell> docShell =
1029 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1030 auto unblockParent = MakeScopeExit(
1031 [loadGroup, stopDetector, parentDoc, docShell]() {
1032 if (docShell) {
1033 docShell->mCheckingSessionHistory = false;
1035 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1036 parentDoc->UnblockOnload(false);
1039 if (!docShell || !docShell->mCheckingSessionHistory) {
1040 return;
1043 if (stopDetector->Canceled()) {
1044 return;
1046 if (currentLoadIdentifier ==
1047 browsingContext->GetCurrentLoadIdentifier() &&
1048 aResult.isSome()) {
1049 loadState->SetLoadingSessionHistoryInfo(aResult.value());
1050 // This is an initial subframe load from the session
1051 // history, index doesn't need to be updated.
1052 loadState->SetLoadIsFromSessionHistory(0, false);
1055 // We got the results back from the parent process, call
1056 // LoadURI again with the possibly updated data.
1057 docShell->LoadURI(loadState, isNavigating, true);
1059 auto reject = [loadGroup, stopDetector, browsingContext,
1060 parentDoc](mozilla::ipc::ResponseRejectReason) {
1061 RefPtr<nsDocShell> docShell =
1062 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1063 if (docShell) {
1064 docShell->mCheckingSessionHistory = false;
1066 // In practise reject shouldn't be called ever.
1067 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1068 parentDoc->UnblockOnload(false);
1070 contentChild->SendGetLoadingSessionHistoryInfoFromParent(
1071 mBrowsingContext, std::move(resolve), std::move(reject));
1072 return true;
1074 } else {
1075 Maybe<LoadingSessionHistoryInfo> info;
1076 mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
1077 info);
1078 if (info.isSome()) {
1079 aLoadState->SetLoadingSessionHistoryInfo(info.value());
1080 // This is an initial subframe load from the session
1081 // history, index doesn't need to be updated.
1082 aLoadState->SetLoadIsFromSessionHistory(0, false);
1086 } else {
1087 // Get the ShEntry for the child from the parent
1088 nsCOMPtr<nsISHEntry> currentSH;
1089 bool oshe = false;
1090 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1091 bool dynamicallyAddedChild = GetCreatedDynamically();
1093 if (!dynamicallyAddedChild && !oshe && currentSH) {
1094 // Only use the old SHEntry, if we're sure enough that
1095 // it wasn't originally for some other frame.
1096 nsCOMPtr<nsISHEntry> shEntry;
1097 currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
1098 mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry));
1099 if (shEntry) {
1100 aLoadState->SetSHEntry(shEntry);
1106 // Make some decisions on the child frame's loadType based on the
1107 // parent's loadType, if the subframe hasn't loaded anything into it.
1109 // In some cases privileged scripts may try to get the DOMWindow
1110 // reference of this docshell before the loading starts, causing the
1111 // initial about:blank content viewer being created and mCurrentURI being
1112 // set. To handle this case we check if mCurrentURI is about:blank and
1113 // currentSHEntry is null.
1114 bool oshe = false;
1115 nsCOMPtr<nsISHEntry> currentChildEntry;
1116 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
1118 if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry ||
1119 mLoadingEntry || mActiveEntry)) {
1120 // This is a pre-existing subframe. If
1121 // 1. The load of this frame was not originally initiated by session
1122 // history directly (i.e. (!shEntry) condition succeeded, but it can
1123 // still be a history load on parent which causes this frame being
1124 // loaded), which we checked with the above assert, and
1125 // 2. mCurrentURI is not null, nor the initial about:blank,
1126 // it is possible that a parent's onLoadHandler or even self's
1127 // onLoadHandler is loading a new page in this child. Check parent's and
1128 // self's busy flag and if it is set, we don't want this onLoadHandler
1129 // load to get in to session history.
1130 BusyFlags parentBusy = parentDS->GetBusyFlags();
1131 BusyFlags selfBusy = GetBusyFlags();
1133 if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
1134 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1135 aLoadState->ClearLoadIsFromSessionHistory();
1137 return false;
1140 // This is a newly created frame. Check for exception cases first.
1141 // By default the subframe will inherit the parent's loadType.
1142 if (aLoadState->LoadIsFromSessionHistory() &&
1143 (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) {
1144 // The parent was loaded normally. In this case, this *brand new*
1145 // child really shouldn't have a SHEntry. If it does, it could be
1146 // because the parent is replacing an existing frame with a new frame,
1147 // in the onLoadHandler. We don't want this url to get into session
1148 // history. Clear off shEntry, and set load type to
1149 // LOAD_BYPASS_HISTORY.
1150 bool inOnLoadHandler = false;
1151 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1152 if (inOnLoadHandler) {
1153 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1154 aLoadState->ClearLoadIsFromSessionHistory();
1156 } else if (parentLoadType == LOAD_REFRESH) {
1157 // Clear shEntry. For refresh loads, we have to load
1158 // what comes through the pipe, not what's in history.
1159 aLoadState->ClearLoadIsFromSessionHistory();
1160 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1161 (aLoadState->LoadIsFromSessionHistory() &&
1162 ((parentLoadType & LOAD_CMD_HISTORY) ||
1163 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1164 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
1165 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
1166 (parentLoadType ==
1167 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
1168 // If the parent url, bypassed history or was loaded from
1169 // history, pass on the parent's loadType to the new child
1170 // frame too, so that the child frame will also
1171 // avoid getting into history.
1172 aLoadState->SetLoadType(parentLoadType);
1173 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1174 // If the parent document is an error page, we don't
1175 // want to update global/session history. However,
1176 // this child frame is not an error page.
1177 aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
1178 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1179 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1180 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1181 // the new frame should inherit the parent's load type so that it also
1182 // bypasses the cache and/or proxy
1183 aLoadState->SetLoadType(parentLoadType);
1186 return false;
1190 * Reset state to a new content model within the current document and the
1191 * document viewer. Called by the document before initiating an out of band
1192 * document.write().
1194 NS_IMETHODIMP
1195 nsDocShell::PrepareForNewContentModel() {
1196 // Clear out our form control state, because the state of controls
1197 // in the pre-open() document should not affect the state of
1198 // controls that are now going to be written.
1199 SetLayoutHistoryState(nullptr);
1200 mEODForCurrentDocument = false;
1201 return NS_OK;
1204 NS_IMETHODIMP
1205 nsDocShell::FirePageHideNotification(bool aIsUnload) {
1206 FirePageHideNotificationInternal(aIsUnload, false);
1207 return NS_OK;
1210 void nsDocShell::FirePageHideNotificationInternal(
1211 bool aIsUnload, bool aSkipCheckingDynEntries) {
1212 if (mDocumentViewer && !mFiredUnloadEvent) {
1213 // Keep an explicit reference since calling PageHide could release
1214 // mDocumentViewer
1215 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
1216 mFiredUnloadEvent = true;
1218 if (mTiming) {
1219 mTiming->NotifyUnloadEventStart();
1222 viewer->PageHide(aIsUnload);
1224 if (mTiming) {
1225 mTiming->NotifyUnloadEventEnd();
1228 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1229 uint32_t n = mChildList.Length();
1230 kids.SetCapacity(n);
1231 for (uint32_t i = 0; i < n; i++) {
1232 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1235 n = kids.Length();
1236 for (uint32_t i = 0; i < n; ++i) {
1237 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1238 if (child) {
1239 // Skip checking dynamic subframe entries in our children.
1240 child->FirePageHideNotificationInternal(aIsUnload, true);
1244 // If the document is unloading, remove all dynamic subframe entries.
1245 if (aIsUnload && !aSkipCheckingDynEntries) {
1246 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
1247 if (rootSH) {
1248 MOZ_LOG(
1249 gSHLog, LogLevel::Debug,
1250 ("nsDocShell %p unloading, remove dynamic subframe entries", this));
1251 if (mozilla::SessionHistoryInParent()) {
1252 if (mActiveEntry) {
1253 mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry();
1255 MOZ_LOG(gSHLog, LogLevel::Debug,
1256 ("nsDocShell %p unloading, no active entries", this));
1257 } else if (mOSHE) {
1258 int32_t index = rootSH->Index();
1259 rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
1264 // Now make sure our editor, if any, is detached before we go
1265 // any farther.
1266 DetachEditorFromWindow();
1270 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) {
1271 MOZ_ASSERT(mozilla::BFCacheInParent());
1273 if (!mScriptGlobal) {
1274 return;
1277 if (RefPtr<nsGlobalWindowInner> inner =
1278 nsGlobalWindowInner::Cast(mScriptGlobal->GetCurrentInnerWindow())) {
1279 if (aThaw) {
1280 inner->Thaw(false);
1281 } else {
1282 inner->Freeze(false);
1287 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
1288 MOZ_ASSERT(mozilla::BFCacheInParent());
1290 if (!mDocumentViewer) {
1291 return;
1294 // Emulate what non-SHIP BFCache does too. In pageshow case
1295 // add and remove a request and before that call SetCurrentURI to get
1296 // the location change notification.
1297 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
1298 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
1299 if (aShow) {
1300 viewer->SetIsHidden(false);
1301 mRefreshURIList = std::move(mBFCachedRefreshURIList);
1302 RefreshURIFromQueue();
1303 mFiredUnloadEvent = false;
1304 RefPtr<Document> doc = viewer->GetDocument();
1305 if (doc) {
1306 doc->NotifyActivityChanged();
1307 nsCOMPtr<nsPIDOMWindowInner> inner =
1308 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
1309 if (mBrowsingContext->IsTop()) {
1310 doc->NotifyPossibleTitleChange(false);
1311 doc->SetLoadingOrRestoredFromBFCacheTimeStampToNow();
1312 if (inner) {
1313 // Now that we have found the inner window of the page restored
1314 // from the history, we have to make sure that
1315 // performance.navigation.type is 2.
1316 // Traditionally this type change has been done to the top level page
1317 // only.
1318 Performance* performance = inner->GetPerformance();
1319 if (performance) {
1320 performance->GetDOMTiming()->NotifyRestoreStart();
1325 nsCOMPtr<nsIChannel> channel = doc->GetChannel();
1326 if (channel) {
1327 SetLoadType(LOAD_HISTORY);
1328 mEODForCurrentDocument = false;
1329 mIsRestoringDocument = true;
1330 mLoadGroup->AddRequest(channel, nullptr);
1331 SetCurrentURI(doc->GetDocumentURI(), channel,
1332 /* aFireOnLocationChange */ true,
1333 /* aIsInitialAboutBlank */ false,
1334 /* aLocationFlags */ 0);
1335 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
1336 mIsRestoringDocument = false;
1338 RefPtr<PresShell> presShell = GetPresShell();
1339 if (presShell) {
1340 presShell->Thaw(false);
1343 if (inner) {
1344 inner->FireDelayedDOMEvents(false);
1347 } else if (!mFiredUnloadEvent) {
1348 // XXXBFCache check again that the page can enter bfcache.
1349 // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
1351 if (mRefreshURIList) {
1352 RefreshURIToQueue();
1353 mBFCachedRefreshURIList = std::move(mRefreshURIList);
1354 } else {
1355 // If Stop was called, the list was moved to mSavedRefreshURIList after
1356 // calling SuspendRefreshURIs, which calls RefreshURIToQueue.
1357 mBFCachedRefreshURIList = std::move(mSavedRefreshURIList);
1360 mFiredUnloadEvent = true;
1361 viewer->PageHide(false);
1363 RefPtr<PresShell> presShell = GetPresShell();
1364 if (presShell) {
1365 presShell->Freeze(false);
1370 nsresult nsDocShell::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) {
1371 nsCOMPtr<nsIRunnable> runnable(aRunnable);
1372 if (NS_WARN_IF(!GetWindow())) {
1373 // Window should only be unavailable after destroyed.
1374 MOZ_ASSERT(mIsBeingDestroyed);
1375 return NS_ERROR_FAILURE;
1377 return SchedulerGroup::Dispatch(runnable.forget());
1380 NS_IMETHODIMP
1381 nsDocShell::DispatchLocationChangeEvent() {
1382 return Dispatch(NewRunnableMethod("nsDocShell::FireDummyOnLocationChange",
1383 this,
1384 &nsDocShell::FireDummyOnLocationChange));
1387 NS_IMETHODIMP
1388 nsDocShell::StartDelayedAutoplayMediaComponents() {
1389 RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow();
1390 if (outerWindow) {
1391 outerWindow->ActivateMediaComponents();
1393 return NS_OK;
1396 bool nsDocShell::MaybeInitTiming() {
1397 if (mTiming && !mBlankTiming) {
1398 return false;
1401 bool canBeReset = false;
1403 if (mScriptGlobal && mBlankTiming) {
1404 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
1405 if (innerWin && innerWin->GetPerformance()) {
1406 mTiming = innerWin->GetPerformance()->GetDOMTiming();
1407 mBlankTiming = false;
1411 if (!mTiming) {
1412 mTiming = new nsDOMNavigationTiming(this);
1413 canBeReset = true;
1416 mTiming->NotifyNavigationStart(
1417 mBrowsingContext->IsActive()
1418 ? nsDOMNavigationTiming::DocShellState::eActive
1419 : nsDOMNavigationTiming::DocShellState::eInactive);
1421 return canBeReset;
1424 void nsDocShell::MaybeResetInitTiming(bool aReset) {
1425 if (aReset) {
1426 mTiming = nullptr;
1430 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
1431 return mTiming;
1434 nsPresContext* nsDocShell::GetEldestPresContext() {
1435 nsIDocumentViewer* viewer = mDocumentViewer;
1436 while (viewer) {
1437 nsIDocumentViewer* prevViewer = viewer->GetPreviousViewer();
1438 if (!prevViewer) {
1439 return viewer->GetPresContext();
1441 viewer = prevViewer;
1444 return nullptr;
1447 nsPresContext* nsDocShell::GetPresContext() {
1448 if (!mDocumentViewer) {
1449 return nullptr;
1452 return mDocumentViewer->GetPresContext();
1455 PresShell* nsDocShell::GetPresShell() {
1456 nsPresContext* presContext = GetPresContext();
1457 return presContext ? presContext->GetPresShell() : nullptr;
1460 PresShell* nsDocShell::GetEldestPresShell() {
1461 nsPresContext* presContext = GetEldestPresContext();
1463 if (presContext) {
1464 return presContext->GetPresShell();
1467 return nullptr;
1470 NS_IMETHODIMP
1471 nsDocShell::GetDocViewer(nsIDocumentViewer** aDocumentViewer) {
1472 NS_ENSURE_ARG_POINTER(aDocumentViewer);
1474 *aDocumentViewer = mDocumentViewer;
1475 NS_IF_ADDREF(*aDocumentViewer);
1476 return NS_OK;
1479 NS_IMETHODIMP
1480 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) {
1481 *aWindowID = mContentWindowID;
1482 return NS_OK;
1485 NS_IMETHODIMP
1486 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) {
1487 mChromeEventHandler = aChromeEventHandler;
1489 if (mScriptGlobal) {
1490 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1493 return NS_OK;
1496 NS_IMETHODIMP
1497 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) {
1498 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1499 RefPtr<EventTarget> handler = mChromeEventHandler;
1500 handler.forget(aChromeEventHandler);
1501 return NS_OK;
1504 NS_IMETHODIMP
1505 nsDocShell::SetCurrentURIForSessionStore(nsIURI* aURI) {
1506 // Note that securityUI will set STATE_IS_INSECURE, even if
1507 // the scheme of |aURI| is "https".
1508 SetCurrentURI(aURI, nullptr,
1509 /* aFireOnLocationChange */
1510 true,
1511 /* aIsInitialAboutBlank */
1512 false,
1513 /* aLocationFlags */
1514 nsIWebProgressListener::LOCATION_CHANGE_SESSION_STORE);
1515 return NS_OK;
1518 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1519 bool aFireOnLocationChange,
1520 bool aIsInitialAboutBlank,
1521 uint32_t aLocationFlags) {
1522 MOZ_ASSERT(!mIsBeingDestroyed);
1524 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1525 ("DOCSHELL %p SetCurrentURI %s\n", this,
1526 aURI ? aURI->GetSpecOrDefault().get() : ""));
1528 // We don't want to send a location change when we're displaying an error
1529 // page, and we don't want to change our idea of "current URI" either
1530 if (mLoadType == LOAD_ERROR_PAGE) {
1531 return false;
1534 bool uriIsEqual = false;
1535 if (!mCurrentURI || !aURI ||
1536 NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
1537 mTitleValidForCurrentURI = false;
1540 SetCurrentURIInternal(aURI);
1542 #ifdef DEBUG
1543 mLastOpenedURI = aURI;
1544 #endif
1546 if (!NS_IsAboutBlank(mCurrentURI)) {
1547 mHasLoadedNonBlankURI = true;
1550 // Don't fire onLocationChange when creating a subframe's initial about:blank
1551 // document, as this can happen when it's not safe for us to run script.
1552 if (aIsInitialAboutBlank && !mHasLoadedNonBlankURI &&
1553 !mBrowsingContext->IsTop()) {
1554 MOZ_ASSERT(!aRequest && aLocationFlags == 0);
1555 return false;
1558 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1560 if (aFireOnLocationChange) {
1561 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1563 return !aFireOnLocationChange;
1566 void nsDocShell::SetCurrentURIInternal(nsIURI* aURI) {
1567 mCurrentURI = aURI;
1568 if (mBrowsingContext) {
1569 mBrowsingContext->ClearCachedValuesOfLocations();
1573 NS_IMETHODIMP
1574 nsDocShell::GetCharset(nsACString& aCharset) {
1575 aCharset.Truncate();
1577 PresShell* presShell = GetPresShell();
1578 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1579 Document* doc = presShell->GetDocument();
1580 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1581 doc->GetDocumentCharacterSet()->Name(aCharset);
1582 return NS_OK;
1585 NS_IMETHODIMP
1586 nsDocShell::ForceEncodingDetection() {
1587 nsCOMPtr<nsIDocumentViewer> viewer;
1588 GetDocViewer(getter_AddRefs(viewer));
1589 if (!viewer) {
1590 return NS_OK;
1593 Document* doc = viewer->GetDocument();
1594 if (!doc || doc->WillIgnoreCharsetOverride()) {
1595 return NS_OK;
1598 mForcedAutodetection = true;
1600 nsIURI* url = doc->GetOriginalURI();
1601 bool isFileURL = url && SchemeIsFile(url);
1603 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1604 auto encoding = doc->GetDocumentCharacterSet();
1605 // AsHTMLDocument is valid, because we called
1606 // WillIgnoreCharsetOverride() above.
1607 if (doc->AsHTMLDocument()->IsPlainText()) {
1608 switch (charsetSource) {
1609 case kCharsetFromInitialAutoDetectionASCII:
1610 // Deliberately no final version
1611 LOGCHARSETMENU(("TEXT:UnlabeledAscii"));
1612 Telemetry::AccumulateCategorical(
1613 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledAscii);
1614 break;
1615 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1616 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1617 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1618 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1619 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1620 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1621 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8"));
1622 Telemetry::AccumulateCategorical(
1623 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1624 UnlabeledNonUtf8);
1625 break;
1626 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1627 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1628 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1629 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD"));
1630 Telemetry::AccumulateCategorical(
1631 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1632 UnlabeledNonUtf8TLD);
1633 break;
1634 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1635 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1636 LOGCHARSETMENU(("TEXT:UnlabeledUtf8"));
1637 Telemetry::AccumulateCategorical(
1638 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledUtf8);
1639 break;
1640 case kCharsetFromChannel:
1641 if (encoding == UTF_8_ENCODING) {
1642 LOGCHARSETMENU(("TEXT:ChannelUtf8"));
1643 Telemetry::AccumulateCategorical(
1644 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::ChannelUtf8);
1645 } else {
1646 LOGCHARSETMENU(("TEXT:ChannelNonUtf8"));
1647 Telemetry::AccumulateCategorical(
1648 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1649 ChannelNonUtf8);
1651 break;
1652 default:
1653 LOGCHARSETMENU(("TEXT:Bug"));
1654 Telemetry::AccumulateCategorical(
1655 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::Bug);
1656 break;
1658 } else {
1659 switch (charsetSource) {
1660 case kCharsetFromInitialAutoDetectionASCII:
1661 // Deliberately no final version
1662 LOGCHARSETMENU(("HTML:UnlabeledAscii"));
1663 Telemetry::AccumulateCategorical(
1664 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledAscii);
1665 break;
1666 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1667 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1668 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1669 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1670 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1671 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1672 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8"));
1673 Telemetry::AccumulateCategorical(
1674 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1675 UnlabeledNonUtf8);
1676 break;
1677 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1678 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1679 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1680 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD"));
1681 Telemetry::AccumulateCategorical(
1682 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1683 UnlabeledNonUtf8TLD);
1684 break;
1685 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1686 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1687 LOGCHARSETMENU(("HTML:UnlabeledUtf8"));
1688 Telemetry::AccumulateCategorical(
1689 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledUtf8);
1690 break;
1691 case kCharsetFromChannel:
1692 if (encoding == UTF_8_ENCODING) {
1693 LOGCHARSETMENU(("HTML:ChannelUtf8"));
1694 Telemetry::AccumulateCategorical(
1695 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::ChannelUtf8);
1696 } else {
1697 LOGCHARSETMENU(("HTML:ChannelNonUtf8"));
1698 Telemetry::AccumulateCategorical(
1699 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1700 ChannelNonUtf8);
1702 break;
1703 case kCharsetFromXmlDeclaration:
1704 case kCharsetFromMetaTag:
1705 if (isFileURL) {
1706 LOGCHARSETMENU(("HTML:LocalLabeled"));
1707 Telemetry::AccumulateCategorical(
1708 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::LocalLabeled);
1709 } else if (encoding == UTF_8_ENCODING) {
1710 LOGCHARSETMENU(("HTML:MetaUtf8"));
1711 Telemetry::AccumulateCategorical(
1712 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::InternalUtf8);
1713 } else {
1714 LOGCHARSETMENU(("HTML:MetaNonUtf8"));
1715 Telemetry::AccumulateCategorical(
1716 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1717 InternalNonUtf8);
1719 break;
1720 default:
1721 LOGCHARSETMENU(("HTML:Bug"));
1722 Telemetry::AccumulateCategorical(
1723 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::Bug);
1724 break;
1727 return NS_OK;
1730 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1731 int32_t aCharsetSource,
1732 nsIPrincipal* aPrincipal) {
1733 mParentCharset = aCharset;
1734 mParentCharsetSource = aCharsetSource;
1735 mParentCharsetPrincipal = aPrincipal;
1738 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1739 int32_t* aCharsetSource,
1740 nsIPrincipal** aPrincipal) {
1741 aCharset = mParentCharset;
1742 *aCharsetSource = mParentCharsetSource;
1743 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1746 NS_IMETHODIMP
1747 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
1748 MOZ_ASSERT(aPromise);
1750 ErrorResult rv;
1751 RefPtr<Document> doc(GetDocument());
1752 RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
1753 if (NS_WARN_IF(rv.Failed())) {
1754 return rv.StealNSResult();
1757 // Retrieve the document's content blocking events from the parent process.
1758 RefPtr<Document::GetContentBlockingEventsPromise> promise =
1759 doc->GetContentBlockingEvents();
1760 if (promise) {
1761 promise->Then(
1762 GetCurrentSerialEventTarget(), __func__,
1763 [retPromise](const Document::GetContentBlockingEventsPromise::
1764 ResolveOrRejectValue& aValue) {
1765 if (aValue.IsResolve()) {
1766 bool has = aValue.ResolveValue() &
1767 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
1768 retPromise->MaybeResolve(has);
1769 } else {
1770 retPromise->MaybeResolve(false);
1773 } else {
1774 retPromise->MaybeResolve(false);
1777 retPromise.forget(aPromise);
1778 return NS_OK;
1781 NS_IMETHODIMP
1782 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
1783 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1785 *aAllowPlugins = mBrowsingContext->GetAllowPlugins();
1786 return NS_OK;
1789 NS_IMETHODIMP
1790 nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
1791 // XXX should enable or disable a plugin host
1792 return mBrowsingContext->SetAllowPlugins(aAllowPlugins);
1795 NS_IMETHODIMP
1796 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
1797 MOZ_ASSERT(aEnabled);
1798 *aEnabled = mCSSErrorReportingEnabled;
1799 return NS_OK;
1802 NS_IMETHODIMP
1803 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1804 mCSSErrorReportingEnabled = aEnabled;
1805 return NS_OK;
1808 NS_IMETHODIMP
1809 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1810 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1811 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1814 void nsDocShell::NotifyPrivateBrowsingChanged() {
1815 MOZ_ASSERT(!mIsBeingDestroyed);
1817 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1818 while (iter.HasMore()) {
1819 nsWeakPtr ref = iter.GetNext();
1820 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1821 if (!obs) {
1822 iter.Remove();
1823 } else {
1824 obs->PrivateModeChanged(UsePrivateBrowsing());
1829 NS_IMETHODIMP
1830 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1831 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1834 NS_IMETHODIMP
1835 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1836 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1839 NS_IMETHODIMP
1840 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1841 NS_ENSURE_ARG_POINTER(aResult);
1843 *aResult = mHasLoadedNonBlankURI;
1844 return NS_OK;
1847 NS_IMETHODIMP
1848 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1849 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1850 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1853 NS_IMETHODIMP
1854 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1855 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1858 NS_IMETHODIMP
1859 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1860 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1861 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1864 NS_IMETHODIMP
1865 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1866 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1869 NS_IMETHODIMP
1870 nsDocShell::AddWeakPrivacyTransitionObserver(
1871 nsIPrivacyTransitionObserver* aObserver) {
1872 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1873 if (!weakObs) {
1874 return NS_ERROR_NOT_AVAILABLE;
1876 mPrivacyObservers.AppendElement(weakObs);
1877 return NS_OK;
1880 NS_IMETHODIMP
1881 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1882 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1883 if (!weakObs) {
1884 return NS_ERROR_FAILURE;
1886 mReflowObservers.AppendElement(weakObs);
1887 return NS_OK;
1890 NS_IMETHODIMP
1891 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1892 nsWeakPtr obs = do_GetWeakReference(aObserver);
1893 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1896 NS_IMETHODIMP
1897 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1898 DOMHighResTimeStamp aStart,
1899 DOMHighResTimeStamp aEnd) {
1900 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1901 while (iter.HasMore()) {
1902 nsWeakPtr ref = iter.GetNext();
1903 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1904 if (!obs) {
1905 iter.Remove();
1906 } else if (aInterruptible) {
1907 obs->ReflowInterruptible(aStart, aEnd);
1908 } else {
1909 obs->Reflow(aStart, aEnd);
1912 return NS_OK;
1915 NS_IMETHODIMP
1916 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1917 NS_ENSURE_ARG_POINTER(aReturn);
1919 *aReturn = mAllowMetaRedirects;
1920 return NS_OK;
1923 NS_IMETHODIMP
1924 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1925 mAllowMetaRedirects = aValue;
1926 return NS_OK;
1929 NS_IMETHODIMP
1930 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1931 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1933 *aAllowSubframes = mAllowSubframes;
1934 return NS_OK;
1937 NS_IMETHODIMP
1938 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1939 mAllowSubframes = aAllowSubframes;
1940 return NS_OK;
1943 NS_IMETHODIMP
1944 nsDocShell::GetAllowImages(bool* aAllowImages) {
1945 NS_ENSURE_ARG_POINTER(aAllowImages);
1947 *aAllowImages = mAllowImages;
1948 return NS_OK;
1951 NS_IMETHODIMP
1952 nsDocShell::SetAllowImages(bool aAllowImages) {
1953 mAllowImages = aAllowImages;
1954 return NS_OK;
1957 NS_IMETHODIMP
1958 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1959 *aAllowMedia = mAllowMedia;
1960 return NS_OK;
1963 NS_IMETHODIMP
1964 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1965 mAllowMedia = aAllowMedia;
1967 // Mute or unmute audio contexts attached to the inner window.
1968 if (mScriptGlobal) {
1969 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1970 if (aAllowMedia) {
1971 innerWin->UnmuteAudioContexts();
1972 } else {
1973 innerWin->MuteAudioContexts();
1978 return NS_OK;
1981 NS_IMETHODIMP
1982 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1983 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1984 return NS_OK;
1987 NS_IMETHODIMP
1988 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
1989 mAllowDNSPrefetch = aAllowDNSPrefetch;
1990 return NS_OK;
1993 NS_IMETHODIMP
1994 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
1995 *aAllowWindowControl = mAllowWindowControl;
1996 return NS_OK;
1999 NS_IMETHODIMP
2000 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
2001 mAllowWindowControl = aAllowWindowControl;
2002 return NS_OK;
2005 NS_IMETHODIMP
2006 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
2007 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
2008 return NS_OK;
2011 NS_IMETHODIMP
2012 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
2013 BrowsingContext::Transaction txn;
2014 txn.SetAllowContentRetargeting(aAllowContentRetargeting);
2015 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
2016 return txn.Commit(mBrowsingContext);
2019 NS_IMETHODIMP
2020 nsDocShell::GetAllowContentRetargetingOnChildren(
2021 bool* aAllowContentRetargetingOnChildren) {
2022 *aAllowContentRetargetingOnChildren =
2023 mBrowsingContext->GetAllowContentRetargetingOnChildren();
2024 return NS_OK;
2027 NS_IMETHODIMP
2028 nsDocShell::SetAllowContentRetargetingOnChildren(
2029 bool aAllowContentRetargetingOnChildren) {
2030 return mBrowsingContext->SetAllowContentRetargetingOnChildren(
2031 aAllowContentRetargetingOnChildren);
2034 NS_IMETHODIMP
2035 nsDocShell::GetMayEnableCharacterEncodingMenu(
2036 bool* aMayEnableCharacterEncodingMenu) {
2037 *aMayEnableCharacterEncodingMenu = false;
2038 if (!mDocumentViewer) {
2039 return NS_OK;
2041 Document* doc = mDocumentViewer->GetDocument();
2042 if (!doc) {
2043 return NS_OK;
2045 if (doc->WillIgnoreCharsetOverride()) {
2046 return NS_OK;
2049 *aMayEnableCharacterEncodingMenu = true;
2050 return NS_OK;
2053 NS_IMETHODIMP
2054 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
2055 DocShellEnumeratorDirection aDirection,
2056 nsTArray<RefPtr<nsIDocShell>>& aResult) {
2057 aResult.Clear();
2059 nsDocShellEnumerator docShellEnum(
2060 (aDirection == ENUMERATE_FORWARDS)
2061 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2062 : nsDocShellEnumerator::EnumerationDirection::Backwards,
2063 aItemType, *this);
2065 nsresult rv = docShellEnum.BuildDocShellArray(aResult);
2066 if (NS_FAILED(rv)) {
2067 return rv;
2070 return NS_OK;
2073 NS_IMETHODIMP
2074 nsDocShell::GetAppType(AppType* aAppType) {
2075 *aAppType = mAppType;
2076 return NS_OK;
2079 NS_IMETHODIMP
2080 nsDocShell::SetAppType(AppType aAppType) {
2081 mAppType = aAppType;
2082 return NS_OK;
2085 NS_IMETHODIMP
2086 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2087 *aAllowAuth = mAllowAuth;
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2093 mAllowAuth = aAllowAuth;
2094 return NS_OK;
2097 NS_IMETHODIMP
2098 nsDocShell::GetZoom(float* aZoom) {
2099 NS_ENSURE_ARG_POINTER(aZoom);
2100 *aZoom = 1.0f;
2101 return NS_OK;
2104 NS_IMETHODIMP
2105 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2107 NS_IMETHODIMP
2108 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
2109 NS_ENSURE_ARG_POINTER(aBusyFlags);
2111 *aBusyFlags = mBusyFlags;
2112 return NS_OK;
2115 NS_IMETHODIMP
2116 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
2117 bool* aTookFocus) {
2118 NS_ENSURE_ARG_POINTER(aTookFocus);
2120 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2121 if (chromeFocus) {
2122 if (aForward) {
2123 *aTookFocus =
2124 NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2125 } else {
2126 *aTookFocus =
2127 NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2129 } else {
2130 *aTookFocus = false;
2133 return NS_OK;
2136 NS_IMETHODIMP
2137 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2138 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
2139 delegate.forget(aLoadURIDelegate);
2140 return NS_OK;
2143 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
2144 if (nsCOMPtr<nsILoadURIDelegate> result =
2145 do_QueryActor("LoadURIDelegate", GetDocument())) {
2146 return result.forget();
2149 return nullptr;
2152 NS_IMETHODIMP
2153 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2154 *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
2155 return NS_OK;
2158 NS_IMETHODIMP
2159 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2160 return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
2163 NS_IMETHODIMP
2164 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
2165 *aPreviousEntryIndex = mPreviousEntryIndex;
2166 return NS_OK;
2169 NS_IMETHODIMP
2170 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
2171 *aLoadedEntryIndex = mLoadedEntryIndex;
2172 return NS_OK;
2175 NS_IMETHODIMP
2176 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2177 // These indices are used for fastback cache eviction, to determine
2178 // which session history entries are candidates for content viewer
2179 // eviction. We need to adjust by the number of entries that we
2180 // just purged from history, so that we look at the right session history
2181 // entries during eviction.
2182 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
2183 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
2185 for (auto* child : mChildList.ForwardRange()) {
2186 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2187 if (shell) {
2188 shell->HistoryPurged(aNumEntries);
2192 return NS_OK;
2195 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2196 // These indices are used for fastback cache eviction, to determine
2197 // which session history entries are candidates for content viewer
2198 // eviction. We need to adjust by the number of entries that we
2199 // just purged from history, so that we look at the right session history
2200 // entries during eviction.
2201 if (aIndex == mPreviousEntryIndex) {
2202 mPreviousEntryIndex = -1;
2203 } else if (aIndex < mPreviousEntryIndex) {
2204 --mPreviousEntryIndex;
2206 if (mLoadedEntryIndex == aIndex) {
2207 mLoadedEntryIndex = 0;
2208 } else if (aIndex < mLoadedEntryIndex) {
2209 --mLoadedEntryIndex;
2212 for (auto* child : mChildList.ForwardRange()) {
2213 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2214 if (shell) {
2215 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2219 return NS_OK;
2222 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2223 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2224 return NS_OK;
2227 NS_IMETHODIMP
2228 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2229 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2230 if (!aValue && mItemType == typeChrome && !parent) {
2231 // Window dragging is always allowed for top level
2232 // chrome docshells.
2233 return NS_ERROR_FAILURE;
2235 mWindowDraggingAllowed = aValue;
2236 return NS_OK;
2239 NS_IMETHODIMP
2240 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2241 // window dragging regions in CSS (-moz-window-drag:drag)
2242 // can be slow. Default behavior is to only allow it for
2243 // chrome top level windows.
2244 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2245 if (mItemType == typeChrome && !parent) {
2246 // Top level chrome window
2247 *aValue = true;
2248 } else {
2249 *aValue = mWindowDraggingAllowed;
2251 return NS_OK;
2254 NS_IMETHODIMP
2255 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2256 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2257 return NS_OK;
2260 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2261 if (mDocumentViewer) {
2262 Document* doc = mDocumentViewer->GetDocument();
2263 if (doc) {
2264 return doc->GetChannel();
2267 return nullptr;
2270 NS_IMETHODIMP
2271 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2272 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2273 if (!weakObs) {
2274 return NS_ERROR_FAILURE;
2276 mScrollObservers.AppendElement(weakObs);
2277 return NS_OK;
2280 NS_IMETHODIMP
2281 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2282 nsWeakPtr obs = do_GetWeakReference(aObserver);
2283 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2286 void nsDocShell::NotifyAsyncPanZoomStarted() {
2287 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2288 while (iter.HasMore()) {
2289 nsWeakPtr ref = iter.GetNext();
2290 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2291 if (obs) {
2292 obs->AsyncPanZoomStarted();
2293 } else {
2294 iter.Remove();
2299 void nsDocShell::NotifyAsyncPanZoomStopped() {
2300 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2301 while (iter.HasMore()) {
2302 nsWeakPtr ref = iter.GetNext();
2303 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2304 if (obs) {
2305 obs->AsyncPanZoomStopped();
2306 } else {
2307 iter.Remove();
2312 NS_IMETHODIMP
2313 nsDocShell::NotifyScrollObservers() {
2314 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2315 while (iter.HasMore()) {
2316 nsWeakPtr ref = iter.GetNext();
2317 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2318 if (obs) {
2319 obs->ScrollPositionChanged();
2320 } else {
2321 iter.Remove();
2324 return NS_OK;
2327 //*****************************************************************************
2328 // nsDocShell::nsIDocShellTreeItem
2329 //*****************************************************************************
2331 NS_IMETHODIMP
2332 nsDocShell::GetName(nsAString& aName) {
2333 aName = mBrowsingContext->Name();
2334 return NS_OK;
2337 NS_IMETHODIMP
2338 nsDocShell::SetName(const nsAString& aName) {
2339 return mBrowsingContext->SetName(aName);
2342 NS_IMETHODIMP
2343 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2344 NS_ENSURE_ARG_POINTER(aResult);
2345 *aResult = mBrowsingContext->NameEquals(aName);
2346 return NS_OK;
2349 NS_IMETHODIMP
2350 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2351 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2352 return NS_OK;
2355 NS_IMETHODIMP
2356 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2357 if (mWillChangeProcess) {
2358 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2359 return NS_ERROR_FAILURE;
2362 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2365 NS_IMETHODIMP
2366 nsDocShell::ClearCachedPlatform() {
2367 nsCOMPtr<nsPIDOMWindowInner> win =
2368 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2369 if (win) {
2370 Navigator* navigator = win->Navigator();
2371 if (navigator) {
2372 navigator->ClearPlatformCache();
2376 return NS_OK;
2379 NS_IMETHODIMP
2380 nsDocShell::ClearCachedUserAgent() {
2381 nsCOMPtr<nsPIDOMWindowInner> win =
2382 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2383 if (win) {
2384 Navigator* navigator = win->Navigator();
2385 if (navigator) {
2386 navigator->ClearUserAgentCache();
2390 return NS_OK;
2393 NS_IMETHODIMP
2394 nsDocShell::GetMetaViewportOverride(
2395 MetaViewportOverride* aMetaViewportOverride) {
2396 NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
2398 *aMetaViewportOverride = mMetaViewportOverride;
2399 return NS_OK;
2402 NS_IMETHODIMP
2403 nsDocShell::SetMetaViewportOverride(
2404 MetaViewportOverride aMetaViewportOverride) {
2405 // We don't have a way to verify this coming from Javascript, so this check is
2406 // still needed.
2407 if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
2408 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
2409 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
2410 return NS_ERROR_INVALID_ARG;
2413 mMetaViewportOverride = aMetaViewportOverride;
2415 // Inform our presShell that it needs to re-check its need for a viewport
2416 // override.
2417 if (RefPtr<PresShell> presShell = GetPresShell()) {
2418 presShell->MaybeRecreateMobileViewportManager(true);
2421 return NS_OK;
2424 /* virtual */
2425 int32_t nsDocShell::ItemType() { return mItemType; }
2427 NS_IMETHODIMP
2428 nsDocShell::GetItemType(int32_t* aItemType) {
2429 NS_ENSURE_ARG_POINTER(aItemType);
2431 MOZ_DIAGNOSTIC_ASSERT(
2432 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2433 *aItemType = mItemType;
2434 return NS_OK;
2437 NS_IMETHODIMP
2438 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2439 if (!mParent) {
2440 *aParent = nullptr;
2441 } else {
2442 CallQueryInterface(mParent, aParent);
2444 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2445 // don't want to throw; we just want to return null.
2446 return NS_OK;
2449 // With Fission, related nsDocShell objects may exist in a different process. In
2450 // that case, this method will return `nullptr`, despite a parent nsDocShell
2451 // object existing.
2453 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2454 // parent entry is not in the current process, and handle the case where the
2455 // parent nsDocShell is inaccessible.
2456 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2457 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2458 return docshell.forget().downcast<nsDocShell>();
2461 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2462 MOZ_ASSERT(!mIsBeingDestroyed);
2464 // If there is an existing document then there is no need to create
2465 // a client for a future initial about:blank document.
2466 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindow() &&
2467 mScriptGlobal->GetCurrentInnerWindow()->GetExtantDoc()) {
2468 MOZ_DIAGNOSTIC_ASSERT(
2469 mScriptGlobal->GetCurrentInnerWindow()->GetClientInfo().isSome());
2470 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2471 return;
2474 // Don't recreate the initial client source. We call this multiple times
2475 // when DoChannelLoad() is called before CreateAboutBlankDocumentViewer.
2476 if (mInitialClientSource) {
2477 return;
2480 // Don't pre-allocate the client when we are sandboxed. The inherited
2481 // principal does not take sandboxing into account.
2482 // TODO: Refactor sandboxing principal code out so we can use it here.
2483 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2484 return;
2487 // We cannot get inherited foreign partitioned principal here. Instead, we
2488 // directly check which principal we want to inherit for the service worker.
2489 nsIPrincipal* principal =
2490 aPrincipal
2491 ? aPrincipal
2492 : GetInheritedPrincipal(
2493 false, StoragePrincipalHelper::
2494 ShouldUsePartitionPrincipalForServiceWorker(this));
2496 // Sometimes there is no principal available when we are called from
2497 // CreateAboutBlankDocumentViewer. For example, sometimes the principal
2498 // is only extracted from the load context after the document is created
2499 // in Document::ResetToURI(). Ideally we would do something similar
2500 // here, but for now lets just avoid the issue by not preallocating the
2501 // client.
2502 if (!principal) {
2503 return;
2506 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2507 if (!win) {
2508 return;
2511 mInitialClientSource = ClientManager::CreateSource(
2512 ClientType::Window, GetMainThreadSerialEventTarget(), principal);
2513 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2515 // Mark the initial client as execution ready, but owned by the docshell.
2516 // If the client is actually used this will cause ClientSource to force
2517 // the creation of the initial about:blank by calling
2518 // nsDocShell::GetDocument().
2519 mInitialClientSource->DocShellExecutionReady(this);
2521 // Next, check to see if the parent is controlled.
2522 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2523 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2524 nsPIDOMWindowInner* parentInner =
2525 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2526 if (!parentInner) {
2527 return;
2530 nsCOMPtr<nsIURI> uri;
2531 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
2533 // We're done if there is no parent controller or if this docshell
2534 // is not permitted to control for some reason.
2535 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2536 if (controller.isNothing() ||
2537 !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2538 return;
2541 mInitialClientSource->InheritController(controller.ref());
2544 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2545 if (mInitialClientSource) {
2546 Maybe<ClientInfo> result;
2547 result.emplace(mInitialClientSource->Info());
2548 return result;
2551 nsPIDOMWindowInner* innerWindow =
2552 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2553 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2555 if (!doc || !doc->IsInitialDocument()) {
2556 return Maybe<ClientInfo>();
2559 return innerWindow->GetClientInfo();
2562 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2563 bool wasFrame = IsSubframe();
2565 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2566 NS_ENSURE_SUCCESS(rv, rv);
2568 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2569 if (wasFrame != IsSubframe() && priorityGroup) {
2570 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2573 // Curse ambiguous nsISupports inheritance!
2574 nsISupports* parent = GetAsSupports(aParent);
2576 // If parent is another docshell, we inherit all their flags for
2577 // allowing plugins, scripting etc.
2578 bool value;
2579 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2581 if (parentAsDocShell) {
2582 if (mAllowMetaRedirects &&
2583 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2584 SetAllowMetaRedirects(value);
2586 if (mAllowSubframes &&
2587 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2588 SetAllowSubframes(value);
2590 if (mAllowImages &&
2591 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2592 SetAllowImages(value);
2594 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2595 if (mAllowWindowControl &&
2596 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2597 SetAllowWindowControl(value);
2599 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2600 value = false;
2602 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2604 // We don't need to inherit metaViewportOverride, because the viewport
2605 // is only relevant for the outermost nsDocShell, not for any iframes
2606 // like this that might be embedded within it.
2609 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2610 if (parentURIListener) {
2611 mContentListener->SetParentContentListener(parentURIListener);
2614 return NS_OK;
2617 void nsDocShell::MaybeRestoreWindowName() {
2618 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2619 return;
2622 // We only restore window.name for the top-level content.
2623 if (!mBrowsingContext->IsTopContent()) {
2624 return;
2627 nsAutoString name;
2629 // Following implements https://html.spec.whatwg.org/#history-traversal:
2630 // Step 4.4. Check if the loading entry has a name.
2632 if (mLSHE) {
2633 mLSHE->GetName(name);
2636 if (mLoadingEntry) {
2637 name = mLoadingEntry->mInfo.GetName();
2640 if (name.IsEmpty()) {
2641 return;
2644 // Step 4.4.1. Set the name to the browsing context.
2645 Unused << mBrowsingContext->SetName(name);
2647 // Step 4.4.2. Clear the name of all entries that are contiguous and
2648 // same-origin with the loading entry.
2649 if (mLSHE) {
2650 nsSHistory::WalkContiguousEntries(
2651 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
2654 if (mLoadingEntry) {
2655 // Clear the name of the session entry in the child side. For parent side,
2656 // the clearing will be done when we commit the history to the parent.
2657 mLoadingEntry->mInfo.SetName(EmptyString());
2661 void nsDocShell::StoreWindowNameToSHEntries() {
2662 MOZ_ASSERT(mBrowsingContext->IsTopContent());
2664 nsAutoString name;
2665 mBrowsingContext->GetName(name);
2667 if (mOSHE) {
2668 nsSHistory::WalkContiguousEntries(
2669 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2672 if (mozilla::SessionHistoryInParent()) {
2673 if (XRE_IsParentProcess()) {
2674 SessionHistoryEntry* entry =
2675 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2676 if (entry) {
2677 nsSHistory::WalkContiguousEntries(
2678 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2680 } else {
2681 // Ask parent process to store the name in entries.
2682 mozilla::Unused
2683 << ContentChild::GetSingleton()
2684 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2685 mBrowsingContext, name);
2690 NS_IMETHODIMP
2691 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2692 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2693 *aParent = do_AddRef(parentBC->GetDocShell()).take();
2695 return NS_OK;
2698 NS_IMETHODIMP
2699 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2700 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2702 RefPtr<nsDocShell> root = this;
2703 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2704 while (parent) {
2705 root = parent;
2706 parent = root->GetInProcessParentDocshell();
2709 root.forget(aRootTreeItem);
2710 return NS_OK;
2713 NS_IMETHODIMP
2714 nsDocShell::GetInProcessSameTypeRootTreeItem(
2715 nsIDocShellTreeItem** aRootTreeItem) {
2716 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2717 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2719 nsCOMPtr<nsIDocShellTreeItem> parent;
2720 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2721 NS_ERROR_FAILURE);
2722 while (parent) {
2723 *aRootTreeItem = parent;
2724 NS_ENSURE_SUCCESS(
2725 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2726 NS_ERROR_FAILURE);
2728 NS_ADDREF(*aRootTreeItem);
2729 return NS_OK;
2732 NS_IMETHODIMP
2733 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2734 NS_ENSURE_ARG_POINTER(aTreeOwner);
2736 *aTreeOwner = mTreeOwner;
2737 NS_IF_ADDREF(*aTreeOwner);
2738 return NS_OK;
2741 NS_IMETHODIMP
2742 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2743 if (mIsBeingDestroyed && aTreeOwner) {
2744 return NS_ERROR_FAILURE;
2747 // Don't automatically set the progress based on the tree owner for frames
2748 if (!IsSubframe()) {
2749 nsCOMPtr<nsIWebProgress> webProgress =
2750 do_QueryInterface(GetAsSupports(this));
2752 if (webProgress) {
2753 nsCOMPtr<nsIWebProgressListener> oldListener =
2754 do_QueryInterface(mTreeOwner);
2755 nsCOMPtr<nsIWebProgressListener> newListener =
2756 do_QueryInterface(aTreeOwner);
2758 if (oldListener) {
2759 webProgress->RemoveProgressListener(oldListener);
2762 if (newListener) {
2763 webProgress->AddProgressListener(newListener,
2764 nsIWebProgress::NOTIFY_ALL);
2769 mTreeOwner = aTreeOwner; // Weak reference per API
2771 for (auto* childDocLoader : mChildList.ForwardRange()) {
2772 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
2773 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2775 if (child->ItemType() == mItemType) {
2776 child->SetTreeOwner(aTreeOwner);
2780 // If we're in the content process and have had a TreeOwner set on us, extract
2781 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2782 // that it hasn't changed.
2783 if (mTreeOwner && XRE_IsContentProcess()) {
2784 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2785 MOZ_ASSERT(newBrowserChild,
2786 "No BrowserChild actor for tree owner in Content!");
2788 if (mBrowserChild) {
2789 nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2790 do_QueryReferent(mBrowserChild);
2791 MOZ_RELEASE_ASSERT(
2792 oldBrowserChild == newBrowserChild,
2793 "Cannot change BrowserChild during nsDocShell lifetime!");
2794 } else {
2795 mBrowserChild = do_GetWeakReference(newBrowserChild);
2799 return NS_OK;
2802 NS_IMETHODIMP
2803 nsDocShell::GetHistoryID(nsID& aID) {
2804 aID = mBrowsingContext->GetHistoryID();
2805 return NS_OK;
2808 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
2810 NS_IMETHODIMP
2811 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2812 *aIsInUnload = mFiredUnloadEvent;
2813 return NS_OK;
2816 NS_IMETHODIMP
2817 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2818 NS_ENSURE_ARG_POINTER(aChildCount);
2819 *aChildCount = mChildList.Length();
2820 return NS_OK;
2823 NS_IMETHODIMP
2824 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2825 NS_ENSURE_ARG_POINTER(aChild);
2827 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2828 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2830 // Make sure we're not creating a loop in the docshell tree
2831 nsDocLoader* ancestor = this;
2832 do {
2833 if (childAsDocLoader == ancestor) {
2834 return NS_ERROR_ILLEGAL_VALUE;
2836 ancestor = ancestor->GetParent();
2837 } while (ancestor);
2839 // Make sure to remove the child from its current parent.
2840 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2841 if (childsParent) {
2842 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2843 NS_ENSURE_SUCCESS(rv, rv);
2846 // Make sure to clear the treeowner in case this child is a different type
2847 // from us.
2848 aChild->SetTreeOwner(nullptr);
2850 nsresult res = AddChildLoader(childAsDocLoader);
2851 NS_ENSURE_SUCCESS(res, res);
2852 NS_ASSERTION(!mChildList.IsEmpty(),
2853 "child list must not be empty after a successful add");
2855 /* Set the child's global history if the parent has one */
2856 if (mBrowsingContext->GetUseGlobalHistory()) {
2857 // childDocShell->SetUseGlobalHistory(true);
2858 // this should be set through BC inherit
2859 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
2862 if (aChild->ItemType() != mItemType) {
2863 return NS_OK;
2866 aChild->SetTreeOwner(mTreeOwner);
2868 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2869 if (!childAsDocShell) {
2870 return NS_OK;
2873 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2875 // Now take this document's charset and set the child's parentCharset field
2876 // to it. We'll later use that field, in the loading process, for the
2877 // charset choosing algorithm.
2878 // If we fail, at any point, we just return NS_OK.
2879 // This code has some performance impact. But this will be reduced when
2880 // the current charset will finally be stored as an Atom, avoiding the
2881 // alias resolution extra look-up.
2883 // we are NOT going to propagate the charset is this Chrome's docshell
2884 if (mItemType == nsIDocShellTreeItem::typeChrome) {
2885 return NS_OK;
2888 // get the parent's current charset
2889 if (!mDocumentViewer) {
2890 return NS_OK;
2892 Document* doc = mDocumentViewer->GetDocument();
2893 if (!doc) {
2894 return NS_OK;
2897 const Encoding* parentCS = doc->GetDocumentCharacterSet();
2898 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2899 // set the child's parentCharset
2900 childAsDocShell->SetParentCharset(parentCS, charsetSource,
2901 doc->NodePrincipal());
2903 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2904 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2906 return NS_OK;
2909 NS_IMETHODIMP
2910 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
2911 NS_ENSURE_ARG_POINTER(aChild);
2913 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2914 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2916 nsresult rv = RemoveChildLoader(childAsDocLoader);
2917 NS_ENSURE_SUCCESS(rv, rv);
2919 aChild->SetTreeOwner(nullptr);
2921 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
2924 NS_IMETHODIMP
2925 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
2926 NS_ENSURE_ARG_POINTER(aChild);
2928 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
2929 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
2931 child.forget(aChild);
2933 return NS_OK;
2936 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
2937 #ifdef DEBUG
2938 if (aIndex < 0) {
2939 NS_WARNING("Negative index passed to GetChildAt");
2940 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
2941 NS_WARNING("Too large an index passed to GetChildAt");
2943 #endif
2945 nsIDocumentLoader* child = ChildAt(aIndex);
2947 // child may be nullptr here.
2948 return static_cast<nsDocShell*>(child);
2951 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
2952 nsISHEntry* aNewEntry,
2953 int32_t aChildOffset, uint32_t aLoadType,
2954 bool aCloneChildren) {
2955 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2956 nsresult rv = NS_OK;
2958 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
2959 /* You get here if you are currently building a
2960 * hierarchy ie.,you just visited a frameset page
2962 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
2963 rv = mLSHE->AddChild(aNewEntry, aChildOffset);
2965 } else if (!aCloneRef) {
2966 /* This is an initial load in some subframe. Just append it if we can */
2967 if (mOSHE) {
2968 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
2970 } else {
2971 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
2972 if (shistory) {
2973 rv = shistory->LegacySHistory()->AddChildSHEntryHelper(
2974 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
2977 return rv;
2980 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
2981 int32_t aChildOffset,
2982 bool aCloneChildren) {
2983 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2984 /* You will get here when you are in a subframe and
2985 * a new url has been loaded on you.
2986 * The mOSHE in this subframe will be the previous url's
2987 * mOSHE. This mOSHE will be used as the identification
2988 * for this subframe in the CloneAndReplace function.
2991 // In this case, we will end up calling AddEntry, which increases the
2992 // current index by 1
2993 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
2994 if (rootSH) {
2995 mPreviousEntryIndex = rootSH->Index();
2998 nsresult rv;
2999 // XXX(farre): this is not Fission safe, expect errors. This never
3000 // get's executed once session history in the parent is enabled.
3001 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
3002 NS_WARNING_ASSERTION(
3003 parent || !UseRemoteSubframes(),
3004 "Failed to add child session history entry! This will be resolved once "
3005 "session history in the parent is enabled.");
3006 if (parent) {
3007 rv = nsDocShell::Cast(parent)->AddChildSHEntry(
3008 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
3011 if (rootSH) {
3012 mLoadedEntryIndex = rootSH->Index();
3014 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
3015 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
3016 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
3017 mLoadedEntryIndex));
3021 return rv;
3024 NS_IMETHODIMP
3025 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3026 *aOSHE = false;
3027 *aEntry = nullptr;
3028 if (mLSHE) {
3029 NS_ADDREF(*aEntry = mLSHE);
3030 } else if (mOSHE) {
3031 NS_ADDREF(*aEntry = mOSHE);
3032 *aOSHE = true;
3034 return NS_OK;
3037 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
3038 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
3039 mBrowsingContext) {
3040 if (XRE_IsContentProcess()) {
3041 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
3042 if (contentChild) {
3043 contentChild->SendSynchronizeLayoutHistoryState(
3044 mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
3046 } else {
3047 SessionHistoryEntry* entry =
3048 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
3049 if (entry) {
3050 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
3053 if (mLoadingEntry &&
3054 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
3055 mLoadingEntry->mInfo.SetLayoutHistoryState(
3056 mActiveEntry->GetLayoutHistoryState());
3060 return NS_OK;
3063 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
3064 if (mLoadGroup) {
3065 mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
3066 } else {
3067 NS_WARNING(
3068 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3069 "propagate the mode to");
3073 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3074 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3075 return mScriptGlobal;
3078 Document* nsDocShell::GetDocument() {
3079 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), nullptr);
3080 return mDocumentViewer->GetDocument();
3083 Document* nsDocShell::GetExtantDocument() {
3084 return mDocumentViewer ? mDocumentViewer->GetDocument() : nullptr;
3087 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3088 if (NS_FAILED(EnsureScriptEnvironment())) {
3089 return nullptr;
3091 return mScriptGlobal;
3094 NS_IMETHODIMP
3095 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3096 NS_ENSURE_ARG_POINTER(aWindow);
3098 nsresult rv = EnsureScriptEnvironment();
3099 NS_ENSURE_SUCCESS(rv, rv);
3101 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3102 window.forget(aWindow);
3103 return NS_OK;
3106 NS_IMETHODIMP
3107 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3108 RefPtr<ContentFrameMessageManager> mm;
3109 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3110 mm = browserChild->GetMessageManager();
3111 } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3112 mm = win->GetMessageManager();
3114 mm.forget(aMessageManager);
3115 return NS_OK;
3118 NS_IMETHODIMP
3119 nsDocShell::GetIsNavigating(bool* aOut) {
3120 *aOut = mIsNavigating;
3121 return NS_OK;
3124 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3125 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3126 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3127 if (!rootSH || !aEntry) {
3128 return;
3131 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3134 //-------------------------------------
3135 //-- Helper Method for Print discovery
3136 //-------------------------------------
3137 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
3138 if (!mBrowsingContext->Top()->GetIsPrinting()) {
3139 return false;
3141 if (aDisplayErrorDialog) {
3142 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3144 return true;
3147 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3148 bool aCheckIfUnloadFired) {
3149 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
3150 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3151 if (!isAllowed) {
3152 return false;
3154 if (!mDocumentViewer) {
3155 return true;
3157 bool firingBeforeUnload;
3158 mDocumentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3159 return !firingBeforeUnload;
3162 //*****************************************************************************
3163 // nsDocShell::nsIWebNavigation
3164 //*****************************************************************************
3166 NS_IMETHODIMP
3167 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3168 *aCanGoBack = false;
3169 if (!IsNavigationAllowed(false)) {
3170 return NS_OK; // JS may not handle returning of an error code
3172 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3173 if (rootSH) {
3174 *aCanGoBack = rootSH->CanGo(-1);
3175 MOZ_LOG(gSHLog, LogLevel::Verbose,
3176 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
3178 return NS_OK;
3180 return NS_ERROR_FAILURE;
3183 NS_IMETHODIMP
3184 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3185 *aCanGoForward = false;
3186 if (!IsNavigationAllowed(false)) {
3187 return NS_OK; // JS may not handle returning of an error code
3189 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3190 if (rootSH) {
3191 *aCanGoForward = rootSH->CanGo(1);
3192 MOZ_LOG(gSHLog, LogLevel::Verbose,
3193 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
3194 return NS_OK;
3196 return NS_ERROR_FAILURE;
3199 NS_IMETHODIMP
3200 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
3201 if (!IsNavigationAllowed()) {
3202 return NS_OK; // JS may not handle returning of an error code
3205 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3206 mIsNavigating = true;
3208 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3209 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3210 ErrorResult rv;
3211 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
3212 return rv.StealNSResult();
3215 NS_IMETHODIMP
3216 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
3217 if (!IsNavigationAllowed()) {
3218 return NS_OK; // JS may not handle returning of an error code
3221 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3222 mIsNavigating = true;
3224 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3225 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3226 ErrorResult rv;
3227 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
3228 return rv.StealNSResult();
3231 // XXX(nika): We may want to stop exposing this API in the child process? Going
3232 // to a specific index from multiple different processes could definitely race.
3233 NS_IMETHODIMP
3234 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
3235 if (!IsNavigationAllowed()) {
3236 return NS_OK; // JS may not handle returning of an error code
3239 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3240 mIsNavigating = true;
3242 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3243 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3245 ErrorResult rv;
3246 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
3247 rv);
3248 return rv.StealNSResult();
3251 nsresult nsDocShell::LoadURI(nsIURI* aURI,
3252 const LoadURIOptions& aLoadURIOptions) {
3253 if (!IsNavigationAllowed()) {
3254 return NS_OK; // JS may not handle returning of an error code
3256 RefPtr<nsDocShellLoadState> loadState;
3257 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3258 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3259 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
3260 if (NS_FAILED(rv) || !loadState) {
3261 return NS_ERROR_FAILURE;
3264 return LoadURI(loadState, true);
3267 NS_IMETHODIMP
3268 nsDocShell::LoadURIFromScript(nsIURI* aURI,
3269 JS::Handle<JS::Value> aLoadURIOptions,
3270 JSContext* aCx) {
3271 // generate dictionary for aLoadURIOptions and forward call
3272 LoadURIOptions loadURIOptions;
3273 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3274 return NS_ERROR_INVALID_ARG;
3276 return LoadURI(aURI, loadURIOptions);
3279 nsresult nsDocShell::FixupAndLoadURIString(
3280 const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) {
3281 if (!IsNavigationAllowed()) {
3282 return NS_OK; // JS may not handle returning of an error code
3285 RefPtr<nsDocShellLoadState> loadState;
3286 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3287 mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState));
3289 uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3290 if (NS_ERROR_MALFORMED_URI == rv) {
3291 MOZ_LOG(gSHLog, LogLevel::Debug,
3292 ("Creating an active entry on nsDocShell %p to %s (because "
3293 "we're showing an error page)",
3294 this, NS_ConvertUTF16toUTF8(aURIString).get()));
3296 // We need to store a session history entry. We don't have a valid URI, so
3297 // we use about:blank instead.
3298 nsCOMPtr<nsIURI> uri;
3299 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
3300 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
3301 if (aLoadURIOptions.mTriggeringPrincipal) {
3302 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
3303 } else {
3304 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
3306 if (mozilla::SessionHistoryInParent()) {
3307 mActiveEntry = MakeUnique<SessionHistoryInfo>(
3308 uri, triggeringPrincipal, nullptr, nullptr, nullptr,
3309 nsLiteralCString("text/html"));
3310 mBrowsingContext->SetActiveSessionHistoryEntry(
3311 Nothing(), mActiveEntry.get(), MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
3312 /* aUpdatedCacheKey = */ 0);
3314 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(),
3315 nullptr) &&
3316 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3317 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3321 if (NS_FAILED(rv) || !loadState) {
3322 return NS_ERROR_FAILURE;
3325 return LoadURI(loadState, true);
3328 NS_IMETHODIMP
3329 nsDocShell::FixupAndLoadURIStringFromScript(
3330 const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions,
3331 JSContext* aCx) {
3332 // generate dictionary for aLoadURIOptions and forward call
3333 LoadURIOptions loadURIOptions;
3334 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3335 return NS_ERROR_INVALID_ARG;
3337 return FixupAndLoadURIString(aURIString, loadURIOptions);
3340 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3341 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3342 // such as the content-chrome boundary, don't fire the error event.
3343 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3344 return;
3347 // If embedder is same-process, then unblocking the load event is already
3348 // handled by nsDocLoader. Fire the error event on our embedder element if
3349 // requested.
3351 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3352 // more like the remote case when in-process.
3353 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3354 if (element) {
3355 if (aFireFrameErrorEvent) {
3356 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3357 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3358 fl->FireErrorEvent();
3362 return;
3365 // If we have a cross-process parent document, we must notify it that we no
3366 // longer block its load event. This is necessary for OOP sub-documents
3367 // because error documents do not result in a call to
3368 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3369 // (Obviously, we must do this before any of the returns below.)
3370 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3371 if (browserChild &&
3372 !mBrowsingContext->GetParentWindowContext()->IsInProcess()) {
3373 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3374 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3375 : EmbedderElementEventType::NoEvent);
3379 NS_IMETHODIMP
3380 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3381 const char16_t* aURL, nsIChannel* aFailedChannel,
3382 bool* aDisplayedErrorPage) {
3383 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3384 ("DOCSHELL %p DisplayLoadError %s\n", this,
3385 aURI ? aURI->GetSpecOrDefault().get() : ""));
3387 *aDisplayedErrorPage = false;
3388 // Get prompt and string bundle services
3389 nsCOMPtr<nsIPrompt> prompter;
3390 nsCOMPtr<nsIStringBundle> stringBundle;
3391 GetPromptAndStringBundle(getter_AddRefs(prompter),
3392 getter_AddRefs(stringBundle));
3394 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3395 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3397 const char* error = nullptr;
3398 // The key used to select the appropriate error message from the properties
3399 // file.
3400 const char* errorDescriptionID = nullptr;
3401 AutoTArray<nsString, 3> formatStrs;
3402 bool addHostPort = false;
3403 bool isBadStsCertError = false;
3404 nsresult rv = NS_OK;
3405 nsAutoString messageStr;
3406 nsAutoCString cssClass;
3407 nsAutoCString errorPage;
3409 errorPage.AssignLiteral("neterror");
3411 // Turn the error code into a human readable error message.
3412 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3413 NS_ENSURE_ARG_POINTER(aURI);
3415 // Extract the schemes into a comma delimited list.
3416 nsAutoCString scheme;
3417 aURI->GetScheme(scheme);
3418 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3419 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3420 while (nestedURI) {
3421 nsCOMPtr<nsIURI> tempURI;
3422 nsresult rv2;
3423 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3424 if (NS_SUCCEEDED(rv2) && tempURI) {
3425 tempURI->GetScheme(scheme);
3426 formatStrs[0].AppendLiteral(", ");
3427 AppendASCIItoUTF16(scheme, formatStrs[0]);
3429 nestedURI = do_QueryInterface(tempURI);
3431 error = "unknownProtocolFound";
3432 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3433 NS_ENSURE_ARG_POINTER(aURI);
3434 error = "fileNotFound";
3435 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3436 NS_ENSURE_ARG_POINTER(aURI);
3437 error = "fileAccessDenied";
3438 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3439 NS_ENSURE_ARG_POINTER(aURI);
3440 // Get the host
3441 nsAutoCString host;
3442 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3443 innermostURI->GetHost(host);
3444 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3445 errorDescriptionID = "dnsNotFound2";
3446 error = "dnsNotFound";
3447 } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3448 NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3449 NS_ENSURE_ARG_POINTER(aURI);
3450 addHostPort = true;
3451 error = "connectionFailure";
3452 } else if (NS_ERROR_NET_INTERRUPT == aError) {
3453 NS_ENSURE_ARG_POINTER(aURI);
3454 addHostPort = true;
3455 error = "netInterrupt";
3456 } else if (NS_ERROR_NET_TIMEOUT == aError ||
3457 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
3458 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
3459 NS_ENSURE_ARG_POINTER(aURI);
3460 // Get the host
3461 nsAutoCString host;
3462 aURI->GetHost(host);
3463 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3464 error = "netTimeout";
3465 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3466 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
3467 // CSP error
3468 cssClass.AssignLiteral("neterror");
3469 error = "cspBlocked";
3470 } else if (NS_ERROR_XFO_VIOLATION == aError) {
3471 // XFO error
3472 cssClass.AssignLiteral("neterror");
3473 error = "xfoBlocked";
3474 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3475 nsCOMPtr<nsINSSErrorsService> nsserr =
3476 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3478 uint32_t errorClass;
3479 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3480 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3483 nsCOMPtr<nsITransportSecurityInfo> tsi;
3484 if (aFailedChannel) {
3485 aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
3487 if (tsi) {
3488 uint32_t securityState;
3489 tsi->GetSecurityState(&securityState);
3490 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3491 error = "sslv3Used";
3492 addHostPort = true;
3493 } else if (securityState &
3494 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3495 error = "weakCryptoUsed";
3496 addHostPort = true;
3498 } else {
3499 // No channel, let's obtain the generic error message
3500 if (nsserr) {
3501 nsserr->GetErrorMessage(aError, messageStr);
3504 // We don't have a message string here anymore but DisplayLoadError
3505 // requires a non-empty messageStr.
3506 messageStr.Truncate();
3507 messageStr.AssignLiteral(u" ");
3508 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3509 error = "nssBadCert";
3511 // If this is an HTTP Strict Transport Security host or a pinned host
3512 // and the certificate is bad, don't allow overrides (RFC 6797 section
3513 // 12.1).
3514 bool isStsHost = false;
3515 bool isPinnedHost = false;
3516 OriginAttributes attrsForHSTS;
3517 if (aFailedChannel) {
3518 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
3519 attrsForHSTS);
3520 } else {
3521 attrsForHSTS = GetOriginAttributes();
3524 if (XRE_IsParentProcess()) {
3525 nsCOMPtr<nsISiteSecurityService> sss =
3526 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3527 NS_ENSURE_SUCCESS(rv, rv);
3528 rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost);
3529 NS_ENSURE_SUCCESS(rv, rv);
3530 } else {
3531 mozilla::dom::ContentChild* cc =
3532 mozilla::dom::ContentChild::GetSingleton();
3533 cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost);
3535 nsCOMPtr<nsIPublicKeyPinningService> pkps =
3536 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
3537 NS_ENSURE_SUCCESS(rv, rv);
3538 rv = pkps->HostHasPins(aURI, &isPinnedHost);
3540 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3541 false)) {
3542 cssClass.AssignLiteral("expertBadCert");
3545 // HSTS/pinning takes precedence over the expert bad cert pref. We
3546 // never want to show the "Add Exception" button for these sites.
3547 // In the future we should differentiate between an HSTS host and a
3548 // pinned host and display a more informative message to the user.
3549 if (isStsHost || isPinnedHost) {
3550 isBadStsCertError = true;
3551 cssClass.AssignLiteral("badStsCert");
3554 errorPage.Assign("certerror");
3555 } else {
3556 error = "nssFailure2";
3558 } else if (NS_ERROR_PHISHING_URI == aError ||
3559 NS_ERROR_MALWARE_URI == aError ||
3560 NS_ERROR_UNWANTED_URI == aError ||
3561 NS_ERROR_HARMFUL_URI == aError) {
3562 nsAutoCString host;
3563 aURI->GetHost(host);
3564 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3566 // Malware and phishing detectors may want to use an alternate error
3567 // page, but if the pref's not set, we'll fall back on the standard page
3568 nsAutoCString alternateErrorPage;
3569 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3570 alternateErrorPage);
3571 if (NS_SUCCEEDED(rv)) {
3572 errorPage.Assign(alternateErrorPage);
3575 if (NS_ERROR_PHISHING_URI == aError) {
3576 error = "deceptiveBlocked";
3577 } else if (NS_ERROR_MALWARE_URI == aError) {
3578 error = "malwareBlocked";
3579 } else if (NS_ERROR_UNWANTED_URI == aError) {
3580 error = "unwantedBlocked";
3581 } else if (NS_ERROR_HARMFUL_URI == aError) {
3582 error = "harmfulBlocked";
3585 cssClass.AssignLiteral("blacklist");
3586 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3587 errorPage.AssignLiteral("tabcrashed");
3588 error = "tabcrashed";
3590 RefPtr<EventTarget> handler = mChromeEventHandler;
3591 if (handler) {
3592 nsCOMPtr<Element> element = do_QueryInterface(handler);
3593 element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
3596 // DisplayLoadError requires a non-empty messageStr to proceed and call
3597 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3598 // space which will be trimmed and thus treated as empty by the front-end.
3599 if (messageStr.IsEmpty()) {
3600 messageStr.AssignLiteral(u" ");
3602 } else if (NS_ERROR_FRAME_CRASHED == aError) {
3603 errorPage.AssignLiteral("framecrashed");
3604 error = "framecrashed";
3605 messageStr.AssignLiteral(u" ");
3606 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3607 errorPage.AssignLiteral("restartrequired");
3608 error = "restartrequired";
3610 // DisplayLoadError requires a non-empty messageStr to proceed and call
3611 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3612 // space which will be trimmed and thus treated as empty by the front-end.
3613 if (messageStr.IsEmpty()) {
3614 messageStr.AssignLiteral(u" ");
3616 } else {
3617 // Errors requiring simple formatting
3618 switch (aError) {
3619 case NS_ERROR_MALFORMED_URI:
3620 // URI is malformed
3621 error = "malformedURI";
3622 errorDescriptionID = "malformedURI2";
3623 break;
3624 case NS_ERROR_REDIRECT_LOOP:
3625 // Doc failed to load because the server generated too many redirects
3626 error = "redirectLoop";
3627 break;
3628 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3629 // Doc failed to load because PSM is not installed
3630 error = "unknownSocketType";
3631 break;
3632 case NS_ERROR_NET_RESET:
3633 // Doc failed to load because the server kept reseting the connection
3634 // before we could read any data from it
3635 error = "netReset";
3636 break;
3637 case NS_ERROR_DOCUMENT_NOT_CACHED:
3638 // Doc failed to load because the cache does not contain a copy of
3639 // the document.
3640 error = "notCached";
3641 break;
3642 case NS_ERROR_OFFLINE:
3643 // Doc failed to load because we are offline.
3644 error = "netOffline";
3645 break;
3646 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3647 // Doc navigation attempted while Printing or Print Preview
3648 error = "isprinting";
3649 break;
3650 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3651 // Port blocked for security reasons
3652 addHostPort = true;
3653 error = "deniedPortAccess";
3654 break;
3655 case NS_ERROR_UNKNOWN_PROXY_HOST:
3656 // Proxy hostname could not be resolved.
3657 error = "proxyResolveFailure";
3658 break;
3659 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3660 case NS_ERROR_PROXY_FORBIDDEN:
3661 case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3662 case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3663 case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3664 // Proxy connection was refused.
3665 error = "proxyConnectFailure";
3666 break;
3667 case NS_ERROR_INVALID_CONTENT_ENCODING:
3668 // Bad Content Encoding.
3669 error = "contentEncodingError";
3670 break;
3671 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3672 // Channel refused to load from an unrecognized content type.
3673 error = "unsafeContentType";
3674 break;
3675 case NS_ERROR_CORRUPTED_CONTENT:
3676 // Broken Content Detected. e.g. Content-MD5 check failure.
3677 error = "corruptedContentErrorv2";
3678 break;
3679 case NS_ERROR_INTERCEPTION_FAILED:
3680 // ServiceWorker intercepted request, but something went wrong.
3681 error = "corruptedContentErrorv2";
3682 break;
3683 case NS_ERROR_NET_INADEQUATE_SECURITY:
3684 // Server negotiated bad TLS for HTTP/2.
3685 error = "inadequateSecurityError";
3686 addHostPort = true;
3687 break;
3688 case NS_ERROR_BLOCKED_BY_POLICY:
3689 case NS_ERROR_DOM_COOP_FAILED:
3690 case NS_ERROR_DOM_COEP_FAILED:
3691 // Page blocked by policy
3692 error = "blockedByPolicy";
3693 break;
3694 case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3695 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3696 // HTTP/2 or HTTP/3 stack detected a protocol error
3697 error = "networkProtocolError";
3698 break;
3700 default:
3701 break;
3705 nsresult delegateErrorCode = aError;
3706 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3707 // caused this error, we replace the error-page with about:httpsonlyerror
3708 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
3709 errorPage.AssignLiteral("httpsonlyerror");
3710 delegateErrorCode = NS_ERROR_HTTPS_ONLY;
3711 } else if (isBadStsCertError) {
3712 delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
3715 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3716 nsCOMPtr<nsIURI> errorPageURI;
3717 rv = loadURIDelegate->HandleLoadError(
3718 aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
3719 getter_AddRefs(errorPageURI));
3720 // If the docshell is going away there's no point in showing an error page.
3721 if (NS_FAILED(rv) || mIsBeingDestroyed) {
3722 *aDisplayedErrorPage = false;
3723 return NS_OK;
3726 if (errorPageURI) {
3727 *aDisplayedErrorPage =
3728 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3729 return NS_OK;
3733 // Test if the error should be displayed
3734 if (!error) {
3735 return NS_OK;
3738 if (!errorDescriptionID) {
3739 errorDescriptionID = error;
3742 Telemetry::AccumulateCategoricalKeyed(
3743 IsSubframe() ? "frame"_ns : "top"_ns,
3744 mozilla::dom::LoadErrorToTelemetryLabel(aError));
3746 // Test if the error needs to be formatted
3747 if (!messageStr.IsEmpty()) {
3748 // already obtained message
3749 } else {
3750 if (addHostPort) {
3751 // Build up the host:port string.
3752 nsAutoCString hostport;
3753 if (aURI) {
3754 aURI->GetHostPort(hostport);
3755 } else {
3756 hostport.Assign('?');
3758 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3761 nsAutoCString spec;
3762 rv = NS_ERROR_NOT_AVAILABLE;
3763 auto& nextFormatStr = *formatStrs.AppendElement();
3764 if (aURI) {
3765 // displaying "file://" is aesthetically unpleasing and could even be
3766 // confusing to the user
3767 if (SchemeIsFile(aURI)) {
3768 aURI->GetPathQueryRef(spec);
3769 } else {
3770 aURI->GetSpec(spec);
3773 nsCOMPtr<nsITextToSubURI> textToSubURI(
3774 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3775 if (NS_SUCCEEDED(rv)) {
3776 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3778 } else {
3779 spec.Assign('?');
3781 if (NS_FAILED(rv)) {
3782 CopyUTF8toUTF16(spec, nextFormatStr);
3784 rv = NS_OK;
3786 nsAutoString str;
3787 rv =
3788 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3789 NS_ENSURE_SUCCESS(rv, rv);
3790 messageStr.Assign(str);
3793 // Display the error as a page or an alert prompt
3794 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3796 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3797 SchemeIsHTTPS(aURI)) {
3798 // Maybe TLS intolerant. Treat this as an SSL error.
3799 error = "nssFailure2";
3802 if (mBrowsingContext->GetUseErrorPages()) {
3803 // Display an error page
3804 nsresult loadedPage =
3805 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3806 cssClass.get(), aFailedChannel);
3807 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3808 } else {
3809 // The prompter reqires that our private window has a document (or it
3810 // asserts). Satisfy that assertion now since GetDoc will force
3811 // creation of one if it hasn't already been created.
3812 if (mScriptGlobal) {
3813 Unused << mScriptGlobal->GetDoc();
3816 // Display a message box
3817 prompter->Alert(nullptr, messageStr.get());
3820 return NS_OK;
3823 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3825 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3826 const char* aErrorPage,
3827 const char* aErrorType,
3828 const char16_t* aDescription,
3829 const char* aCSSClass,
3830 nsIChannel* aFailedChannel) {
3831 if (mIsBeingDestroyed) {
3832 return NS_ERROR_NOT_AVAILABLE;
3835 #if defined(DEBUG)
3836 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3837 nsAutoCString chanName;
3838 if (aFailedChannel) {
3839 aFailedChannel->GetName(chanName);
3840 } else {
3841 chanName.AssignLiteral("<no channel>");
3844 MOZ_LOG(gDocShellLog, LogLevel::Debug,
3845 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3846 this, aURI ? aURI->GetSpecOrDefault().get() : "",
3847 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3849 #endif
3851 nsAutoCString url;
3852 if (aURI) {
3853 nsresult rv = aURI->GetSpec(url);
3854 NS_ENSURE_SUCCESS(rv, rv);
3855 } else if (aURL) {
3856 CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3857 } else {
3858 return NS_ERROR_INVALID_POINTER;
3861 // Create a URL to pass all the error information through to the page.
3863 #undef SAFE_ESCAPE
3864 #define SAFE_ESCAPE(output, input, params) \
3865 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3866 return NS_ERROR_OUT_OF_MEMORY; \
3869 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3870 SAFE_ESCAPE(escapedUrl, url, url_Path);
3871 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3872 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3873 url_Path);
3874 if (aCSSClass) {
3875 nsCString cssClass(aCSSClass);
3876 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3878 nsCString errorPageUrl("about:");
3879 errorPageUrl.AppendASCII(aErrorPage);
3880 errorPageUrl.AppendLiteral("?e=");
3882 errorPageUrl.AppendASCII(escapedError.get());
3883 errorPageUrl.AppendLiteral("&u=");
3884 errorPageUrl.AppendASCII(escapedUrl.get());
3885 if ((strcmp(aErrorPage, "blocked") == 0) &&
3886 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
3887 errorPageUrl.AppendLiteral("&o=1");
3889 if (!escapedCSSClass.IsEmpty()) {
3890 errorPageUrl.AppendLiteral("&s=");
3891 errorPageUrl.AppendASCII(escapedCSSClass.get());
3893 errorPageUrl.AppendLiteral("&c=UTF-8");
3895 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
3896 int32_t cpsState;
3897 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
3898 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
3899 errorPageUrl.AppendLiteral("&captive=true");
3902 errorPageUrl.AppendLiteral("&d=");
3903 errorPageUrl.AppendASCII(escapedDescription.get());
3905 nsCOMPtr<nsIURI> errorPageURI;
3906 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3907 NS_ENSURE_SUCCESS(rv, rv);
3909 return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
3912 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
3913 nsIChannel* aFailedChannel) {
3914 mFailedChannel = aFailedChannel;
3915 mFailedURI = aFailedURI;
3916 mFailedLoadType = mLoadType;
3918 if (mLSHE) {
3919 // Abandon mLSHE's BFCache entry and create a new one. This way, if
3920 // we go back or forward to another SHEntry with the same doc
3921 // identifier, the error page won't persist.
3922 mLSHE->AbandonBFCacheEntry();
3925 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
3926 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
3927 if (mBrowsingContext) {
3928 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
3929 loadState->SetTriggeringWindowId(
3930 mBrowsingContext->GetCurrentInnerWindowId());
3931 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
3932 if (innerWin) {
3933 loadState->SetTriggeringStorageAccess(innerWin->UsingStorageAccess());
3936 loadState->SetLoadType(LOAD_ERROR_PAGE);
3937 loadState->SetFirstParty(true);
3938 loadState->SetSourceBrowsingContext(mBrowsingContext);
3939 if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
3940 // We keep the loading entry for the load that failed here. If the user
3941 // reloads we want to try to reload the original load, not the error page.
3942 loadState->SetLoadingSessionHistoryInfo(
3943 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
3945 return InternalLoad(loadState);
3948 NS_IMETHODIMP
3949 nsDocShell::Reload(uint32_t aReloadFlags) {
3950 if (!IsNavigationAllowed()) {
3951 return NS_OK; // JS may not handle returning of an error code
3954 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
3955 "Reload command not updated to use load flags!");
3956 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
3957 "Don't pass these flags to Reload");
3959 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3960 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3962 // Send notifications to the HistoryListener if any, about the impending
3963 // reload
3964 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3965 if (mozilla::SessionHistoryInParent()) {
3966 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
3967 bool forceReload = IsForceReloadType(loadType);
3968 if (!XRE_IsParentProcess()) {
3969 ++mPendingReloadCount;
3970 RefPtr<nsDocShell> docShell(this);
3971 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
3972 NS_ENSURE_STATE(viewer);
3974 bool okToUnload = true;
3975 MOZ_TRY(viewer->PermitUnload(&okToUnload));
3976 if (!okToUnload) {
3977 return NS_OK;
3980 RefPtr<Document> doc(GetDocument());
3981 RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
3982 nsCOMPtr<nsIURI> currentURI(mCurrentURI);
3983 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
3984 RefPtr<StopDetector> stopDetector = new StopDetector();
3985 nsCOMPtr<nsILoadGroup> loadGroup;
3986 GetLoadGroup(getter_AddRefs(loadGroup));
3987 if (loadGroup) {
3988 // loadGroup may be null in theory. In that case stopDetector just
3989 // doesn't do anything.
3990 loadGroup->AddRequest(stopDetector, nullptr);
3993 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
3994 mBrowsingContext, forceReload,
3995 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo,
3996 loadGroup, stopDetector](
3997 std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>,
3998 Maybe<bool>>&& aResult) {
3999 auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() {
4000 if (loadGroup) {
4001 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
4005 // Decrease mPendingReloadCount before any other early returns!
4006 if (--(docShell->mPendingReloadCount) > 0) {
4007 return;
4010 if (stopDetector->Canceled()) {
4011 return;
4013 bool canReload;
4014 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4015 Maybe<bool> reloadingActiveEntry;
4017 std::tie(canReload, loadState, reloadingActiveEntry) = aResult;
4019 if (!canReload) {
4020 return;
4023 if (loadState.isSome()) {
4024 MOZ_LOG(
4025 gSHLog, LogLevel::Debug,
4026 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
4027 loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
4028 docShell->LoadHistoryEntry(loadState.ref(), loadType,
4029 reloadingActiveEntry.ref());
4030 } else {
4031 MOZ_LOG(gSHLog, LogLevel::Debug,
4032 ("nsDocShell %p ReloadDocument", docShell.get()));
4033 ReloadDocument(docShell, doc, loadType, browsingContext,
4034 currentURI, referrerInfo,
4035 /* aNotifiedBeforeUnloadListeners */ true);
4038 [](mozilla::ipc::ResponseRejectReason) {});
4039 } else {
4040 // Parent process
4041 bool canReload = false;
4042 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4043 Maybe<bool> reloadingActiveEntry;
4044 if (!mBrowsingContext->IsDiscarded()) {
4045 mBrowsingContext->Canonical()->NotifyOnHistoryReload(
4046 forceReload, canReload, loadState, reloadingActiveEntry);
4048 if (canReload) {
4049 if (loadState.isSome()) {
4050 MOZ_LOG(gSHLog, LogLevel::Debug,
4051 ("nsDocShell %p Reload - LoadHistoryEntry", this));
4052 LoadHistoryEntry(loadState.ref(), loadType,
4053 reloadingActiveEntry.ref());
4054 } else {
4055 MOZ_LOG(gSHLog, LogLevel::Debug,
4056 ("nsDocShell %p ReloadDocument", this));
4057 RefPtr<Document> doc = GetDocument();
4058 RefPtr<BrowsingContext> bc = mBrowsingContext;
4059 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4060 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4061 ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4065 return NS_OK;
4068 bool canReload = true;
4069 if (rootSH) {
4070 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
4073 if (!canReload) {
4074 return NS_OK;
4077 /* If you change this part of code, make sure bug 45297 does not re-occur */
4078 if (mOSHE) {
4079 nsCOMPtr<nsISHEntry> oshe = mOSHE;
4080 return LoadHistoryEntry(
4081 oshe, loadType,
4082 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4085 if (mLSHE) { // In case a reload happened before the current load is done
4086 nsCOMPtr<nsISHEntry> lshe = mLSHE;
4087 return LoadHistoryEntry(
4088 lshe, loadType,
4089 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4092 RefPtr<Document> doc = GetDocument();
4093 RefPtr<BrowsingContext> bc = mBrowsingContext;
4094 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4095 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4096 return ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4099 /* static */
4100 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
4101 uint32_t aLoadType,
4102 BrowsingContext* aBrowsingContext,
4103 nsIURI* aCurrentURI,
4104 nsIReferrerInfo* aReferrerInfo,
4105 bool aNotifiedBeforeUnloadListeners) {
4106 if (!aDocument) {
4107 return NS_OK;
4110 // Do not inherit owner from document
4111 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4112 nsAutoString srcdoc;
4113 nsIURI* baseURI = nullptr;
4114 nsCOMPtr<nsIURI> originalURI;
4115 nsCOMPtr<nsIURI> resultPrincipalURI;
4116 bool loadReplace = false;
4118 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
4119 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
4120 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
4121 uint64_t triggeringWindowId = aDocument->InnerWindowID();
4122 bool triggeringStorageAccess = aDocument->UsingStorageAccess();
4124 nsAutoString contentTypeHint;
4125 aDocument->GetContentType(contentTypeHint);
4127 if (aDocument->IsSrcdocDocument()) {
4128 aDocument->GetSrcdocData(srcdoc);
4129 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4130 baseURI = aDocument->GetBaseURI();
4131 } else {
4132 srcdoc = VoidString();
4134 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
4135 if (chan) {
4136 uint32_t loadFlags;
4137 chan->GetLoadFlags(&loadFlags);
4138 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4139 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4140 if (httpChan) {
4141 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4144 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
4145 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4148 if (!triggeringPrincipal) {
4149 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4150 return NS_ERROR_FAILURE;
4153 // Stack variables to ensure changes to the member variables don't affect to
4154 // the call.
4155 nsCOMPtr<nsIURI> currentURI = aCurrentURI;
4157 // Reload always rewrites result principal URI.
4158 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4159 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4161 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
4162 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
4163 loadState->SetReferrerInfo(aReferrerInfo);
4164 loadState->SetOriginalURI(originalURI);
4165 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
4166 loadState->SetLoadReplace(loadReplace);
4167 loadState->SetTriggeringPrincipal(triggeringPrincipal);
4168 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
4169 loadState->SetTriggeringWindowId(triggeringWindowId);
4170 loadState->SetTriggeringStorageAccess(triggeringStorageAccess);
4171 loadState->SetPrincipalToInherit(triggeringPrincipal);
4172 loadState->SetCsp(csp);
4173 loadState->SetInternalLoadFlags(flags);
4174 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
4175 loadState->SetLoadType(aLoadType);
4176 loadState->SetFirstParty(true);
4177 loadState->SetSrcdocData(srcdoc);
4178 loadState->SetSourceBrowsingContext(aBrowsingContext);
4179 loadState->SetBaseURI(baseURI);
4180 loadState->SetHasValidUserGestureActivation(
4181 context && context->HasValidTransientUserGestureActivation());
4182 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
4183 return aDocShell->InternalLoad(loadState);
4186 NS_IMETHODIMP
4187 nsDocShell::Stop(uint32_t aStopFlags) {
4188 // Revoke any pending event related to content viewer restoration
4189 mRestorePresentationEvent.Revoke();
4191 if (mLoadType == LOAD_ERROR_PAGE) {
4192 if (mLSHE) {
4193 // Since error page loads never unset mLSHE, do so now
4194 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
4196 mActiveEntryIsLoadingFromSessionHistory = false;
4198 mFailedChannel = nullptr;
4199 mFailedURI = nullptr;
4202 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4203 // Stop the document loading and animations
4204 if (mDocumentViewer) {
4205 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4206 viewer->Stop();
4208 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4209 // Stop the document loading only
4210 if (mDocumentViewer) {
4211 RefPtr<Document> doc = mDocumentViewer->GetDocument();
4212 if (doc) {
4213 doc->StopDocumentLoad();
4218 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4219 // Suspend any timers that were set for this loader. We'll clear
4220 // them out for good in CreateDocumentViewer.
4221 if (mRefreshURIList) {
4222 SuspendRefreshURIs();
4223 mSavedRefreshURIList.swap(mRefreshURIList);
4224 mRefreshURIList = nullptr;
4227 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4228 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4229 // redundant apis!
4230 Stop();
4232 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4233 // BFCache now, and the Stop above will have removed the DocumentChannel
4234 // from the loadgroup.
4235 mChannelToDisconnectOnPageHide = 0;
4238 for (auto* child : mChildList.ForwardRange()) {
4239 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
4240 if (shellAsNav) {
4241 shellAsNav->Stop(aStopFlags);
4245 return NS_OK;
4248 NS_IMETHODIMP
4249 nsDocShell::GetDocument(Document** aDocument) {
4250 NS_ENSURE_ARG_POINTER(aDocument);
4251 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), NS_ERROR_FAILURE);
4253 RefPtr<Document> doc = mDocumentViewer->GetDocument();
4254 if (!doc) {
4255 return NS_ERROR_NOT_AVAILABLE;
4258 doc.forget(aDocument);
4259 return NS_OK;
4262 NS_IMETHODIMP
4263 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4264 NS_ENSURE_ARG_POINTER(aURI);
4266 nsCOMPtr<nsIURI> uri = mCurrentURI;
4267 uri.forget(aURI);
4268 return NS_OK;
4271 NS_IMETHODIMP
4272 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4273 NS_ENSURE_ARG_POINTER(aSessionHistory);
4274 RefPtr<ChildSHistory> shistory = GetSessionHistory();
4275 shistory.forget(aSessionHistory);
4276 return NS_OK;
4279 //*****************************************************************************
4280 // nsDocShell::nsIWebPageDescriptor
4281 //*****************************************************************************
4283 NS_IMETHODIMP
4284 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
4285 const nsAString& aURI) {
4286 if (!aOtherDocShell) {
4287 return NS_ERROR_INVALID_POINTER;
4289 nsCOMPtr<nsIURI> newURI;
4290 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
4291 if (NS_FAILED(rv)) {
4292 return rv;
4295 RefPtr<nsDocShellLoadState> loadState;
4296 uint32_t cacheKey;
4297 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
4298 if (mozilla::SessionHistoryInParent()) {
4299 loadState = new nsDocShellLoadState(newURI);
4300 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
4301 return NS_ERROR_INVALID_POINTER;
4303 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
4304 } else {
4305 nsCOMPtr<nsISHEntry> entry;
4306 bool isOriginalSHE;
4307 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
4308 if (!entry) {
4309 return NS_ERROR_INVALID_POINTER;
4311 rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
4312 NS_ENSURE_SUCCESS(rv, rv);
4313 entry->GetCacheKey(&cacheKey);
4314 loadState->SetURI(newURI);
4315 loadState->SetSHEntry(nullptr);
4318 // We're doing a load of the page, via an API that
4319 // is only exposed to system code. The triggering principal for this load
4320 // should be the system principal.
4321 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4322 loadState->SetOriginalURI(nullptr);
4323 loadState->SetResultPrincipalURI(nullptr);
4325 return InternalLoad(loadState, Some(cacheKey));
4328 NS_IMETHODIMP
4329 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4330 MOZ_ASSERT(aPageDescriptor, "Null out param?");
4332 *aPageDescriptor = nullptr;
4334 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4335 if (src) {
4336 nsCOMPtr<nsISHEntry> dest;
4338 nsresult rv = src->Clone(getter_AddRefs(dest));
4339 if (NS_FAILED(rv)) {
4340 return rv;
4343 // null out inappropriate cloned attributes...
4344 dest->SetParent(nullptr);
4345 dest->SetIsSubFrame(false);
4347 return CallQueryInterface(dest, aPageDescriptor);
4350 return NS_ERROR_NOT_AVAILABLE;
4353 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
4354 const {
4355 nsCOMPtr<nsIInputStream> postData;
4356 if (mozilla::SessionHistoryInParent()) {
4357 if (mActiveEntry) {
4358 postData = mActiveEntry->GetPostData();
4359 } else if (mLoadingEntry) {
4360 postData = mLoadingEntry->mInfo.GetPostData();
4362 } else {
4363 if (mOSHE) {
4364 postData = mOSHE->GetPostData();
4365 } else if (mLSHE) {
4366 postData = mLSHE->GetPostData();
4370 return postData.forget();
4373 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4374 if (mozilla::SessionHistoryInParent()) {
4375 if (mActiveEntry) {
4376 return Some(mActiveEntry->GetCacheKey());
4379 if (mLoadingEntry) {
4380 return Some(mLoadingEntry->mInfo.GetCacheKey());
4382 } else {
4383 if (mOSHE) {
4384 return Some(mOSHE->GetCacheKey());
4387 if (mLSHE) {
4388 return Some(mLSHE->GetCacheKey());
4392 return Nothing();
4395 bool nsDocShell::FillLoadStateFromCurrentEntry(
4396 nsDocShellLoadState& aLoadState) {
4397 if (mLoadingEntry) {
4398 mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
4399 return true;
4401 if (mActiveEntry) {
4402 mActiveEntry->FillLoadInfo(aLoadState);
4403 return true;
4405 return false;
4408 //*****************************************************************************
4409 // nsDocShell::nsIBaseWindow
4410 //*****************************************************************************
4412 NS_IMETHODIMP
4413 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4414 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4415 int32_t aWidth, int32_t aHeight) {
4416 SetParentWidget(aParentWidget);
4417 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4418 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE);
4420 return NS_OK;
4423 NS_IMETHODIMP
4424 nsDocShell::Destroy() {
4425 // XXX: We allow this function to be called just once. If you are going to
4426 // reset new variables in this function, please make sure the variables will
4427 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4428 // in the setter functions for the variables would be enough.
4429 if (mIsBeingDestroyed) {
4430 return NS_ERROR_DOCSHELL_DYING;
4433 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4434 "Unexpected item type in docshell");
4436 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4437 if (serv) {
4438 const char* msg = mItemType == typeContent
4439 ? NS_WEBNAVIGATION_DESTROY
4440 : NS_CHROME_WEBNAVIGATION_DESTROY;
4441 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4444 mIsBeingDestroyed = true;
4446 // Brak the cycle with the initial client, if present.
4447 mInitialClientSource.reset();
4449 // Make sure to blow away our mLoadingURI just in case. No loads
4450 // from inside this pagehide.
4451 mLoadingURI = nullptr;
4453 // Fire unload event before we blow anything away.
4454 (void)FirePageHideNotification(true);
4456 // Clear pointers to any detached nsEditorData that's lying
4457 // around in shistory entries. Breaks cycle. See bug 430921.
4458 if (mOSHE) {
4459 mOSHE->SetEditorData(nullptr);
4461 if (mLSHE) {
4462 mLSHE->SetEditorData(nullptr);
4465 // Note: mContentListener can be null if Init() failed and we're being
4466 // called from the destructor.
4467 if (mContentListener) {
4468 mContentListener->DropDocShellReference();
4469 mContentListener->SetParentContentListener(nullptr);
4470 // Note that we do NOT set mContentListener to null here; that
4471 // way if someone tries to do a load in us after this point
4472 // the nsDSURIContentListener will block it. All of which
4473 // means that we should do this before calling Stop(), of
4474 // course.
4477 // Stop any URLs that are currently being loaded...
4478 Stop(nsIWebNavigation::STOP_ALL);
4480 mEditorData = nullptr;
4482 // Save the state of the current document, before destroying the window.
4483 // This is needed to capture the state of a frameset when the new document
4484 // causes the frameset to be destroyed...
4485 PersistLayoutHistoryState();
4487 // Remove this docshell from its parent's child list
4488 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4489 do_QueryInterface(GetAsSupports(mParent));
4490 if (docShellParentAsItem) {
4491 docShellParentAsItem->RemoveChild(this);
4494 if (mDocumentViewer) {
4495 mDocumentViewer->Close(nullptr);
4496 mDocumentViewer->Destroy();
4497 mDocumentViewer = nullptr;
4500 nsDocLoader::Destroy();
4502 mParentWidget = nullptr;
4503 SetCurrentURIInternal(nullptr);
4505 if (mScriptGlobal) {
4506 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4507 mScriptGlobal = nullptr;
4510 if (GetSessionHistory()) {
4511 // We want to destroy these content viewers now rather than
4512 // letting their destruction wait for the session history
4513 // entries to get garbage collected. (Bug 488394)
4514 GetSessionHistory()->EvictLocalDocumentViewers();
4517 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
4518 mBrowsingContext->PrepareForProcessChange();
4521 SetTreeOwner(nullptr);
4523 mBrowserChild = nullptr;
4525 mChromeEventHandler = nullptr;
4527 // Cancel any timers that were set for this docshell; this is needed
4528 // to break the cycle between us and the timers.
4529 CancelRefreshURITimers();
4531 return NS_OK;
4534 double nsDocShell::GetWidgetCSSToDeviceScale() {
4535 if (mParentWidget) {
4536 return mParentWidget->GetDefaultScale().scale;
4538 if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) {
4539 return ownerWindow->GetWidgetCSSToDeviceScale();
4541 return 1.0;
4544 NS_IMETHODIMP
4545 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4546 if (mParentWidget) {
4547 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4548 return NS_OK;
4551 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4552 if (ownerWindow) {
4553 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4556 *aScale = 1.0;
4557 return NS_OK;
4560 NS_IMETHODIMP
4561 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4562 mBounds.MoveTo(aX, aY);
4564 if (mDocumentViewer) {
4565 NS_ENSURE_SUCCESS(mDocumentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4568 return NS_OK;
4571 NS_IMETHODIMP
4572 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4573 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4574 if (ownerWindow) {
4575 return ownerWindow->SetPositionDesktopPix(aX, aY);
4578 double scale = 1.0;
4579 GetDevicePixelsPerDesktopPixel(&scale);
4580 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4583 NS_IMETHODIMP
4584 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4585 return GetPositionAndSize(aX, aY, nullptr, nullptr);
4588 NS_IMETHODIMP
4589 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4590 int32_t x = 0, y = 0;
4591 GetPosition(&x, &y);
4592 return SetPositionAndSize(x, y, aWidth, aHeight,
4593 aRepaint ? nsIBaseWindow::eRepaint : 0);
4596 NS_IMETHODIMP
4597 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4598 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4601 NS_IMETHODIMP
4602 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4603 int32_t aHeight, uint32_t aFlags) {
4604 mBounds.SetRect(aX, aY, aWidth, aHeight);
4606 // Hold strong ref, since SetBounds can make us null out mDocumentViewer
4607 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4608 if (viewer) {
4609 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4610 ? nsIDocumentViewer::eDelayResize
4611 : 0;
4612 // XXX Border figured in here or is that handled elsewhere?
4613 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4614 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4617 return NS_OK;
4620 NS_IMETHODIMP
4621 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4622 int32_t* aHeight) {
4623 if (mParentWidget) {
4624 // ensure size is up-to-date if window has changed resolution
4625 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4626 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4629 // We should really consider just getting this information from
4630 // our window instead of duplicating the storage and code...
4631 if (aWidth || aHeight) {
4632 // Caller wants to know our size; make sure to give them up to
4633 // date information.
4634 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4635 if (doc) {
4636 doc->FlushPendingNotifications(FlushType::Layout);
4640 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4641 return NS_OK;
4644 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4645 int32_t* aHeight) {
4646 if (aX) {
4647 *aX = mBounds.X();
4649 if (aY) {
4650 *aY = mBounds.Y();
4652 if (aWidth) {
4653 *aWidth = mBounds.Width();
4655 if (aHeight) {
4656 *aHeight = mBounds.Height();
4660 NS_IMETHODIMP
4661 nsDocShell::SetDimensions(DimensionRequest&& aRequest) {
4662 return NS_ERROR_NOT_IMPLEMENTED;
4665 NS_IMETHODIMP
4666 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
4667 int32_t* aY, int32_t* aCX, int32_t* aCY) {
4668 return NS_ERROR_NOT_IMPLEMENTED;
4671 NS_IMETHODIMP
4672 nsDocShell::Repaint(bool aForce) {
4673 PresShell* presShell = GetPresShell();
4674 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4676 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4677 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4679 viewManager->InvalidateAllViews();
4680 return NS_OK;
4683 NS_IMETHODIMP
4684 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4685 NS_ENSURE_ARG_POINTER(aParentWidget);
4687 *aParentWidget = mParentWidget;
4688 NS_IF_ADDREF(*aParentWidget);
4690 return NS_OK;
4693 NS_IMETHODIMP
4694 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4695 MOZ_ASSERT(!mIsBeingDestroyed);
4696 mParentWidget = aParentWidget;
4698 return NS_OK;
4701 NS_IMETHODIMP
4702 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4703 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4705 if (mParentWidget) {
4706 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4707 } else {
4708 *aParentNativeWindow = nullptr;
4711 return NS_OK;
4714 NS_IMETHODIMP
4715 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4716 return NS_ERROR_NOT_IMPLEMENTED;
4719 NS_IMETHODIMP
4720 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4721 // the nativeHandle should be accessed from nsIAppWindow
4722 return NS_ERROR_NOT_IMPLEMENTED;
4725 NS_IMETHODIMP
4726 nsDocShell::GetVisibility(bool* aVisibility) {
4727 NS_ENSURE_ARG_POINTER(aVisibility);
4729 *aVisibility = false;
4731 if (!mDocumentViewer) {
4732 return NS_OK;
4735 PresShell* presShell = GetPresShell();
4736 if (!presShell) {
4737 return NS_OK;
4740 // get the view manager
4741 nsViewManager* vm = presShell->GetViewManager();
4742 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4744 // get the root view
4745 nsView* view = vm->GetRootView(); // views are not ref counted
4746 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4748 // if our root view is hidden, we are not visible
4749 if (view->GetVisibility() == ViewVisibility::Hide) {
4750 return NS_OK;
4753 // otherwise, we must walk up the document and view trees checking
4754 // for a hidden view, unless we're an off screen browser, which
4755 // would make this test meaningless.
4757 RefPtr<nsDocShell> docShell = this;
4758 RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4759 while (parentItem) {
4760 // Null-check for crash in bug 267804
4761 if (!parentItem->GetPresShell()) {
4762 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4763 return NS_OK;
4766 vm = docShell->GetPresShell()->GetViewManager();
4767 if (vm) {
4768 view = vm->GetRootView();
4771 if (view) {
4772 view = view->GetParent(); // anonymous inner view
4773 if (view) {
4774 view = view->GetParent(); // subdocumentframe's view
4778 nsIFrame* frame = view ? view->GetFrame() : nullptr;
4779 if (frame && !frame->IsVisibleConsideringAncestors(
4780 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
4781 return NS_OK;
4784 docShell = parentItem;
4785 parentItem = docShell->GetInProcessParentDocshell();
4788 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4789 if (!treeOwnerAsWin) {
4790 *aVisibility = true;
4791 return NS_OK;
4794 // Check with the tree owner as well to give embedders a chance to
4795 // expose visibility as well.
4796 nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility);
4797 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
4798 // The tree owner had no opinion on our visibility.
4799 *aVisibility = true;
4800 return NS_OK;
4802 return rv;
4805 void nsDocShell::ActivenessMaybeChanged() {
4806 const bool isActive = mBrowsingContext->IsActive();
4807 if (RefPtr<PresShell> presShell = GetPresShell()) {
4808 presShell->ActivenessMaybeChanged();
4811 // Tell the window about it
4812 if (mScriptGlobal) {
4813 mScriptGlobal->SetIsBackground(!isActive);
4814 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4815 // Update orientation when the top-level browsing context becomes active.
4816 if (isActive && mBrowsingContext->IsTop()) {
4817 // We only care about the top-level browsing context.
4818 auto orientation = mBrowsingContext->GetOrientationLock();
4819 ScreenOrientation::UpdateActiveOrientationLock(orientation);
4822 doc->PostVisibilityUpdateEvent();
4826 // Tell the nsDOMNavigationTiming about it
4827 RefPtr<nsDOMNavigationTiming> timing = mTiming;
4828 if (!timing && mDocumentViewer) {
4829 if (Document* doc = mDocumentViewer->GetDocument()) {
4830 timing = doc->GetNavigationTiming();
4833 if (timing) {
4834 timing->NotifyDocShellStateChanged(
4835 isActive ? nsDOMNavigationTiming::DocShellState::eActive
4836 : nsDOMNavigationTiming::DocShellState::eInactive);
4839 // Restart or stop meta refresh timers if necessary
4840 if (mDisableMetaRefreshWhenInactive) {
4841 if (isActive) {
4842 ResumeRefreshURIs();
4843 } else {
4844 SuspendRefreshURIs();
4848 if (InputTaskManager::CanSuspendInputEvent()) {
4849 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
4853 NS_IMETHODIMP
4854 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4855 if (!mWillChangeProcess) {
4856 // Intentionally ignoring handling discarded browsing contexts.
4857 Unused << mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4858 } else {
4859 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4860 // shutdown. Sorry DevTools, your DocShell is in another process.
4861 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4863 return NS_OK;
4866 NS_IMETHODIMP
4867 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4868 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4869 return NS_OK;
4872 NS_IMETHODIMP
4873 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4874 NS_ENSURE_ARG_POINTER(aFailedChannel);
4875 Document* doc = GetDocument();
4876 if (!doc) {
4877 *aFailedChannel = nullptr;
4878 return NS_OK;
4880 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4881 return NS_OK;
4884 NS_IMETHODIMP
4885 nsDocShell::SetVisibility(bool aVisibility) {
4886 // Show()/Hide() may change mDocumentViewer.
4887 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4888 if (!viewer) {
4889 return NS_OK;
4891 if (aVisibility) {
4892 viewer->Show();
4893 } else {
4894 viewer->Hide();
4897 return NS_OK;
4900 NS_IMETHODIMP
4901 nsDocShell::GetEnabled(bool* aEnabled) {
4902 NS_ENSURE_ARG_POINTER(aEnabled);
4903 *aEnabled = true;
4904 return NS_ERROR_NOT_IMPLEMENTED;
4907 NS_IMETHODIMP
4908 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4910 NS_IMETHODIMP
4911 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4912 // We don't create our own widget, so simply return the parent one.
4913 return GetParentWidget(aMainWidget);
4916 NS_IMETHODIMP
4917 nsDocShell::GetTitle(nsAString& aTitle) {
4918 aTitle = mTitle;
4919 return NS_OK;
4922 NS_IMETHODIMP
4923 nsDocShell::SetTitle(const nsAString& aTitle) {
4924 // Avoid unnecessary updates of the title if the URI and the title haven't
4925 // changed.
4926 if (mTitleValidForCurrentURI && mTitle == aTitle) {
4927 return NS_OK;
4930 // Store local title
4931 mTitle = aTitle;
4932 mTitleValidForCurrentURI = true;
4934 // When title is set on the top object it should then be passed to the
4935 // tree owner.
4936 if (mBrowsingContext->IsTop()) {
4937 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4938 if (treeOwnerAsWin) {
4939 treeOwnerAsWin->SetTitle(aTitle);
4943 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4944 UpdateGlobalHistoryTitle(mCurrentURI);
4947 // Update SessionHistory with the document's title.
4948 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
4949 SetTitleOnHistoryEntry(true);
4952 return NS_OK;
4955 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
4956 if (mOSHE) {
4957 mOSHE->SetTitle(mTitle);
4960 if (mActiveEntry && mBrowsingContext) {
4961 mActiveEntry->SetTitle(mTitle);
4962 if (aUpdateEntryInSessionHistory) {
4963 if (XRE_IsParentProcess()) {
4964 SessionHistoryEntry* entry =
4965 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
4966 if (entry) {
4967 entry->SetTitle(mTitle);
4969 } else {
4970 mozilla::Unused
4971 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
4972 mBrowsingContext, mTitle);
4978 nsPoint nsDocShell::GetCurScrollPos() {
4979 nsPoint scrollPos;
4980 if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
4981 scrollPos = sf->GetVisualViewportOffset();
4983 return scrollPos;
4986 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
4987 int32_t aCurVerticalPos) {
4988 nsIScrollableFrame* sf = GetRootScrollFrame();
4989 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4991 ScrollMode scrollMode =
4992 sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
4994 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
4995 sf->ScrollTo(targetPos, scrollMode);
4997 // Set the visual viewport offset as well.
4999 RefPtr<PresShell> presShell = GetPresShell();
5000 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5002 nsPresContext* presContext = presShell->GetPresContext();
5003 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
5005 // Only the root content document can have a distinct visual viewport offset.
5006 if (!presContext->IsRootContentDocumentCrossProcess()) {
5007 return NS_OK;
5010 // Not on a platform with a distinct visual viewport - don't bother setting
5011 // the visual viewport offset.
5012 if (!presShell->IsVisualViewportSizeSet()) {
5013 return NS_OK;
5016 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
5017 scrollMode);
5019 return NS_OK;
5022 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
5023 if (mScrollbarPref == aPref) {
5024 return;
5026 mScrollbarPref = aPref;
5027 auto* ps = GetPresShell();
5028 if (!ps) {
5029 return;
5031 nsIFrame* scrollFrame = ps->GetRootScrollFrame();
5032 if (!scrollFrame) {
5033 return;
5035 ps->FrameNeedsReflow(scrollFrame,
5036 IntrinsicDirty::FrameAncestorsAndDescendants,
5037 NS_FRAME_IS_DIRTY);
5040 //*****************************************************************************
5041 // nsDocShell::nsIRefreshURI
5042 //*****************************************************************************
5044 NS_IMETHODIMP
5045 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5046 uint32_t aDelay) {
5047 MOZ_ASSERT(!mIsBeingDestroyed);
5049 NS_ENSURE_ARG(aURI);
5051 /* Check if Meta refresh/redirects are permitted. Some
5052 * embedded applications may not want to do this.
5053 * Must do this before sending out NOTIFY_REFRESH events
5054 * because listeners may have side effects (e.g. displaying a
5055 * button to manually trigger the refresh later).
5057 bool allowRedirects = true;
5058 GetAllowMetaRedirects(&allowRedirects);
5059 if (!allowRedirects) {
5060 return NS_OK;
5063 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5064 // give them a chance to block this refresh.
5065 bool sameURI;
5066 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5067 if (NS_FAILED(rv)) {
5068 sameURI = false;
5070 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
5071 return NS_OK;
5074 nsCOMPtr<nsITimerCallback> refreshTimer =
5075 new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
5077 BusyFlags busyFlags = GetBusyFlags();
5079 if (!mRefreshURIList) {
5080 mRefreshURIList = nsArray::Create();
5083 if (busyFlags & BUSY_FLAGS_BUSY ||
5084 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
5085 // We don't want to create the timer right now. Instead queue up the
5086 // request and trigger the timer in EndPageLoad() or whenever we become
5087 // active.
5088 mRefreshURIList->AppendElement(refreshTimer);
5089 } else {
5090 // There is no page loading going on right now. Create the
5091 // timer and fire it right away.
5092 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5093 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
5095 nsCOMPtr<nsITimer> timer;
5096 MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
5097 nsITimer::TYPE_ONE_SHOT));
5099 mRefreshURIList->AppendElement(timer); // owning timer ref
5101 return NS_OK;
5104 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
5105 nsIPrincipal* aPrincipal,
5106 uint32_t aDelay,
5107 nsITimer* aTimer) {
5108 MOZ_ASSERT(aTimer, "Must have a timer here");
5110 // Remove aTimer from mRefreshURIList if needed
5111 if (mRefreshURIList) {
5112 uint32_t n = 0;
5113 mRefreshURIList->GetLength(&n);
5115 for (uint32_t i = 0; i < n; ++i) {
5116 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5117 if (timer == aTimer) {
5118 mRefreshURIList->RemoveElementAt(i);
5119 break;
5124 return ForceRefreshURI(aURI, aPrincipal, aDelay);
5127 NS_IMETHODIMP
5128 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5129 uint32_t aDelay) {
5130 NS_ENSURE_ARG(aURI);
5132 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
5133 loadState->SetOriginalURI(mCurrentURI);
5134 loadState->SetResultPrincipalURI(aURI);
5135 loadState->SetResultPrincipalURIIsSome(true);
5136 loadState->SetKeepResultPrincipalURIIfSet(true);
5137 loadState->SetIsMetaRefresh(true);
5139 // Set the triggering pricipal to aPrincipal if available, or current
5140 // document's principal otherwise.
5141 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
5142 RefPtr<Document> doc = GetDocument();
5143 if (!principal) {
5144 if (!doc) {
5145 return NS_ERROR_FAILURE;
5147 principal = doc->NodePrincipal();
5149 loadState->SetTriggeringPrincipal(principal);
5150 if (doc) {
5151 loadState->SetCsp(doc->GetCsp());
5152 loadState->SetHasValidUserGestureActivation(
5153 doc->HasValidTransientUserGestureActivation());
5154 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
5155 loadState->SetTriggeringWindowId(doc->InnerWindowID());
5156 loadState->SetTriggeringStorageAccess(doc->UsingStorageAccess());
5159 loadState->SetPrincipalIsExplicit(true);
5161 /* Check if this META refresh causes a redirection
5162 * to another site.
5164 bool equalUri = false;
5165 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5167 nsCOMPtr<nsIReferrerInfo> referrerInfo;
5168 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
5169 /* It is a META refresh based redirection within the threshold time
5170 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5171 * Pass a REPLACE flag to LoadURI().
5173 loadState->SetLoadType(LOAD_REFRESH_REPLACE);
5175 /* For redirects we mimic HTTP, which passes the
5176 * original referrer.
5177 * We will pass in referrer but will not send to server
5179 if (mReferrerInfo) {
5180 referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5181 ->CloneWithNewSendReferrer(false);
5183 } else {
5184 loadState->SetLoadType(LOAD_REFRESH);
5185 /* We do need to pass in a referrer, but we don't want it to
5186 * be sent to the server.
5187 * For most refreshes the current URI is an appropriate
5188 * internal referrer.
5190 referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5193 loadState->SetReferrerInfo(referrerInfo);
5194 loadState->SetLoadFlags(
5195 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5196 loadState->SetFirstParty(true);
5199 * LoadURI(...) will cancel all refresh timers... This causes the
5200 * Timer and its refreshData instance to be released...
5202 LoadURI(loadState, false);
5204 return NS_OK;
5207 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
5208 const char16_t* aEnd) {
5209 const char16_t* iter = aStart;
5210 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
5211 ++iter;
5213 return iter;
5216 static std::tuple<const char16_t*, const char16_t*> ExtractURLString(
5217 const char16_t* aPosition, const char16_t* aEnd) {
5218 MOZ_ASSERT(aPosition != aEnd);
5220 // 1. Let urlString be the substring of input from the code point at
5221 // position to the end of the string.
5222 const char16_t* urlStart = aPosition;
5223 const char16_t* urlEnd = aEnd;
5225 // 2. If the code point in input pointed to by position is U+0055 (U) or
5226 // U+0075 (u), then advance position to the next code point.
5227 // Otherwise, jump to the step labeled skip quotes.
5228 if (*aPosition == 'U' || *aPosition == 'u') {
5229 ++aPosition;
5231 // 3. If the code point in input pointed to by position is U+0052 (R) or
5232 // U+0072 (r), then advance position to the next code point.
5233 // Otherwise, jump to the step labeled parse.
5234 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
5235 return std::make_tuple(urlStart, urlEnd);
5238 ++aPosition;
5240 // 4. If the code point in input pointed to by position is U+004C (L) or
5241 // U+006C (l), then advance position to the next code point.
5242 // Otherwise, jump to the step labeled parse.
5243 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
5244 return std::make_tuple(urlStart, urlEnd);
5247 ++aPosition;
5249 // 5. Skip ASCII whitespace within input given position.
5250 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5252 // 6. If the code point in input pointed to by position is U+003D (=),
5253 // then advance position to the next code point. Otherwise, jump to
5254 // the step labeled parse.
5255 if (aPosition == aEnd || *aPosition != '=') {
5256 return std::make_tuple(urlStart, urlEnd);
5259 ++aPosition;
5261 // 7. Skip ASCII whitespace within input given position.
5262 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5265 // 8. Skip quotes: If the code point in input pointed to by position is
5266 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5267 // advance position to the next code point. Otherwise, let quote be
5268 // the empty string.
5269 Maybe<char> quote;
5270 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
5271 quote.emplace(*aPosition);
5272 ++aPosition;
5275 // 9. Set urlString to the substring of input from the code point at
5276 // position to the end of the string.
5277 urlStart = aPosition;
5278 urlEnd = aEnd;
5280 // 10. If quote is not the empty string, and there is a code point in
5281 // urlString equal to quote, then truncate urlString at that code
5282 // point, so that it and all subsequent code points are removed.
5283 const char16_t* quotePos;
5284 if (quote.isSome() &&
5285 (quotePos = nsCharTraits<char16_t>::find(
5286 urlStart, std::distance(urlStart, aEnd), quote.value()))) {
5287 urlEnd = quotePos;
5290 return std::make_tuple(urlStart, urlEnd);
5293 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
5294 const nsAString& aHeader) {
5295 if (mIsBeingDestroyed) {
5296 return;
5299 const char16_t* position = aHeader.BeginReading();
5300 const char16_t* end = aHeader.EndReading();
5302 // See
5303 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5305 // 3. Skip ASCII whitespace
5306 position = SkipASCIIWhitespace(position, end);
5308 // 4. Let time be 0.
5309 CheckedInt<uint32_t> milliSeconds;
5311 // 5. Collect a sequence of code points that are ASCII digits
5312 const char16_t* digitsStart = position;
5313 while (position != end && mozilla::IsAsciiDigit(*position)) {
5314 ++position;
5317 if (position == digitsStart) {
5318 // 6. If timeString is the empty string, then:
5319 // 1. If the code point in input pointed to by position is not U+002E
5320 // (.), then return.
5321 if (position == end || *position != '.') {
5322 return;
5324 } else {
5325 // 7. Otherwise, set time to the result of parsing timeString using the
5326 // rules for parsing non-negative integers.
5327 nsContentUtils::ParseHTMLIntegerResultFlags result;
5328 uint32_t seconds =
5329 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
5330 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
5331 if (result & nsContentUtils::eParseHTMLInteger_Error) {
5332 // The spec assumes no errors here (since we only pass ASCII digits in),
5333 // but we can still overflow, so this block should deal with that (and
5334 // only that).
5335 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
5336 return;
5338 MOZ_ASSERT(
5339 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
5341 milliSeconds = seconds;
5342 milliSeconds *= 1000;
5343 if (!milliSeconds.isValid()) {
5344 return;
5348 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5349 // STOP characters (.) from input given position. Ignore any collected
5350 // characters.
5351 while (position != end &&
5352 (mozilla::IsAsciiDigit(*position) || *position == '.')) {
5353 ++position;
5356 // 9. Let urlRecord be document's URL.
5357 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
5359 // 10. If position is not past the end of input
5360 if (position != end) {
5361 // 1. If the code point in input pointed to by position is not U+003B (;),
5362 // U+002C (,), or ASCII whitespace, then return.
5363 if (*position != ';' && *position != ',' &&
5364 !mozilla::IsAsciiWhitespace(*position)) {
5365 return;
5368 // 2. Skip ASCII whitespace within input given position.
5369 position = SkipASCIIWhitespace(position, end);
5371 // 3. If the code point in input pointed to by position is U+003B (;) or
5372 // U+002C (,), then advance position to the next code point.
5373 if (position != end && (*position == ';' || *position == ',')) {
5374 ++position;
5376 // 4. Skip ASCII whitespace within input given position.
5377 position = SkipASCIIWhitespace(position, end);
5380 // 11. If position is not past the end of input, then:
5381 if (position != end) {
5382 const char16_t* urlStart;
5383 const char16_t* urlEnd;
5385 // 1-10. See ExtractURLString.
5386 std::tie(urlStart, urlEnd) = ExtractURLString(position, end);
5388 // 11. Parse: Parse urlString relative to document. If that fails, return.
5389 // Otherwise, set urlRecord to the resulting URL record.
5390 nsresult rv =
5391 NS_NewURI(getter_AddRefs(urlRecord),
5392 Substring(urlStart, std::distance(urlStart, urlEnd)),
5393 /* charset = */ nullptr, aDocument->GetDocBaseURI());
5394 NS_ENSURE_SUCCESS_VOID(rv);
5398 nsIPrincipal* principal = aDocument->NodePrincipal();
5399 nsCOMPtr<nsIScriptSecurityManager> securityManager =
5400 nsContentUtils::GetSecurityManager();
5401 nsresult rv = securityManager->CheckLoadURIWithPrincipal(
5402 principal, urlRecord,
5403 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5404 aDocument->InnerWindowID());
5405 NS_ENSURE_SUCCESS_VOID(rv);
5407 bool isjs = true;
5408 rv = NS_URIChainHasFlags(
5409 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5410 NS_ENSURE_SUCCESS_VOID(rv);
5412 if (isjs) {
5413 return;
5416 RefreshURI(urlRecord, principal, milliSeconds.value());
5419 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5420 if (!aTimerList) {
5421 return;
5424 uint32_t n = 0;
5425 aTimerList->GetLength(&n);
5427 while (n) {
5428 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5430 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5432 if (timer) {
5433 timer->Cancel();
5438 NS_IMETHODIMP
5439 nsDocShell::CancelRefreshURITimers() {
5440 DoCancelRefreshURITimers(mRefreshURIList);
5441 DoCancelRefreshURITimers(mSavedRefreshURIList);
5442 DoCancelRefreshURITimers(mBFCachedRefreshURIList);
5443 mRefreshURIList = nullptr;
5444 mSavedRefreshURIList = nullptr;
5445 mBFCachedRefreshURIList = nullptr;
5447 return NS_OK;
5450 NS_IMETHODIMP
5451 nsDocShell::GetRefreshPending(bool* aResult) {
5452 if (!mRefreshURIList) {
5453 *aResult = false;
5454 return NS_OK;
5457 uint32_t count;
5458 nsresult rv = mRefreshURIList->GetLength(&count);
5459 if (NS_SUCCEEDED(rv)) {
5460 *aResult = (count != 0);
5462 return rv;
5465 void nsDocShell::RefreshURIToQueue() {
5466 if (mRefreshURIList) {
5467 uint32_t n = 0;
5468 mRefreshURIList->GetLength(&n);
5470 for (uint32_t i = 0; i < n; ++i) {
5471 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5472 if (!timer) {
5473 continue; // this must be a nsRefreshURI already
5476 // Replace this timer object with a nsRefreshTimer object.
5477 nsCOMPtr<nsITimerCallback> callback;
5478 timer->GetCallback(getter_AddRefs(callback));
5480 timer->Cancel();
5482 mRefreshURIList->ReplaceElementAt(callback, i);
5487 NS_IMETHODIMP
5488 nsDocShell::SuspendRefreshURIs() {
5489 RefreshURIToQueue();
5491 // Suspend refresh URIs for our child shells as well.
5492 for (auto* child : mChildList.ForwardRange()) {
5493 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5494 if (shell) {
5495 shell->SuspendRefreshURIs();
5499 return NS_OK;
5502 NS_IMETHODIMP
5503 nsDocShell::ResumeRefreshURIs() {
5504 RefreshURIFromQueue();
5506 // Resume refresh URIs for our child shells as well.
5507 for (auto* child : mChildList.ForwardRange()) {
5508 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5509 if (shell) {
5510 shell->ResumeRefreshURIs();
5514 return NS_OK;
5517 nsresult nsDocShell::RefreshURIFromQueue() {
5518 if (!mRefreshURIList) {
5519 return NS_OK;
5521 uint32_t n = 0;
5522 mRefreshURIList->GetLength(&n);
5524 while (n) {
5525 nsCOMPtr<nsITimerCallback> refreshInfo =
5526 do_QueryElementAt(mRefreshURIList, --n);
5528 if (refreshInfo) {
5529 // This is the nsRefreshTimer object, waiting to be
5530 // setup in a timer object and fired.
5531 // Create the timer and trigger it.
5532 uint32_t delay = static_cast<nsRefreshTimer*>(
5533 static_cast<nsITimerCallback*>(refreshInfo))
5534 ->GetDelay();
5535 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5536 if (win) {
5537 nsCOMPtr<nsITimer> timer;
5538 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5539 nsITimer::TYPE_ONE_SHOT);
5541 if (timer) {
5542 // Replace the nsRefreshTimer element in the queue with
5543 // its corresponding timer object, so that in case another
5544 // load comes through before the timer can go off, the timer will
5545 // get cancelled in CancelRefreshURITimer()
5546 mRefreshURIList->ReplaceElementAt(timer, n);
5552 return NS_OK;
5555 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) {
5556 nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
5557 bool firstPart = false;
5558 return multiPartChannel &&
5559 NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) &&
5560 !firstPart;
5563 nsresult nsDocShell::Embed(nsIDocumentViewer* aDocumentViewer,
5564 WindowGlobalChild* aWindowActor,
5565 bool aIsTransientAboutBlank, bool aPersist,
5566 nsIRequest* aRequest, nsIURI* aPreviousURI) {
5567 // Save the LayoutHistoryState of the previous document, before
5568 // setting up new document
5569 PersistLayoutHistoryState();
5571 nsresult rv = SetupNewViewer(aDocumentViewer, aWindowActor);
5572 NS_ENSURE_SUCCESS(rv, rv);
5574 // XXX What if SetupNewViewer fails?
5575 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
5576 // Set history.state
5577 SetDocCurrentStateObj(mLSHE,
5578 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
5581 if (mLSHE) {
5582 // Restore the editing state, if it's stored in session history.
5583 if (mLSHE->HasDetachedEditor()) {
5584 ReattachEditorToWindow(mLSHE);
5587 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5590 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() &&
5591 !IsFollowupPartOfMultipart(aRequest)) {
5592 bool expired = false;
5593 uint32_t cacheKey = 0;
5594 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
5595 if (cacheChannel) {
5596 // Check if the page has expired from cache
5597 uint32_t expTime = 0;
5598 cacheChannel->GetCacheTokenExpirationTime(&expTime);
5599 uint32_t now = PRTimeToSeconds(PR_Now());
5600 if (expTime <= now) {
5601 expired = true;
5604 // The checks for updating cache key are similar to the old session
5605 // history in OnNewURI. Try to update the cache key if
5606 // - we should update session history and aren't doing a session
5607 // history load.
5608 // - we're doing a forced reload.
5609 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
5610 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
5611 IsForceReloadType(mLoadType)) {
5612 cacheChannel->GetCacheKey(&cacheKey);
5616 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
5617 MoveLoadingToActiveEntry(aPersist, expired, cacheKey, aPreviousURI);
5620 bool updateHistory = true;
5622 // Determine if this type of load should update history
5623 switch (mLoadType) {
5624 case LOAD_NORMAL_REPLACE:
5625 case LOAD_REFRESH_REPLACE:
5626 case LOAD_STOP_CONTENT_AND_REPLACE:
5627 case LOAD_RELOAD_BYPASS_CACHE:
5628 case LOAD_RELOAD_BYPASS_PROXY:
5629 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5630 case LOAD_REPLACE_BYPASS_CACHE:
5631 updateHistory = false;
5632 break;
5633 default:
5634 break;
5637 if (!updateHistory) {
5638 SetLayoutHistoryState(nullptr);
5641 return NS_OK;
5644 //*****************************************************************************
5645 // nsDocShell::nsIWebProgressListener
5646 //*****************************************************************************
5648 NS_IMETHODIMP
5649 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5650 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5651 int32_t aCurTotalProgress,
5652 int32_t aMaxTotalProgress) {
5653 return NS_OK;
5656 NS_IMETHODIMP
5657 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5658 uint32_t aStateFlags, nsresult aStatus) {
5659 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5660 // Save timing statistics.
5661 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5662 nsCOMPtr<nsIURI> uri;
5663 channel->GetURI(getter_AddRefs(uri));
5664 nsAutoCString aURI;
5665 uri->GetAsciiSpec(aURI);
5667 if (this == aProgress) {
5668 mozilla::Unused << MaybeInitTiming();
5669 mTiming->NotifyFetchStart(uri,
5670 ConvertLoadTypeToNavigationType(mLoadType));
5671 // If we are starting a DocumentChannel, we need to pass the timing
5672 // statistics so that should a process switch occur, the starting type can
5673 // be passed to the new DocShell running in the other content process.
5674 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5675 docChannel->SetNavigationTiming(mTiming);
5679 // Page has begun to load
5680 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5682 if ((aStateFlags & STATE_RESTORING) == 0) {
5683 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
5684 if (IsForceReloadType(mLoadType)) {
5685 if (WindowContext* windowContext =
5686 mBrowsingContext->GetCurrentWindowContext()) {
5687 SessionStoreChild::From(windowContext->GetWindowGlobalChild())
5688 ->ResetSessionStore(mBrowsingContext,
5689 mBrowsingContext->GetSessionStoreEpoch());
5694 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5695 // Page is loading
5696 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5697 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5698 // Page has finished loading
5699 mBusyFlags = BUSY_FLAGS_NONE;
5702 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5703 nsCOMPtr<nsIWebProgress> webProgress =
5704 do_QueryInterface(GetAsSupports(this));
5705 // Is the document stop notification for this document?
5706 if (aProgress == webProgress.get()) {
5707 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5708 EndPageLoad(aProgress, channel, aStatus);
5711 // note that redirect state changes will go through here as well, but it
5712 // is better to handle those in OnRedirectStateChange where more
5713 // information is available.
5714 return NS_OK;
5717 NS_IMETHODIMP
5718 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5719 nsIURI* aURI, uint32_t aFlags) {
5720 // Since we've now changed Documents, notify the BrowsingContext that we've
5721 // changed. Ideally we'd just let the BrowsingContext do this when it
5722 // changes the current window global, but that happens before this and we
5723 // have a lot of tests that depend on the specific ordering of messages.
5724 bool isTopLevel = false;
5725 if (XRE_IsParentProcess() &&
5726 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
5727 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
5728 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5730 return NS_OK;
5733 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5734 nsIChannel* aNewChannel,
5735 uint32_t aRedirectFlags,
5736 uint32_t aStateFlags) {
5737 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5738 "Calling OnRedirectStateChange when there is no redirect");
5740 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5741 return; // not a toplevel document
5744 nsCOMPtr<nsIURI> oldURI, newURI;
5745 aOldChannel->GetURI(getter_AddRefs(oldURI));
5746 aNewChannel->GetURI(getter_AddRefs(newURI));
5747 if (!oldURI || !newURI) {
5748 return;
5751 // DocumentChannel adds redirect chain to global history in the parent
5752 // process. The redirect chain can't be queried from the content process, so
5753 // there's no need to update global history here.
5754 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5755 if (!docChannel) {
5756 // Below a URI visit is saved (see AddURIVisit method doc).
5757 // The visit chain looks something like:
5758 // ...
5759 // Site N - 1
5760 // => Site N
5761 // (redirect to =>) Site N + 1 (we are here!)
5763 // Get N - 1 and transition type
5764 nsCOMPtr<nsIURI> previousURI;
5765 uint32_t previousFlags = 0;
5766 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5768 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5769 net::ChannelIsPost(aOldChannel)) {
5770 // 1. Internal redirects are ignored because they are specific to the
5771 // channel implementation.
5772 // 2. POSTs are not saved by global history.
5774 // Regardless, we need to propagate the previous visit to the new
5775 // channel.
5776 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5777 } else {
5778 // Get the HTTP response code, if available.
5779 uint32_t responseStatus = 0;
5780 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5781 if (httpChannel) {
5782 Unused << httpChannel->GetResponseStatus(&responseStatus);
5785 // Add visit N -1 => N
5786 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5788 // Since N + 1 could be the final destination, we will not save N => N + 1
5789 // here. OnNewURI will do that, so we will cache it.
5790 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5794 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5795 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5796 mLoadType = LOAD_NORMAL_REPLACE;
5797 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5801 NS_IMETHODIMP
5802 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5803 nsresult aStatus, const char16_t* aMessage) {
5804 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5805 return NS_OK;
5808 NS_IMETHODIMP
5809 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5810 uint32_t aState) {
5811 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5812 return NS_OK;
5815 NS_IMETHODIMP
5816 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5817 nsIRequest* aRequest, uint32_t aEvent) {
5818 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5819 return NS_OK;
5822 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5823 const nsACString& aKeyword, bool aIsPrivateContext) {
5824 nsCOMPtr<nsIURIFixupInfo> info;
5825 if (!XRE_IsContentProcess()) {
5826 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5827 if (uriFixup) {
5828 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
5831 return info.forget();
5834 /* static */
5835 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
5836 nsIChannel* aChannel, nsIURI* aUrl) {
5837 if (!aChannel) {
5838 return nullptr;
5841 nsresult rv = NS_OK;
5842 nsAutoCString host;
5843 rv = aUrl->GetAsciiHost(host);
5844 if (NS_WARN_IF(NS_FAILED(rv))) {
5845 return nullptr;
5848 // No point in going further if "www." is included in the hostname
5849 // already. That is the only hueristic we're applying in this function.
5850 if (StringBeginsWith(host, "www."_ns)) {
5851 return nullptr;
5854 // Return if fixup enable pref is turned off.
5855 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5856 return nullptr;
5859 // Return if scheme is not HTTPS.
5860 if (!SchemeIsHTTPS(aUrl)) {
5861 return nullptr;
5864 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
5865 if (!info) {
5866 return nullptr;
5869 // Skip doing the fixup if our channel was redirected, because we
5870 // shouldn't be guessing things about the post-redirect URI.
5871 if (!info->RedirectChain().IsEmpty()) {
5872 return nullptr;
5875 int32_t port = 0;
5876 rv = aUrl->GetPort(&port);
5877 if (NS_WARN_IF(NS_FAILED(rv))) {
5878 return nullptr;
5881 // Don't fix up hosts with ports.
5882 if (port != -1) {
5883 return nullptr;
5886 // Don't fix up localhost url.
5887 if (host == "localhost") {
5888 return nullptr;
5891 // Don't fix up hostnames with IP address.
5892 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
5893 return nullptr;
5896 nsAutoCString userPass;
5897 rv = aUrl->GetUserPass(userPass);
5898 if (NS_WARN_IF(NS_FAILED(rv))) {
5899 return nullptr;
5902 // Security - URLs with user / password info should NOT be modified.
5903 if (!userPass.IsEmpty()) {
5904 return nullptr;
5907 nsCOMPtr<nsITransportSecurityInfo> tsi;
5908 rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi));
5909 if (NS_WARN_IF(NS_FAILED(rv))) {
5910 return nullptr;
5913 if (NS_WARN_IF(!tsi)) {
5914 return nullptr;
5917 nsCOMPtr<nsIX509Cert> cert;
5918 rv = tsi->GetServerCert(getter_AddRefs(cert));
5919 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
5920 return nullptr;
5923 nsTArray<uint8_t> certBytes;
5924 rv = cert->GetRawDER(certBytes);
5925 if (NS_FAILED(rv)) {
5926 return nullptr;
5929 mozilla::pkix::Input serverCertInput;
5930 mozilla::pkix::Result rv1 =
5931 serverCertInput.Init(certBytes.Elements(), certBytes.Length());
5932 if (rv1 != mozilla::pkix::Success) {
5933 return nullptr;
5936 nsAutoCString newHost("www."_ns);
5937 newHost.Append(host);
5939 mozilla::pkix::Input newHostInput;
5940 rv1 = newHostInput.Init(
5941 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
5942 newHost.Length());
5943 if (rv1 != mozilla::pkix::Success) {
5944 return nullptr;
5947 // Check if adding a "www." prefix to the request's hostname will
5948 // cause the response's certificate to match.
5949 rv1 = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput);
5950 if (rv1 != mozilla::pkix::Success) {
5951 return nullptr;
5954 nsCOMPtr<nsIURI> newURI;
5955 Unused << NS_MutateURI(aUrl).SetHost(newHost).Finalize(
5956 getter_AddRefs(newURI));
5958 return newURI.forget();
5961 /* static */
5962 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
5963 nsIChannel* aChannel, nsresult aStatus,
5964 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
5965 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
5966 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData,
5967 bool* outWasSchemelessInput) {
5968 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
5969 aStatus != NS_ERROR_CONNECTION_REFUSED &&
5970 aStatus !=
5971 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
5972 return nullptr;
5975 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
5976 return nullptr;
5979 nsCOMPtr<nsIURI> url;
5980 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
5981 if (NS_FAILED(rv)) {
5982 return nullptr;
5986 // Try and make an alternative URI from the old one
5988 nsCOMPtr<nsIURI> newURI;
5989 nsCOMPtr<nsIInputStream> newPostData;
5991 nsAutoCString oldSpec;
5992 url->GetSpec(oldSpec);
5995 // First try keyword fixup
5997 nsAutoString keywordProviderName, keywordAsSent;
5998 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
5999 // we should only perform a keyword search under the following
6000 // conditions:
6001 // (0) Pref keyword.enabled is true
6002 // (1) the url scheme is http (or https)
6003 // (2) the url does not have a protocol scheme
6004 // If we don't enforce such a policy, then we end up doing
6005 // keyword searchs on urls we don't intend like imap, file,
6006 // mailbox, etc. This could lead to a security problem where we
6007 // send data to the keyword server that we shouldn't be.
6008 // Someone needs to clean up keywords in general so we can
6009 // determine on a per url basis if we want keywords
6010 // enabled...this is just a bandaid...
6011 nsAutoCString scheme;
6012 Unused << url->GetScheme(scheme);
6013 if (Preferences::GetBool("keyword.enabled", false) &&
6014 StringBeginsWith(scheme, "http"_ns)) {
6015 bool attemptFixup = false;
6016 nsAutoCString host;
6017 Unused << url->GetHost(host);
6018 if (host.FindChar('.') == kNotFound) {
6019 attemptFixup = true;
6020 } else {
6021 // For domains with dots, we check the public suffix validity.
6022 nsCOMPtr<nsIEffectiveTLDService> tldService =
6023 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
6024 if (tldService) {
6025 nsAutoCString suffix;
6026 attemptFixup =
6027 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
6028 suffix.IsEmpty();
6031 if (attemptFixup) {
6032 nsCOMPtr<nsIURIFixupInfo> info;
6033 // only send non-qualified hosts to the keyword server
6034 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
6035 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
6036 } else {
6038 // If this string was passed through nsStandardURL by
6039 // chance, then it may have been converted from UTF-8 to
6040 // ACE, which would result in a completely bogus keyword
6041 // query. Here we try to recover the original Unicode
6042 // value, but this is not 100% correct since the value may
6043 // have been normalized per the IDN normalization rules.
6045 // Since we don't have access to the exact original string
6046 // that was entered by the user, this will just have to do.
6047 bool isACE;
6048 nsAutoCString utf8Host;
6049 nsCOMPtr<nsIIDNService> idnSrv =
6050 do_GetService(NS_IDNSERVICE_CONTRACTID);
6051 if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6052 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6053 info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
6055 } else {
6056 info = KeywordToURI(host, aUsePrivateBrowsing);
6059 if (info) {
6060 info->GetPreferredURI(getter_AddRefs(newURI));
6061 info->GetWasSchemelessInput(outWasSchemelessInput);
6062 if (newURI) {
6063 info->GetKeywordAsSent(keywordAsSent);
6064 info->GetKeywordProviderName(keywordProviderName);
6065 info->GetPostData(getter_AddRefs(newPostData));
6073 // Now try change the address, e.g. turn http://foo into
6074 // http://www.foo.com, and if that doesn't work try https with
6075 // https://foo and https://www.foo.com.
6077 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6078 // Skip fixup for anything except a normal document load
6079 // operation on the topframe.
6080 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
6082 if (doCreateAlternate) {
6083 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6084 nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
6085 // Only do this if our channel was loaded directly by the user from the
6086 // URL bar or similar (system principal) and not redirected, because we
6087 // shouldn't be guessing things about links from other sites, or a
6088 // post-redirect URI.
6089 doCreateAlternate = principal && principal->IsSystemPrincipal() &&
6090 loadInfo->RedirectChain().IsEmpty();
6092 // Test if keyword lookup produced a new URI or not
6093 if (doCreateAlternate && newURI) {
6094 bool sameURI = false;
6095 url->Equals(newURI, &sameURI);
6096 if (!sameURI) {
6097 // Keyword lookup made a new URI so no need to try
6098 // an alternate one.
6099 doCreateAlternate = false;
6102 if (doCreateAlternate) {
6103 newURI = nullptr;
6104 newPostData = nullptr;
6105 keywordProviderName.Truncate();
6106 keywordAsSent.Truncate();
6107 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6108 if (uriFixup) {
6109 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
6110 uriFixup->GetFixupURIInfo(oldSpec, nsIURIFixup::FIXUP_FLAG_NONE,
6111 getter_AddRefs(fixupInfo));
6112 if (fixupInfo) {
6113 fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
6117 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6118 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6119 // Try HTTPS, since http didn't work
6120 if (SchemeIsHTTP(url)) {
6121 int32_t port = 0;
6122 url->GetPort(&port);
6124 // Fall back to HTTPS only if port is default
6125 if (port == -1) {
6126 newURI = nullptr;
6127 newPostData = nullptr;
6128 Unused << NS_MutateURI(url)
6129 .SetScheme("https"_ns)
6130 .Finalize(getter_AddRefs(newURI));
6135 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try prefixing the domain name
6136 // with www. to see if we can avoid showing the cert error page. For example,
6137 // https://example.com -> https://www.example.com.
6138 if (aStatus ==
6139 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6140 newPostData = nullptr;
6141 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
6144 // Did we make a new URI that is different to the old one? If so
6145 // load it.
6147 if (newURI) {
6148 // Make sure the new URI is different from the old one,
6149 // otherwise there's little point trying to load it again.
6150 bool sameURI = false;
6151 url->Equals(newURI, &sameURI);
6152 if (!sameURI) {
6153 if (aNewPostData) {
6154 newPostData.forget(aNewPostData);
6156 if (aNotifyKeywordSearchLoading) {
6157 // This notification is meant for Firefox Health Report so it
6158 // can increment counts from the search engine
6159 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6161 return newURI.forget();
6165 return nullptr;
6168 nsresult nsDocShell::FilterStatusForErrorPage(
6169 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
6170 bool aIsTopFrame, bool aUseErrorPages, bool aIsInitialDocument,
6171 bool* aSkippedUnknownProtocolNavigation) {
6172 // Errors to be shown only on top-level frames
6173 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6174 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6175 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6176 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6177 aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6178 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6179 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6180 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6181 aStatus == NS_ERROR_MALFORMED_URI ||
6182 aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
6183 aStatus == NS_ERROR_DOM_COOP_FAILED ||
6184 aStatus == NS_ERROR_DOM_COEP_FAILED) &&
6185 (aIsTopFrame || aUseErrorPages)) {
6186 return aStatus;
6189 if (aStatus == NS_ERROR_NET_TIMEOUT ||
6190 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
6191 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6192 aStatus == NS_ERROR_REDIRECT_LOOP ||
6193 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6194 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
6195 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
6196 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
6197 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
6198 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6199 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6200 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6201 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6202 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6203 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
6204 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
6205 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
6206 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
6207 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6208 // Errors to be shown for any frame
6209 return aStatus;
6212 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6213 // For unknown protocols we only display an error if the load is triggered
6214 // by the browser itself, or we're replacing the initial document (and
6215 // nothing else). Showing the error for page-triggered navigations causes
6216 // annoying behavior for users, see bug 1528305.
6218 // We could, maybe, try to detect if this is in response to some user
6219 // interaction (like clicking a link, or something else) and maybe show
6220 // the error page in that case. But this allows for ctrl+clicking and such
6221 // to see the error page.
6222 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6223 if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6224 StaticPrefs::dom_no_unknown_protocol_error_enabled() &&
6225 !aIsInitialDocument) {
6226 if (aSkippedUnknownProtocolNavigation) {
6227 *aSkippedUnknownProtocolNavigation = true;
6229 return NS_OK;
6231 return aStatus;
6234 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6235 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6236 // Caching channels would have to look at their flags to work
6237 // out which error to return. Or we can fix up the error here.
6238 if (!(aLoadType & LOAD_CMD_HISTORY)) {
6239 return NS_ERROR_OFFLINE;
6241 return aStatus;
6244 return NS_OK;
6247 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6248 nsIChannel* aChannel, nsresult aStatus) {
6249 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
6250 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
6251 static_cast<uint32_t>(aStatus)));
6252 if (!aChannel) {
6253 return NS_ERROR_NULL_POINTER;
6256 // Make sure to discard the initial client if we never created the initial
6257 // about:blank document. Do this before possibly returning from the method
6258 // due to an error.
6259 mInitialClientSource.reset();
6261 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6262 if (reporter) {
6263 nsCOMPtr<nsILoadGroup> loadGroup;
6264 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6265 if (loadGroup) {
6266 reporter->FlushConsoleReports(loadGroup);
6267 } else {
6268 reporter->FlushConsoleReports(GetDocument());
6272 nsCOMPtr<nsIURI> url;
6273 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6274 if (NS_FAILED(rv)) {
6275 return rv;
6278 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6279 if (timingChannel) {
6280 TimeStamp channelCreationTime;
6281 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6282 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6283 Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
6284 channelCreationTime);
6288 // Timing is picked up by the window, we don't need it anymore
6289 mTiming = nullptr;
6291 // clean up reload state for meta charset
6292 if (eCharsetReloadRequested == mCharsetReloadState) {
6293 mCharsetReloadState = eCharsetReloadStopOrigional;
6294 } else {
6295 mCharsetReloadState = eCharsetReloadInit;
6298 // Save a pointer to the currently-loading history entry.
6299 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6300 // entry further down in this method.
6301 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6302 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
6305 // one of many safeguards that prevent death and destruction if
6306 // someone is so very very rude as to bring this window down
6307 // during this load handler.
6309 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6311 // Notify the DocumentViewer that the Document has finished loading. This
6312 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6313 if (!mEODForCurrentDocument && mDocumentViewer) {
6314 mIsExecutingOnLoadHandler = true;
6315 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
6316 viewer->LoadComplete(aStatus);
6317 mIsExecutingOnLoadHandler = false;
6319 mEODForCurrentDocument = true;
6321 /* Check if the httpChannel has any cache-control related response headers,
6322 * like no-store, no-cache. If so, update SHEntry so that
6323 * when a user goes back/forward to this page, we appropriately do
6324 * form value restoration or load from server.
6326 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6327 if (!httpChannel) {
6328 // HttpChannel could be hiding underneath a Multipart channel.
6329 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6332 if (httpChannel) {
6333 // figure out if SH should be saving layout state.
6334 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6335 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6336 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
6337 mLSHE->SetSaveLayoutStateFlag(false);
6341 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6342 // onLoadHandler tries to load something different in
6343 // itself or one of its children, we can deal with it appropriately.
6344 if (mLSHE) {
6345 mLSHE->SetLoadType(LOAD_HISTORY);
6347 // Clear the mLSHE reference to indicate document loading is done one
6348 // way or another.
6349 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6351 mActiveEntryIsLoadingFromSessionHistory = false;
6353 // if there's a refresh header in the channel, this method
6354 // will set it up for us.
6355 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
6356 RefreshURIFromQueue();
6358 // Test whether this is the top frame or a subframe
6359 bool isTopFrame = mBrowsingContext->IsTop();
6361 bool hadErrorStatus = false;
6362 // If status code indicates an error it means that DocumentChannel already
6363 // tried to fixup the uri and failed. Throw an error dialog box here.
6364 if (NS_FAILED(aStatus)) {
6365 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6366 // the error event to our embedder, since tests are relying on this.
6367 // The error event is usually fired by the caller of InternalLoad, but
6368 // this particular error can happen asynchronously.
6369 // Bug 1629201 is filed for having much clearer decision making around
6370 // which cases need error events.
6371 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6372 aStatus == NS_ERROR_CONTENT_BLOCKED);
6373 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6375 bool isInitialDocument =
6376 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6377 bool skippedUnknownProtocolNavigation = false;
6378 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
6379 mBrowsingContext->GetUseErrorPages(),
6380 isInitialDocument,
6381 &skippedUnknownProtocolNavigation);
6382 hadErrorStatus = true;
6383 if (NS_FAILED(aStatus)) {
6384 if (!mIsBeingDestroyed) {
6385 DisplayLoadError(aStatus, url, nullptr, aChannel);
6387 } else if (skippedUnknownProtocolNavigation) {
6388 nsTArray<nsString> params;
6389 if (NS_FAILED(
6390 NS_GetSanitizedURIStringFromURI(url, *params.AppendElement()))) {
6391 params.LastElement().AssignLiteral(u"(unknown uri)");
6393 nsContentUtils::ReportToConsole(
6394 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
6395 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
6396 params);
6398 } else {
6399 // If we have a host
6400 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6401 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6404 if (hadErrorStatus) {
6405 // Don't send session store updates if the reason EndPageLoad was called is
6406 // because we are process switching. Sometimes the update takes too long and
6407 // incorrectly overrides session store data from the following load.
6408 return NS_OK;
6410 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
6411 if (WindowContext* windowContext =
6412 mBrowsingContext->GetCurrentWindowContext()) {
6413 using Change = SessionStoreChangeListener::Change;
6415 // We've finished loading the page and now we want to collect all the
6416 // session store state that the page is initialized with.
6417 SessionStoreChangeListener::CollectSessionStoreData(
6418 windowContext,
6419 EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory,
6420 Change::WireFrame));
6424 return NS_OK;
6427 //*****************************************************************************
6428 // nsDocShell: Content Viewer Management
6429 //*****************************************************************************
6431 nsresult nsDocShell::EnsureDocumentViewer() {
6432 if (mDocumentViewer) {
6433 return NS_OK;
6435 if (mIsBeingDestroyed) {
6436 return NS_ERROR_FAILURE;
6439 nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6440 nsCOMPtr<nsIURI> baseURI;
6441 nsIPrincipal* principal = GetInheritedPrincipal(false);
6442 nsIPrincipal* partitionedPrincipal = GetInheritedPrincipal(false, true);
6444 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6445 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6446 if (parentItem) {
6447 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6448 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6449 if (parentElement) {
6450 baseURI = parentElement->GetBaseURI();
6451 cspToInheritForAboutBlank = parentElement->GetCsp();
6456 nsresult rv = CreateAboutBlankDocumentViewer(
6457 principal, partitionedPrincipal, cspToInheritForAboutBlank, baseURI,
6458 /* aIsInitialDocument */ true);
6460 NS_ENSURE_STATE(mDocumentViewer);
6462 if (NS_SUCCEEDED(rv)) {
6463 RefPtr<Document> doc(GetDocument());
6464 MOZ_ASSERT(doc,
6465 "Should have doc if CreateAboutBlankDocumentViewer "
6466 "succeeded!");
6467 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
6469 // Documents created using EnsureDocumentViewer may be transient
6470 // placeholders created by framescripts before content has a
6471 // chance to load. In some cases, window.open(..., "noopener")
6472 // will create such a document and then synchronously tear it
6473 // down, firing a "pagehide" event. Doing so violates our
6474 // assertions about DocGroups. It's easier to silence the
6475 // assertion here than to avoid creating the extra document.
6476 doc->IgnoreDocGroupMismatches();
6479 return rv;
6482 nsresult nsDocShell::CreateAboutBlankDocumentViewer(
6483 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
6484 nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI, bool aIsInitialDocument,
6485 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
6486 bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6487 WindowGlobalChild* aActor) {
6488 RefPtr<Document> blankDoc;
6489 nsCOMPtr<nsIDocumentViewer> viewer;
6490 nsresult rv = NS_ERROR_FAILURE;
6492 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6494 /* mCreatingDocument should never be true at this point. However, it's
6495 a theoretical possibility. We want to know about it and make it stop,
6496 and this sounds like a job for an assertion. */
6497 NS_ASSERTION(!mCreatingDocument,
6498 "infinite(?) loop creating document averted");
6499 if (mCreatingDocument) {
6500 return NS_ERROR_FAILURE;
6503 if (!mBrowsingContext->AncestorsAreCurrent() ||
6504 mBrowsingContext->IsInBFCache()) {
6505 mBrowsingContext->RemoveRootFromBFCacheSync();
6506 return NS_ERROR_NOT_AVAILABLE;
6509 // mDocumentViewer->PermitUnload may release |this| docshell.
6510 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6512 AutoRestore<bool> creatingDocument(mCreatingDocument);
6513 mCreatingDocument = true;
6515 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6516 mItemType != typeChrome) {
6517 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6518 mBrowsingContext->OriginAttributesRef());
6521 // Make sure timing is created. But first record whether we had it
6522 // already, so we don't clobber the timing for an in-progress load.
6523 bool hadTiming = mTiming;
6524 bool toBeReset = MaybeInitTiming();
6525 if (mDocumentViewer) {
6526 if (aCheckPermitUnload) {
6527 // We've got a content viewer already. Make sure the user
6528 // permits us to discard the current document and replace it
6529 // with about:blank. And also ensure we fire the unload events
6530 // in the current document.
6532 // Unload gets fired first for
6533 // document loaded from the session history.
6534 mTiming->NotifyBeforeUnload();
6536 bool okToUnload;
6537 rv = mDocumentViewer->PermitUnload(&okToUnload);
6539 if (NS_SUCCEEDED(rv) && !okToUnload) {
6540 // The user chose not to unload the page, interrupt the load.
6541 MaybeResetInitTiming(toBeReset);
6542 return NS_ERROR_FAILURE;
6544 if (mTiming) {
6545 mTiming->NotifyUnloadAccepted(mCurrentURI);
6549 mSavingOldViewer =
6550 aTryToSaveOldPresentation &&
6551 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
6552 /* aReportBFCacheComboTelemetry */ true);
6554 // Make sure to blow away our mLoadingURI just in case. No loads
6555 // from inside this pagehide.
6556 mLoadingURI = nullptr;
6558 // Stop any in-progress loading, so that we don't accidentally trigger any
6559 // PageShow notifications from Embed() interrupting our loading below.
6560 Stop();
6562 // Notify the current document that it is about to be unloaded!!
6564 // It is important to fire the unload() notification *before* any state
6565 // is changed within the DocShell - otherwise, javascript will get the
6566 // wrong information :-(
6568 (void)FirePageHideNotification(!mSavingOldViewer);
6569 // pagehide notification might destroy this docshell.
6570 if (mIsBeingDestroyed) {
6571 return NS_ERROR_DOCSHELL_DYING;
6575 // Now make sure we don't think we're in the middle of firing unload after
6576 // this point. This will make us fire unload when the about:blank document
6577 // unloads... but that's ok, more or less. Would be nice if it fired load
6578 // too, of course.
6579 mFiredUnloadEvent = false;
6581 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6582 nsContentUtils::FindInternalDocumentViewer("text/html"_ns);
6584 if (docFactory) {
6585 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6586 const uint32_t sandboxFlags =
6587 mBrowsingContext->GetHasLoadedNonInitialDocument()
6588 ? mBrowsingContext->GetSandboxFlags()
6589 : mBrowsingContext->GetInitialSandboxFlags();
6590 // If we're sandboxed, then create a new null principal. We skip
6591 // this if we're being created from WindowGlobalChild, since in
6592 // that case we already have a null principal if required.
6593 // We can't compare againt the BrowsingContext sandbox flag, since
6594 // the value was taken when the load initiated and may have since
6595 // changed.
6596 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6597 if (aPrincipal) {
6598 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6599 } else {
6600 principal = NullPrincipal::Create(GetOriginAttributes());
6602 partitionedPrincipal = principal;
6603 } else {
6604 principal = aPrincipal;
6605 partitionedPrincipal = aPartitionedPrincipal;
6608 // We cannot get the foreign partitioned prinicpal for the initial
6609 // about:blank page. So, we change to check if we need to use the
6610 // partitioned principal for the service worker here.
6611 MaybeCreateInitialClientSource(
6612 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6613 this)
6614 ? partitionedPrincipal
6615 : principal);
6617 // generate (about:blank) document to load
6618 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6619 partitionedPrincipal, this);
6620 if (blankDoc) {
6621 // Hack: manually set the CSP for the new document
6622 // Please create an actual copy of the CSP (do not share the same
6623 // reference) otherwise appending a new policy within the new
6624 // document will be incorrectly propagated to the opening doc.
6625 if (aCSP) {
6626 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6627 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6628 blankDoc->SetCsp(cspToInherit);
6631 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6633 blankDoc->SetEmbedderPolicy(aCOEP);
6635 // Hack: set the base URI manually, since this document never
6636 // got Reset() with a channel.
6637 blankDoc->SetBaseURI(aBaseURI);
6639 // Copy our sandbox flags to the document. These are immutable
6640 // after being set here.
6641 blankDoc->SetSandboxFlags(sandboxFlags);
6643 blankDoc->InitFeaturePolicy();
6645 // create a content viewer for us and the new document
6646 docFactory->CreateInstanceForDocument(
6647 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6648 getter_AddRefs(viewer));
6650 // hook 'em up
6651 if (viewer) {
6652 viewer->SetContainer(this);
6653 rv = Embed(viewer, aActor, true, false, nullptr, mCurrentURI);
6654 NS_ENSURE_SUCCESS(rv, rv);
6656 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6657 /* aFireLocationChange */ true,
6658 /* aIsInitialAboutBlank */ true,
6659 /* aLocationFlags */ 0);
6660 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6665 // The transient about:blank viewer doesn't have a session history entry.
6666 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6668 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6669 // have one before entering this function.
6670 if (!hadTiming) {
6671 mTiming = nullptr;
6672 mBlankTiming = true;
6675 return rv;
6678 NS_IMETHODIMP
6679 nsDocShell::CreateAboutBlankDocumentViewer(nsIPrincipal* aPrincipal,
6680 nsIPrincipal* aPartitionedPrincipal,
6681 nsIContentSecurityPolicy* aCSP) {
6682 return CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6683 nullptr,
6684 /* aIsInitialDocument */ false);
6687 nsresult nsDocShell::CreateDocumentViewerForActor(
6688 WindowGlobalChild* aWindowActor) {
6689 MOZ_ASSERT(aWindowActor);
6691 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6692 // FIXME: We may want to support non-initial documents here.
6693 nsresult rv = CreateAboutBlankDocumentViewer(
6694 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6695 /* aCsp */ nullptr,
6696 /* aBaseURI */ nullptr,
6697 /* aIsInitialDocument */ true,
6698 /* aCOEP */ Nothing(),
6699 /* aTryToSaveOldPresentation */ true,
6700 /* aCheckPermitUnload */ true, aWindowActor);
6701 #ifdef DEBUG
6702 if (NS_SUCCEEDED(rv)) {
6703 RefPtr<Document> doc(GetDocument());
6704 MOZ_ASSERT(
6705 doc,
6706 "Should have a document if CreateAboutBlankDocumentViewer succeeded");
6707 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6708 "New document should be in the same global as our actor");
6709 MOZ_ASSERT(doc->IsInitialDocument(),
6710 "New document should be an initial document");
6712 #endif
6714 return rv;
6717 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6718 nsIRequest* aNewRequest,
6719 Document* aNewDocument,
6720 bool aReportBFCacheComboTelemetry) {
6721 if (!mOSHE) {
6722 return false; // no entry to save into
6725 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6726 "mOSHE cannot be non-null with SHIP");
6727 nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
6728 if (viewer) {
6729 NS_WARNING("mOSHE already has a content viewer!");
6730 return false;
6733 // Only save presentation for "normal" loads and link loads. Anything else
6734 // probably wants to refetch the page, so caching the old presentation
6735 // would be incorrect.
6736 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6737 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6738 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6739 aLoadType != LOAD_ERROR_PAGE) {
6740 return false;
6743 // If the session history entry has the saveLayoutState flag set to false,
6744 // then we should not cache the presentation.
6745 if (!mOSHE->GetSaveLayoutStateFlag()) {
6746 return false;
6749 // If the document is not done loading, don't cache it.
6750 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6751 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6752 ("Blocked due to document still loading"));
6753 return false;
6756 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6757 return false;
6760 // Avoid doing the work of saving the presentation state in the case where
6761 // the content viewer cache is disabled.
6762 if (nsSHistory::GetMaxTotalViewers() == 0) {
6763 return false;
6766 // Don't cache the content viewer if we're in a subframe.
6767 if (mBrowsingContext->GetParent()) {
6768 return false; // this is a subframe load
6771 // If the document does not want its presentation cached, then don't.
6772 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6774 uint32_t bfCacheCombo = 0;
6775 bool canSavePresentation =
6776 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6777 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6778 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6779 auto* browsingContextGroup = mBrowsingContext->Group();
6780 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6781 browsingContextGroup->Toplevels();
6783 for (const auto& browsingContext : topLevelContext) {
6784 if (browsingContext != mBrowsingContext) {
6785 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6786 canSavePresentation = false;
6788 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6789 break;
6794 if (aReportBFCacheComboTelemetry) {
6795 ReportBFCacheComboTelemetry(bfCacheCombo);
6797 return doc && canSavePresentation;
6800 /* static */
6801 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6802 // There are 11 possible reasons to make a request fails to use BFCache
6803 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6804 // the common combinations for reasons which make requests fail to use
6805 // BFCache. These combinations are generated based on some local browsings,
6806 // we need to adjust them when necessary.
6807 enum BFCacheStatusCombo : uint32_t {
6808 BFCACHE_SUCCESS,
6809 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6810 // If both unload and beforeunload listeners are presented, it'll be
6811 // recorded as unload
6812 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6813 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6814 mozilla::dom::BFCacheStatus::REQUEST,
6815 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6816 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6817 mozilla::dom::BFCacheStatus::REQUEST |
6818 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6819 UNLOAD_REQUEST_PEER_MSE =
6820 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6821 mozilla::dom::BFCacheStatus::REQUEST |
6822 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6823 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6824 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6825 mozilla::dom::BFCacheStatus::REQUEST |
6826 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6827 SUSPENDED_UNLOAD_REQUEST_PEER =
6828 mozilla::dom::BFCacheStatus::SUSPENDED |
6829 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6830 mozilla::dom::BFCacheStatus::REQUEST |
6831 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6832 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6833 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6836 // Beforeunload is recorded as a blocker only if it is the only one to block
6837 // bfcache.
6838 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6839 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6841 switch (aCombo) {
6842 case BFCACHE_SUCCESS:
6843 Telemetry::AccumulateCategorical(
6844 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6845 break;
6846 case NOT_ONLY_TOPLEVEL:
6847 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6848 Telemetry::AccumulateCategorical(
6849 Telemetry::LABELS_BFCACHE_COMBO::Other);
6850 break;
6852 Telemetry::AccumulateCategorical(
6853 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6854 Telemetry::AccumulateCategorical(
6855 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6856 break;
6857 case UNLOAD:
6858 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6859 break;
6860 case BEFOREUNLOAD:
6861 Telemetry::AccumulateCategorical(
6862 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6863 break;
6864 case UNLOAD_REQUEST:
6865 Telemetry::AccumulateCategorical(
6866 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6867 break;
6868 case REQUEST:
6869 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6870 break;
6871 case UNLOAD_REQUEST_PEER:
6872 Telemetry::AccumulateCategorical(
6873 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6874 break;
6875 case UNLOAD_REQUEST_PEER_MSE:
6876 Telemetry::AccumulateCategorical(
6877 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6878 break;
6879 case UNLOAD_REQUEST_MSE:
6880 Telemetry::AccumulateCategorical(
6881 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6882 break;
6883 case SUSPENDED_UNLOAD_REQUEST_PEER:
6884 Telemetry::AccumulateCategorical(
6885 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6886 break;
6887 case REMOTE_SUBFRAMES:
6888 Telemetry::AccumulateCategorical(
6889 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6890 break;
6891 default:
6892 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6893 break;
6897 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6898 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6899 MOZ_ASSERT(!mIsBeingDestroyed);
6901 NS_ASSERTION(!mEditorData,
6902 "Why reattach an editor when we already have one?");
6903 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6904 "Reattaching when there's not a detached editor.");
6906 if (mEditorData || !aSHEntry) {
6907 return;
6910 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6911 if (mEditorData) {
6912 #ifdef DEBUG
6913 nsresult rv =
6914 #endif
6915 mEditorData->ReattachToWindow(this);
6916 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
6920 void nsDocShell::DetachEditorFromWindow() {
6921 if (!mEditorData || mEditorData->WaitingForLoad()) {
6922 // If there's nothing to detach, or if the editor data is actually set
6923 // up for the _new_ page that's coming in, don't detach.
6924 return;
6927 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6928 "Detaching editor when it's already detached.");
6930 nsresult res = mEditorData->DetachFromWindow();
6931 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6933 if (NS_SUCCEEDED(res)) {
6934 // Make mOSHE hold the owning ref to the editor data.
6935 if (mOSHE) {
6936 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
6937 "We should not set the editor data again once after we "
6938 "detached the editor data during destroying this docshell");
6939 mOSHE->SetEditorData(mEditorData.release());
6940 } else {
6941 mEditorData = nullptr;
6945 #ifdef DEBUG
6947 bool isEditable;
6948 GetEditable(&isEditable);
6949 NS_ASSERTION(!isEditable,
6950 "Window is still editable after detaching editor.");
6952 #endif // DEBUG
6955 nsresult nsDocShell::CaptureState() {
6956 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6958 if (!mOSHE || mOSHE == mLSHE) {
6959 // No entry to save into, or we're replacing the existing entry.
6960 return NS_ERROR_FAILURE;
6963 if (!mScriptGlobal) {
6964 return NS_ERROR_FAILURE;
6967 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
6968 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
6970 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
6971 nsAutoCString spec;
6972 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
6973 if (uri) {
6974 uri->GetSpec(spec);
6976 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6977 ("Saving presentation into session history, URI: %s", spec.get()));
6980 mOSHE->SetWindowState(windowState);
6982 // Suspend refresh URIs and save off the timer queue
6983 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6985 // Capture the current content viewer bounds.
6986 if (mDocumentViewer) {
6987 nsIntRect bounds;
6988 mDocumentViewer->GetBounds(bounds);
6989 mOSHE->SetViewerBounds(bounds);
6992 // Capture the docshell hierarchy.
6993 mOSHE->ClearChildShells();
6995 uint32_t childCount = mChildList.Length();
6996 for (uint32_t i = 0; i < childCount; ++i) {
6997 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6998 NS_ASSERTION(childShell, "null child shell");
7000 mOSHE->AddChildShell(childShell);
7003 return NS_OK;
7006 NS_IMETHODIMP
7007 nsDocShell::RestorePresentationEvent::Run() {
7008 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7010 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7011 NS_WARNING("RestoreFromHistory failed");
7013 return NS_OK;
7016 NS_IMETHODIMP
7017 nsDocShell::BeginRestore(nsIDocumentViewer* aDocumentViewer, bool aTop) {
7018 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7020 nsresult rv;
7021 if (!aDocumentViewer) {
7022 rv = EnsureDocumentViewer();
7023 NS_ENSURE_SUCCESS(rv, rv);
7025 aDocumentViewer = mDocumentViewer;
7028 // Dispatch events for restoring the presentation. We try to simulate
7029 // the progress notifications loading the document would cause, so we add
7030 // the document's channel to the loadgroup to initiate stateChange
7031 // notifications.
7033 RefPtr<Document> doc = aDocumentViewer->GetDocument();
7034 if (doc) {
7035 nsIChannel* channel = doc->GetChannel();
7036 if (channel) {
7037 mEODForCurrentDocument = false;
7038 mIsRestoringDocument = true;
7039 mLoadGroup->AddRequest(channel, nullptr);
7040 mIsRestoringDocument = false;
7044 if (!aTop) {
7045 // This point corresponds to us having gotten OnStartRequest or
7046 // STATE_START, so do the same thing that CreateDocumentViewer does at
7047 // this point to ensure that unload/pagehide events for this document
7048 // will fire when it's unloaded again.
7049 mFiredUnloadEvent = false;
7051 // For non-top frames, there is no notion of making sure that the
7052 // previous document is in the domwindow when STATE_START notifications
7053 // happen. We can just call BeginRestore for all of the child shells
7054 // now.
7055 rv = BeginRestoreChildren();
7056 NS_ENSURE_SUCCESS(rv, rv);
7059 return NS_OK;
7062 nsresult nsDocShell::BeginRestoreChildren() {
7063 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7065 for (auto* childDocLoader : mChildList.ForwardRange()) {
7066 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7067 if (child) {
7068 nsresult rv = child->BeginRestore(nullptr, false);
7069 NS_ENSURE_SUCCESS(rv, rv);
7072 return NS_OK;
7075 NS_IMETHODIMP
7076 nsDocShell::FinishRestore() {
7077 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7079 // First we call finishRestore() on our children. In the simulated load,
7080 // all of the child frames finish loading before the main document.
7082 for (auto* childDocLoader : mChildList.ForwardRange()) {
7083 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7084 if (child) {
7085 child->FinishRestore();
7089 if (mOSHE && mOSHE->HasDetachedEditor()) {
7090 ReattachEditorToWindow(mOSHE);
7093 RefPtr<Document> doc = GetDocument();
7094 if (doc) {
7095 // Finally, we remove the request from the loadgroup. This will
7096 // cause onStateChange(STATE_STOP) to fire, which will fire the
7097 // pageshow event to the chrome.
7099 nsIChannel* channel = doc->GetChannel();
7100 if (channel) {
7101 mIsRestoringDocument = true;
7102 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7103 mIsRestoringDocument = false;
7107 return NS_OK;
7110 NS_IMETHODIMP
7111 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7112 *aRestoring = mIsRestoringDocument;
7113 return NS_OK;
7116 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7117 bool* aRestoring) {
7118 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7119 MOZ_ASSERT(!mIsBeingDestroyed);
7121 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7122 "RestorePresentation should only be called for history loads");
7124 nsCOMPtr<nsIDocumentViewer> viewer = aSHEntry->GetDocumentViewer();
7126 nsAutoCString spec;
7127 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7128 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7129 if (uri) {
7130 uri->GetSpec(spec);
7134 *aRestoring = false;
7136 if (!viewer) {
7137 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7138 ("no saved presentation for uri: %s", spec.get()));
7139 return NS_OK;
7142 // We need to make sure the content viewer's container is this docshell.
7143 // In subframe navigation, it's possible for the docshell that the
7144 // content viewer was originally loaded into to be replaced with a
7145 // different one. We don't currently support restoring the presentation
7146 // in that case.
7148 nsCOMPtr<nsIDocShell> container;
7149 viewer->GetContainer(getter_AddRefs(container));
7150 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7151 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7152 ("No valid container, clearing presentation"));
7153 aSHEntry->SetDocumentViewer(nullptr);
7154 return NS_ERROR_FAILURE;
7157 NS_ASSERTION(mDocumentViewer != viewer, "Restoring existing presentation");
7159 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7160 ("restoring presentation from session history: %s", spec.get()));
7162 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7164 // Post an event that will remove the request after we've returned
7165 // to the event loop. This mimics the way it is called by nsIChannel
7166 // implementations.
7168 // Revoke any pending restore (just in case).
7169 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7170 "should only have one RestorePresentationEvent");
7171 mRestorePresentationEvent.Revoke();
7173 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7174 nsresult rv = Dispatch(do_AddRef(evt));
7175 if (NS_SUCCEEDED(rv)) {
7176 mRestorePresentationEvent = evt.get();
7177 // The rest of the restore processing will happen on our event
7178 // callback.
7179 *aRestoring = true;
7182 return rv;
7185 namespace {
7186 class MOZ_STACK_CLASS PresentationEventForgetter {
7187 public:
7188 explicit PresentationEventForgetter(
7189 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7190 aRestorePresentationEvent)
7191 : mRestorePresentationEvent(aRestorePresentationEvent),
7192 mEvent(aRestorePresentationEvent.get()) {}
7194 ~PresentationEventForgetter() { Forget(); }
7196 void Forget() {
7197 if (mRestorePresentationEvent.get() == mEvent) {
7198 mRestorePresentationEvent.Forget();
7199 mEvent = nullptr;
7203 private:
7204 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7205 mRestorePresentationEvent;
7206 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7209 } // namespace
7211 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7212 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7215 nsresult nsDocShell::RestoreFromHistory() {
7216 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7217 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7218 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7220 // This section of code follows the same ordering as CreateDocumentViewer.
7221 if (!mLSHE) {
7222 return NS_ERROR_FAILURE;
7225 nsCOMPtr<nsIDocumentViewer> viewer = mLSHE->GetDocumentViewer();
7226 if (!viewer) {
7227 return NS_ERROR_FAILURE;
7230 if (mSavingOldViewer) {
7231 // We determined that it was safe to cache the document presentation
7232 // at the time we initiated the new load. We need to check whether
7233 // it's still safe to do so, since there may have been DOM mutations
7234 // or new requests initiated.
7235 RefPtr<Document> doc = viewer->GetDocument();
7236 nsIRequest* request = nullptr;
7237 if (doc) {
7238 request = doc->GetChannel();
7240 mSavingOldViewer = CanSavePresentation(
7241 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
7244 // Protect against mLSHE going away via a load triggered from
7245 // pagehide or unload.
7246 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7248 // Make sure to blow away our mLoadingURI just in case. No loads
7249 // from inside this pagehide.
7250 mLoadingURI = nullptr;
7252 // Notify the old content viewer that it's being hidden.
7253 FirePageHideNotification(!mSavingOldViewer);
7254 // pagehide notification might destroy this docshell.
7255 if (mIsBeingDestroyed) {
7256 return NS_ERROR_DOCSHELL_DYING;
7259 // If mLSHE was changed as a result of the pagehide event, then
7260 // something else was loaded. Don't finish restoring.
7261 if (mLSHE != origLSHE) {
7262 return NS_OK;
7265 // Add the request to our load group. We do this before swapping out
7266 // the content viewers so that consumers of STATE_START can access
7267 // the old document. We only deal with the toplevel load at this time --
7268 // to be consistent with normal document loading, subframes cannot start
7269 // loading until after data arrives, which is after STATE_START completes.
7271 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7272 mRestorePresentationEvent.get();
7273 Stop();
7274 // Make sure we're still restoring the same presentation.
7275 // If we aren't, docshell is in process doing another load already.
7276 NS_ENSURE_STATE(currentPresentationRestoration ==
7277 mRestorePresentationEvent.get());
7278 BeginRestore(viewer, true);
7279 NS_ENSURE_STATE(currentPresentationRestoration ==
7280 mRestorePresentationEvent.get());
7281 forgetter.Forget();
7283 // Set mFiredUnloadEvent = false so that the unload handler for the
7284 // *new* document will fire.
7285 mFiredUnloadEvent = false;
7287 mURIResultedInDocument = true;
7288 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7289 if (rootSH) {
7290 mPreviousEntryIndex = rootSH->Index();
7291 rootSH->LegacySHistory()->UpdateIndex();
7292 mLoadedEntryIndex = rootSH->Index();
7293 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7294 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7295 mLoadedEntryIndex));
7298 // Rather than call Embed(), we will retrieve the viewer from the session
7299 // history entry and swap it in.
7300 // XXX can we refactor this so that we can just call Embed()?
7301 PersistLayoutHistoryState();
7302 nsresult rv;
7303 if (mDocumentViewer) {
7304 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7305 if (mOSHE) {
7306 mOSHE->SyncPresentationState();
7308 mSavingOldViewer = false;
7312 mSavedRefreshURIList = nullptr;
7314 // In cases where we use a transient about:blank viewer between loads,
7315 // we never show the transient viewer, so _its_ previous viewer is never
7316 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7317 // before we grab the root view sibling, so that we don't grab a view
7318 // that's about to go away.
7320 if (mDocumentViewer) {
7321 // Make sure to hold a strong ref to previousViewer here while we
7322 // drop the reference to it from mDocumentViewer.
7323 nsCOMPtr<nsIDocumentViewer> previousViewer =
7324 mDocumentViewer->GetPreviousViewer();
7325 if (previousViewer) {
7326 mDocumentViewer->SetPreviousViewer(nullptr);
7327 previousViewer->Destroy();
7331 // Save off the root view's parent and sibling so that we can insert the
7332 // new content viewer's root view at the same position. Also save the
7333 // bounds of the root view's widget.
7335 nsView* rootViewSibling = nullptr;
7336 nsView* rootViewParent = nullptr;
7337 nsIntRect newBounds(0, 0, 0, 0);
7339 PresShell* oldPresShell = GetPresShell();
7340 if (oldPresShell) {
7341 nsViewManager* vm = oldPresShell->GetViewManager();
7342 if (vm) {
7343 nsView* oldRootView = vm->GetRootView();
7345 if (oldRootView) {
7346 rootViewSibling = oldRootView->GetNextSibling();
7347 rootViewParent = oldRootView->GetParent();
7349 mDocumentViewer->GetBounds(newBounds);
7354 nsCOMPtr<nsIContent> container;
7355 RefPtr<Document> sibling;
7356 if (rootViewParent && rootViewParent->GetParent()) {
7357 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7358 container = frame ? frame->GetContent() : nullptr;
7360 if (rootViewSibling) {
7361 nsIFrame* frame = rootViewSibling->GetFrame();
7362 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7365 // Transfer ownership to mDocumentViewer. By ensuring that either the
7366 // docshell or the session history, but not both, have references to the
7367 // content viewer, we prevent the viewer from being torn down after
7368 // Destroy() is called.
7370 if (mDocumentViewer) {
7371 mDocumentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7372 viewer->SetPreviousViewer(mDocumentViewer);
7374 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
7375 // We don't plan to save a viewer in mOSHE; tell it to drop
7376 // any other state it's holding.
7377 mOSHE->SyncPresentationState();
7380 // Order the mDocumentViewer setup just like Embed does.
7381 mDocumentViewer = nullptr;
7383 // Now that we're about to switch documents, forget all of our children.
7384 // Note that we cached them as needed up in CaptureState above.
7385 DestroyChildren();
7387 mDocumentViewer.swap(viewer);
7389 // Grab all of the related presentation from the SHEntry now.
7390 // Clearing the viewer from the SHEntry will clear all of this state.
7391 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7392 mLSHE->SetWindowState(nullptr);
7394 bool sticky = mLSHE->GetSticky();
7396 RefPtr<Document> document = mDocumentViewer->GetDocument();
7398 nsCOMArray<nsIDocShellTreeItem> childShells;
7399 int32_t i = 0;
7400 nsCOMPtr<nsIDocShellTreeItem> child;
7401 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7402 child) {
7403 childShells.AppendObject(child);
7406 // get the previous content viewer size
7407 nsIntRect oldBounds(0, 0, 0, 0);
7408 mLSHE->GetViewerBounds(oldBounds);
7410 // Restore the refresh URI list. The refresh timers will be restarted
7411 // when EndPageLoad() is called.
7412 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7414 // Reattach to the window object.
7415 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7416 rv = mDocumentViewer->Open(windowState, mLSHE);
7417 mIsRestoringDocument = false;
7419 // Hack to keep nsDocShellEditorData alive across the
7420 // SetContentViewer(nullptr) call below.
7421 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7423 // Now remove it from the cached presentation.
7424 mLSHE->SetDocumentViewer(nullptr);
7425 mEODForCurrentDocument = false;
7427 mLSHE->SetEditorData(data.release());
7429 #ifdef DEBUG
7431 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7432 nsCOMPtr<nsIDocShellTreeItem> childShell;
7433 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7434 NS_ASSERTION(!refreshURIs && !childShell,
7435 "SHEntry should have cleared presentation state");
7437 #endif
7439 // Restore the sticky state of the viewer. The viewer has set this state
7440 // on the history entry in Destroy() just before marking itself non-sticky,
7441 // to avoid teardown of the presentation.
7442 mDocumentViewer->SetSticky(sticky);
7444 NS_ENSURE_SUCCESS(rv, rv);
7446 // mLSHE is now our currently-loaded document.
7447 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7449 // We aren't going to restore any items from the LayoutHistoryState,
7450 // but we don't want them to stay around in case the page is reloaded.
7451 SetLayoutHistoryState(nullptr);
7453 // This is the end of our Embed() replacement
7455 mSavingOldViewer = false;
7456 mEODForCurrentDocument = false;
7458 if (document) {
7459 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7460 if (parent) {
7461 RefPtr<Document> d = parent->GetDocument();
7462 if (d) {
7463 if (d->EventHandlingSuppressed()) {
7464 document->SuppressEventHandling(d->EventHandlingSuppressed());
7469 // Use the uri from the mLSHE we had when we entered this function
7470 // (which need not match the document's URI if anchors are involved),
7471 // since that's the history entry we're loading. Note that if we use
7472 // origLSHE we don't have to worry about whether the entry in question
7473 // is still mLSHE or whether it's now mOSHE.
7474 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7475 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7476 /* aIsInitialAboutBlank */ false,
7477 /* aLocationFlags */ 0);
7480 // This is the end of our CreateDocumentViewer() replacement.
7481 // Now we simulate a load. First, we restore the state of the javascript
7482 // window object.
7483 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7484 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7486 // Now, dispatch a title change event which would happen as the
7487 // <head> is parsed.
7488 document->NotifyPossibleTitleChange(false);
7490 // Now we simulate appending child docshells for subframes.
7491 for (i = 0; i < childShells.Count(); ++i) {
7492 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7493 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7495 // Make sure to not clobber the state of the child. Since AddChild
7496 // always clobbers it, save it off first.
7497 bool allowRedirects;
7498 childShell->GetAllowMetaRedirects(&allowRedirects);
7500 bool allowSubframes;
7501 childShell->GetAllowSubframes(&allowSubframes);
7503 bool allowImages;
7504 childShell->GetAllowImages(&allowImages);
7506 bool allowMedia = childShell->GetAllowMedia();
7508 bool allowDNSPrefetch;
7509 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7511 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7512 bool allowContentRetargetingOnChildren =
7513 childShell->GetAllowContentRetargetingOnChildren();
7515 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7516 // the child inherits our state. Among other things, this means that the
7517 // child inherits our mPrivateBrowsingId, which is what we want.
7518 AddChild(childItem);
7520 childShell->SetAllowMetaRedirects(allowRedirects);
7521 childShell->SetAllowSubframes(allowSubframes);
7522 childShell->SetAllowImages(allowImages);
7523 childShell->SetAllowMedia(allowMedia);
7524 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7525 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7526 childShell->SetAllowContentRetargetingOnChildren(
7527 allowContentRetargetingOnChildren);
7529 rv = childShell->BeginRestore(nullptr, false);
7530 NS_ENSURE_SUCCESS(rv, rv);
7533 // Make sure to restore the window state after adding the child shells back
7534 // to the tree. This is necessary for Thaw() and Resume() to propagate
7535 // properly.
7536 rv = privWin->RestoreWindowState(windowState);
7537 NS_ENSURE_SUCCESS(rv, rv);
7539 RefPtr<PresShell> presShell = GetPresShell();
7541 // We may be displayed on a different monitor (or in a different
7542 // HiDPI mode) than when we got into the history list. So we need
7543 // to check if this has happened. See bug 838239.
7545 // Because the prescontext normally handles resolution changes via
7546 // a runnable (see nsPresContext::UIResolutionChanged), its device
7547 // context won't be -immediately- updated as a result of calling
7548 // presShell->BackingScaleFactorChanged().
7550 // But we depend on that device context when adjusting the view size
7551 // via mDocumentViewer->SetBounds(newBounds) below. So we need to
7552 // explicitly tell it to check for changed resolution here.
7553 if (presShell) {
7554 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7555 if (pc->DeviceContext()->CheckDPIChange()) {
7556 presShell->BackingScaleFactorChanged();
7558 // Recompute zoom and text-zoom and such.
7559 pc->RecomputeBrowsingContextDependentData();
7562 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7563 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7565 // Insert the new root view at the correct location in the view tree.
7566 if (container) {
7567 nsSubDocumentFrame* subDocFrame =
7568 do_QueryFrame(container->GetPrimaryFrame());
7569 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7570 } else {
7571 rootViewParent = nullptr;
7573 if (sibling && sibling->GetPresShell() &&
7574 sibling->GetPresShell()->GetViewManager()) {
7575 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7576 } else {
7577 rootViewSibling = nullptr;
7579 if (rootViewParent && newRootView &&
7580 newRootView->GetParent() != rootViewParent) {
7581 nsViewManager* parentVM = rootViewParent->GetViewManager();
7582 if (parentVM) {
7583 // InsertChild(parent, child, sib, true) inserts the child after
7584 // sib in content order, which is before sib in view order. BUT
7585 // when sib is null it inserts at the end of the the document
7586 // order, i.e., first in view order. But when oldRootSibling is
7587 // null, the old root as at the end of the view list --- last in
7588 // content order --- and we want to call InsertChild(parent, child,
7589 // nullptr, false) in that case.
7590 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7591 rootViewSibling ? true : false);
7593 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7594 "error in InsertChild");
7598 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7600 // If parent is suspended, increase suspension count.
7601 // This can't be done as early as event suppression since this
7602 // depends on docshell tree.
7603 privWinInner->SyncStateFromParentWindow();
7605 // Now that all of the child docshells have been put into place, we can
7606 // restart the timers for the window and all of the child frames.
7607 privWinInner->Resume();
7609 // Now that we have found the inner window of the page restored
7610 // from the history, we have to make sure that
7611 // performance.navigation.type is 2.
7612 Performance* performance = privWinInner->GetPerformance();
7613 if (performance) {
7614 performance->GetDOMTiming()->NotifyRestoreStart();
7617 // Restore the refresh URI list. The refresh timers will be restarted
7618 // when EndPageLoad() is called.
7619 mRefreshURIList = refreshURIList;
7621 // Meta-refresh timers have been restarted for this shell, but not
7622 // for our children. Walk the child shells and restart their timers.
7623 for (auto* childDocLoader : mChildList.ForwardRange()) {
7624 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7625 if (child) {
7626 child->ResumeRefreshURIs();
7630 // Make sure this presentation is the same size as the previous
7631 // presentation. If this is not the same size we showed it at last time,
7632 // then we need to resize the widget.
7634 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7635 // presentation had the infobar visible, then we will resize the new
7636 // presentation to that smaller size. However, firing the locationchanged
7637 // event will hide the infobar, which will immediately resize the window
7638 // back to the larger size. A future optimization might be to restore
7639 // the presentation at the "wrong" size, then fire the locationchanged
7640 // event and check whether the docshell's new size is the same as the
7641 // cached viewer size (skipping the resize if they are equal).
7643 if (newRootView) {
7644 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7645 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7646 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7647 newBounds.width, newBounds.height));
7648 mDocumentViewer->SetBounds(newBounds);
7649 } else {
7650 nsIScrollableFrame* rootScrollFrame =
7651 presShell->GetRootScrollFrameAsScrollable();
7652 if (rootScrollFrame) {
7653 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7658 // The FinishRestore call below can kill these, null them out so we don't
7659 // have invalid pointer lying around.
7660 newRootView = rootViewSibling = rootViewParent = nullptr;
7661 newVM = nullptr;
7663 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7664 // update it.
7665 if (oldPresShell && presShell &&
7666 presShell->IsUnderHiddenEmbedderElement() !=
7667 oldPresShell->IsUnderHiddenEmbedderElement()) {
7668 presShell->SetIsUnderHiddenEmbedderElement(
7669 oldPresShell->IsUnderHiddenEmbedderElement());
7672 // Simulate the completion of the load.
7673 nsDocShell::FinishRestore();
7675 // Restart plugins, and paint the content.
7676 if (presShell) {
7677 presShell->Thaw();
7680 return privWin->FireDelayedDOMEvents(true);
7683 nsresult nsDocShell::CreateDocumentViewer(const nsACString& aContentType,
7684 nsIRequest* aRequest,
7685 nsIStreamListener** aContentHandler) {
7686 *aContentHandler = nullptr;
7688 if (!mTreeOwner || mIsBeingDestroyed) {
7689 // If we don't have a tree owner, then we're in the process of being
7690 // destroyed. Rather than continue trying to load something, just give up.
7691 return NS_ERROR_DOCSHELL_DYING;
7694 if (!mBrowsingContext->AncestorsAreCurrent() ||
7695 mBrowsingContext->IsInBFCache()) {
7696 mBrowsingContext->RemoveRootFromBFCacheSync();
7697 return NS_ERROR_NOT_AVAILABLE;
7700 // Can we check the content type of the current content viewer
7701 // and reuse it without destroying it and re-creating it?
7703 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7705 // Instantiate the content viewer object
7706 nsCOMPtr<nsIDocumentViewer> viewer;
7707 nsresult rv = NewDocumentViewerObj(aContentType, aRequest, mLoadGroup,
7708 aContentHandler, getter_AddRefs(viewer));
7710 if (NS_FAILED(rv)) {
7711 return rv;
7714 // Notify the current document that it is about to be unloaded!!
7716 // It is important to fire the unload() notification *before* any state
7717 // is changed within the DocShell - otherwise, javascript will get the
7718 // wrong information :-(
7721 if (mSavingOldViewer) {
7722 // We determined that it was safe to cache the document presentation
7723 // at the time we initiated the new load. We need to check whether
7724 // it's still safe to do so, since there may have been DOM mutations
7725 // or new requests initiated.
7726 RefPtr<Document> doc = viewer->GetDocument();
7727 mSavingOldViewer = CanSavePresentation(
7728 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
7731 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7733 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7734 if (aOpenedChannel) {
7735 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7738 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7739 // it before we do call Embed.
7740 nsCOMPtr<nsIURI> previousURI = mCurrentURI;
7742 FirePageHideNotification(!mSavingOldViewer);
7743 if (mIsBeingDestroyed) {
7744 // Force to stop the newly created orphaned viewer.
7745 viewer->Stop();
7746 return NS_ERROR_DOCSHELL_DYING;
7748 mLoadingURI = nullptr;
7750 // Set mFiredUnloadEvent = false so that the unload handler for the
7751 // *new* document will fire.
7752 mFiredUnloadEvent = false;
7754 // we've created a new document so go ahead and call
7755 // OnNewURI(), but don't fire OnLocationChange()
7756 // notifications before we've called Embed(). See bug 284993.
7757 mURIResultedInDocument = true;
7758 bool errorOnLocationChangeNeeded = false;
7759 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7760 nsCOMPtr<nsIURI> failedURI;
7762 if (mLoadType == LOAD_ERROR_PAGE) {
7763 // We need to set the SH entry and our current URI here and not
7764 // at the moment we load the page. We want the same behavior
7765 // of Stop() as for a normal page load. See bug 514232 for details.
7767 // Revert mLoadType to load type to state the page load failed,
7768 // following function calls need it.
7769 mLoadType = mFailedLoadType;
7771 Document* doc = viewer->GetDocument();
7772 if (doc) {
7773 doc->SetFailedChannel(failedChannel);
7776 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7777 if (failedChannel) {
7778 // Make sure we have a URI to set currentURI.
7779 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7780 } else {
7781 // if there is no failed channel we have to explicitly provide
7782 // a triggeringPrincipal for the history entry.
7783 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7786 if (!failedURI) {
7787 failedURI = mFailedURI;
7789 if (!failedURI) {
7790 // We need a URI object to store a session history entry, so make up a URI
7791 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7794 // When we don't have failedURI, something wrong will happen. See
7795 // bug 291876.
7796 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7798 mFailedChannel = nullptr;
7799 mFailedURI = nullptr;
7801 // Create an shistory entry for the old load.
7802 if (failedURI) {
7803 errorOnLocationChangeNeeded =
7804 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7805 nullptr, nullptr, false, false);
7808 // Be sure to have a correct mLSHE, it may have been cleared by
7809 // EndPageLoad. See bug 302115.
7810 ChildSHistory* shistory = GetSessionHistory();
7811 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7812 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7813 if (idx == -1) {
7814 idx = shistory->Index();
7816 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7819 mLoadType = LOAD_ERROR_PAGE;
7822 nsCOMPtr<nsIURI> finalURI;
7823 // If this a redirect, use the final url (uri)
7824 // else use the original url
7826 // Note that this should match what documents do (see Document::Reset).
7827 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7829 bool onLocationChangeNeeded = false;
7830 if (finalURI) {
7831 // Pass false for aCloneSHChildren, since we're loading a new page here.
7832 onLocationChangeNeeded = OnNewURI(finalURI, aOpenedChannel, nullptr,
7833 nullptr, nullptr, nullptr, true, false);
7836 // let's try resetting the load group if we need to...
7837 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7838 NS_ENSURE_SUCCESS(
7839 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7840 NS_ERROR_FAILURE);
7842 if (currentLoadGroup != mLoadGroup) {
7843 nsLoadFlags loadFlags = 0;
7845 // Cancel any URIs that are currently loading...
7846 // XXX: Need to do this eventually Stop();
7848 // Retarget the document to this loadgroup...
7850 /* First attach the channel to the right loadgroup
7851 * and then remove from the old loadgroup. This
7852 * puts the notifications in the right order and
7853 * we don't null-out mLSHE in OnStateChange() for
7854 * all redirected urls
7856 aOpenedChannel->SetLoadGroup(mLoadGroup);
7858 // Mark the channel as being a document URI...
7859 aOpenedChannel->GetLoadFlags(&loadFlags);
7860 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7861 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7862 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7863 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7866 aOpenedChannel->SetLoadFlags(loadFlags);
7868 mLoadGroup->AddRequest(aRequest, nullptr);
7869 if (currentLoadGroup) {
7870 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7873 // Update the notification callbacks, so that progress and
7874 // status information are sent to the right docshell...
7875 aOpenedChannel->SetNotificationCallbacks(this);
7878 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
7879 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
7880 aOpenedChannel, previousURI),
7881 NS_ERROR_FAILURE);
7883 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
7884 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
7887 mSavedRefreshURIList = nullptr;
7888 mSavingOldViewer = false;
7889 mEODForCurrentDocument = false;
7891 // if this document is part of a multipart document,
7892 // the ID can be used to distinguish it from the other parts.
7893 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
7894 if (multiPartChannel) {
7895 if (PresShell* presShell = GetPresShell()) {
7896 if (Document* doc = presShell->GetDocument()) {
7897 uint32_t partID;
7898 multiPartChannel->GetPartID(&partID);
7899 doc->SetPartID(partID);
7904 if (errorOnLocationChangeNeeded) {
7905 FireOnLocationChange(this, failedChannel, failedURI,
7906 LOCATION_CHANGE_ERROR_PAGE);
7907 } else if (onLocationChangeNeeded) {
7908 uint32_t locationFlags =
7909 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
7910 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
7913 return NS_OK;
7916 nsresult nsDocShell::NewDocumentViewerObj(const nsACString& aContentType,
7917 nsIRequest* aRequest,
7918 nsILoadGroup* aLoadGroup,
7919 nsIStreamListener** aContentHandler,
7920 nsIDocumentViewer** aViewer) {
7921 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7923 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
7924 nsContentUtils::FindInternalDocumentViewer(aContentType);
7925 if (!docLoaderFactory) {
7926 return NS_ERROR_FAILURE;
7929 // Now create an instance of the content viewer nsLayoutDLF makes the
7930 // determination if it should be a "view-source" instead of "view"
7931 nsresult rv = docLoaderFactory->CreateInstance(
7932 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
7933 aContentHandler, aViewer);
7934 NS_ENSURE_SUCCESS(rv, rv);
7936 (*aViewer)->SetContainer(this);
7937 return NS_OK;
7940 nsresult nsDocShell::SetupNewViewer(nsIDocumentViewer* aNewViewer,
7941 WindowGlobalChild* aWindowActor) {
7942 MOZ_ASSERT(!mIsBeingDestroyed);
7945 // Copy content viewer state from previous or parent content viewer.
7947 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7949 // Do NOT to maintain a reference to the old content viewer outside
7950 // of this "copying" block, or it will not be destroyed until the end of
7951 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7953 // In this block of code, if we get an error result, we return it
7954 // but if we get a null pointer, that's perfectly legal for parent
7955 // and parentContentViewer.
7958 int32_t x = 0;
7959 int32_t y = 0;
7960 int32_t cx = 0;
7961 int32_t cy = 0;
7963 // This will get the size from the current content viewer or from the
7964 // Init settings
7965 DoGetPositionAndSize(&x, &y, &cx, &cy);
7967 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7968 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
7969 NS_ERROR_FAILURE);
7970 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7972 const Encoding* reloadEncoding = nullptr;
7973 int32_t reloadEncodingSource = kCharsetUninitialized;
7974 // |newMUDV| also serves as a flag to set the data from the above vars
7975 nsCOMPtr<nsIDocumentViewer> newViewer;
7977 if (mDocumentViewer || parent) {
7978 nsCOMPtr<nsIDocumentViewer> oldViewer;
7979 if (mDocumentViewer) {
7980 // Get any interesting state from old content viewer
7981 // XXX: it would be far better to just reuse the document viewer ,
7982 // since we know we're just displaying the same document as before
7983 oldViewer = mDocumentViewer;
7985 // Tell the old content viewer to hibernate in session history when
7986 // it is destroyed.
7988 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7989 if (mOSHE) {
7990 mOSHE->SyncPresentationState();
7992 mSavingOldViewer = false;
7994 } else {
7995 // No old content viewer, so get state from parent's content viewer
7996 parent->GetDocViewer(getter_AddRefs(oldViewer));
7999 if (oldViewer) {
8000 newViewer = aNewViewer;
8001 if (newViewer) {
8002 reloadEncoding =
8003 oldViewer->GetReloadEncodingAndSource(&reloadEncodingSource);
8008 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8009 bool isUnderHiddenEmbedderElement = false;
8010 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8011 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
8012 if (viewer) {
8013 // Stop any activity that may be happening in the old document before
8014 // releasing it...
8015 viewer->Stop();
8017 // Try to extract the canvas background color from the old
8018 // presentation shell, so we can use it for the next document.
8019 if (PresShell* presShell = viewer->GetPresShell()) {
8020 bgcolor = presShell->GetCanvasBackground();
8021 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
8024 viewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8025 aNewViewer->SetPreviousViewer(viewer);
8027 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
8028 // We don't plan to save a viewer in mOSHE; tell it to drop
8029 // any other state it's holding.
8030 mOSHE->SyncPresentationState();
8033 mDocumentViewer = nullptr;
8035 // Now that we're about to switch documents, forget all of our children.
8036 // Note that we cached them as needed up in CaptureState above.
8037 DestroyChildren();
8039 mDocumentViewer = aNewViewer;
8041 nsCOMPtr<nsIWidget> widget;
8042 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8044 nsIntRect bounds(x, y, cx, cy);
8046 mDocumentViewer->SetNavigationTiming(mTiming);
8048 if (NS_FAILED(mDocumentViewer->Init(widget, bounds, aWindowActor))) {
8049 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
8050 viewer->Close(nullptr);
8051 viewer->Destroy();
8052 mDocumentViewer = nullptr;
8053 SetCurrentURIInternal(nullptr);
8054 NS_WARNING("ContentViewer Initialization failed");
8055 return NS_ERROR_FAILURE;
8058 // If we have old state to copy, set the old state onto the new content
8059 // viewer
8060 if (newViewer) {
8061 newViewer->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8064 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8066 // Stuff the bgcolor from the old pres shell into the new
8067 // pres shell. This improves page load continuity.
8068 if (RefPtr<PresShell> presShell = mDocumentViewer->GetPresShell()) {
8069 presShell->SetCanvasBackground(bgcolor);
8070 presShell->ActivenessMaybeChanged();
8071 if (isUnderHiddenEmbedderElement) {
8072 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8076 // XXX: It looks like the LayoutState gets restored again in Embed()
8077 // right after the call to SetupNewViewer(...)
8079 // We don't show the mDocumentViewer yet, since we want to draw the old page
8080 // until we have enough of the new page to show. Just return with the new
8081 // viewer still set to hidden.
8083 return NS_OK;
8086 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8087 SessionHistoryInfo* aInfo) {
8088 NS_ENSURE_TRUE_VOID(mDocumentViewer);
8090 RefPtr<Document> document = GetDocument();
8091 NS_ENSURE_TRUE_VOID(document);
8093 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8094 if (mozilla::SessionHistoryInParent()) {
8095 // If aInfo is null, just set the document's state object to null.
8096 if (aInfo) {
8097 scContainer = aInfo->GetStateData();
8099 MOZ_LOG(gSHLog, LogLevel::Debug,
8100 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8101 } else {
8102 if (aShEntry) {
8103 scContainer = aShEntry->GetStateData();
8105 // If aShEntry is null, just set the document's state object to null.
8109 // It's OK for scContainer too be null here; that just means there's no
8110 // state data associated with this history entry.
8111 document->SetStateObject(scContainer);
8114 nsresult nsDocShell::CheckLoadingPermissions() {
8115 // This method checks whether the caller may load content into
8116 // this docshell. Even though we've done our best to hide windows
8117 // from code that doesn't have the right to access them, it's
8118 // still possible for an evil site to open a window and access
8119 // frames in the new window through window.frames[] (which is
8120 // allAccess for historic reasons), so we still need to do this
8121 // check on load.
8122 nsresult rv = NS_OK;
8124 if (!IsSubframe()) {
8125 // We're not a frame. Permit all loads.
8126 return rv;
8129 // Note - The check for a current JSContext here isn't necessarily sensical.
8130 // It's just designed to preserve the old semantics during a mass-conversion
8131 // patch.
8132 if (!nsContentUtils::GetCurrentJSContext()) {
8133 return NS_OK;
8136 // Check if the caller is from the same origin as this docshell,
8137 // or any of its ancestors.
8138 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8139 bc = bc->GetParent()) {
8140 // If the BrowsingContext is not in process, then it
8141 // is true by construction that its principal will not
8142 // subsume the current docshell principal.
8143 if (!bc->IsInProcess()) {
8144 continue;
8147 nsCOMPtr<nsIScriptGlobalObject> sgo =
8148 bc->GetDocShell()->GetScriptGlobalObject();
8149 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8151 nsIPrincipal* p;
8152 if (!sop || !(p = sop->GetPrincipal())) {
8153 return NS_ERROR_UNEXPECTED;
8156 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8157 // Same origin, permit load
8158 return NS_OK;
8162 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8165 //*****************************************************************************
8166 // nsDocShell: Site Loading
8167 //*****************************************************************************
8169 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8170 bool aInPrivateBrowsing) {
8171 if (XRE_IsContentProcess()) {
8172 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8173 if (contentChild) {
8174 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8176 return;
8179 #ifdef MOZ_PLACES
8180 nsCOMPtr<nsIFaviconService> favSvc =
8181 do_GetService("@mozilla.org/browser/favicon-service;1");
8182 if (favSvc) {
8183 favSvc->CopyFavicons(aOldURI, aNewURI,
8184 aInPrivateBrowsing
8185 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8186 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8187 nullptr);
8189 #endif
8192 class InternalLoadEvent : public Runnable {
8193 public:
8194 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8195 : mozilla::Runnable("InternalLoadEvent"),
8196 mDocShell(aDocShell),
8197 mLoadState(aLoadState) {
8198 // For events, both target and filename should be the version of "null" they
8199 // expect. By the time the event is fired, both window targeting and file
8200 // downloading have been handled, so we should never have an internal load
8201 // event that retargets or had a download.
8202 mLoadState->SetTarget(u""_ns);
8203 mLoadState->SetFileName(VoidString());
8206 NS_IMETHOD
8207 Run() override {
8208 #ifndef ANDROID
8209 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8210 "InternalLoadEvent: Should always have a principal here");
8211 #endif
8212 return mDocShell->InternalLoad(mLoadState);
8215 private:
8216 RefPtr<nsDocShell> mDocShell;
8217 RefPtr<nsDocShellLoadState> mLoadState;
8221 * Returns true if we started an asynchronous load (i.e., from the network), but
8222 * the document we're loading there hasn't yet become this docshell's active
8223 * document.
8225 * When JustStartedNetworkLoad is true, you should be careful about modifying
8226 * mLoadType and mLSHE. These are both set when the asynchronous load first
8227 * starts, and the load expects that, when it eventually runs InternalLoad,
8228 * mLoadType and mLSHE will have their original values.
8230 bool nsDocShell::JustStartedNetworkLoad() {
8231 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8234 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8235 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8236 // <embed>, etc)
8238 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8239 // later when we call DoURILoad.
8240 nsContentPolicyType nsDocShell::DetermineContentType() {
8241 if (!IsSubframe()) {
8242 return nsIContentPolicy::TYPE_DOCUMENT;
8245 const auto& maybeEmbedderElementType =
8246 GetBrowsingContext()->GetEmbedderElementType();
8247 if (!maybeEmbedderElementType) {
8248 // If the EmbedderElementType hasn't been set yet, just assume we're
8249 // an iframe since that's more common.
8250 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8253 return maybeEmbedderElementType->EqualsLiteral("iframe")
8254 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8255 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8258 bool nsDocShell::NoopenerForceEnabled() {
8259 // If current's top-level browsing context's active document's
8260 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8261 // if currentDoc's origin is not same origin with currentDoc's top-level
8262 // origin, noopener is force enabled, and name is cleared to "_blank".
8263 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8264 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8265 topPolicy ==
8266 nsILoadInfo::
8267 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8268 !mBrowsingContext->SameOriginWithTop();
8271 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8272 MOZ_ASSERT(aLoadState, "need a load state!");
8273 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8274 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8275 "should not have picked target yet");
8277 nsresult rv = NS_OK;
8278 RefPtr<BrowsingContext> targetContext;
8280 // Only _self, _parent, and _top are supported in noopener case. But we
8281 // have to be careful to not apply that to the noreferrer case. See bug
8282 // 1358469.
8283 bool allowNamedTarget =
8284 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8285 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8286 if (allowNamedTarget ||
8287 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8288 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8289 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8290 Document* document = GetDocument();
8291 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8292 WindowGlobalChild* wgc = document->GetWindowGlobalChild();
8293 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
8294 targetContext = wgc->FindBrowsingContextWithName(
8295 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8298 if (!targetContext) {
8299 // If the targetContext doesn't exist, then this is a new docShell and we
8300 // should consider this a TYPE_DOCUMENT load
8302 // For example, when target="_blank"
8304 // If there's no targetContext, that means we are about to create a new
8305 // window. Perform a content policy check before creating the window. Please
8306 // note for all other docshell loads content policy checks are performed
8307 // within the contentSecurityManager when the channel is about to be
8308 // openend.
8309 nsISupports* requestingContext = nullptr;
8310 if (XRE_IsContentProcess()) {
8311 // In e10s the child process doesn't have access to the element that
8312 // contains the browsing context (because that element is in the chrome
8313 // process). So we just pass mScriptGlobal.
8314 requestingContext = ToSupports(mScriptGlobal);
8315 } else {
8316 // This is for loading non-e10s tabs and toplevel windows of various
8317 // sorts.
8318 // For the toplevel window cases, requestingElement will be null.
8319 nsCOMPtr<Element> requestingElement =
8320 mScriptGlobal->GetFrameElementInternal();
8321 requestingContext = requestingElement;
8324 // Ideally we should use the same loadinfo as within DoURILoad which
8325 // should match this one when both are applicable.
8326 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
8327 new LoadInfo(mScriptGlobal, aLoadState->URI(),
8328 aLoadState->TriggeringPrincipal(), requestingContext,
8329 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8331 // Since Content Policy checks are performed within docShell as well as
8332 // the ContentSecurityManager we need a reliable way to let certain
8333 // nsIContentPolicy consumers ignore duplicate calls.
8334 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8336 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8337 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8338 &shouldLoad);
8340 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8341 if (NS_SUCCEEDED(rv)) {
8342 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8343 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8345 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8346 return NS_ERROR_BLOCKED_BY_POLICY;
8350 return NS_ERROR_CONTENT_BLOCKED;
8355 // Resolve the window target before going any further...
8356 // If the load has been targeted to another DocShell, then transfer the
8357 // load to it...
8360 // We've already done our owner-inheriting. Mask out that bit, so we
8361 // don't try inheriting an owner from the target window if we came up
8362 // with a null owner above.
8363 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8365 if (!targetContext) {
8366 // If the docshell's document is sandboxed, only open a new window
8367 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8368 // (i.e. if allow-popups is specified)
8369 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8370 Document* doc = mDocumentViewer->GetDocument();
8372 const bool isDocumentAuxSandboxed =
8373 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8375 if (isDocumentAuxSandboxed) {
8376 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8379 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8380 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8382 RefPtr<BrowsingContext> newBC;
8383 nsAutoCString spec;
8384 aLoadState->URI()->GetSpec(spec);
8386 // If we are a noopener load, we just hand the whole thing over to our
8387 // window.
8388 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8389 NoopenerForceEnabled()) {
8390 // Various asserts that we know to hold because NO_OPENER loads can only
8391 // happen for links.
8392 MOZ_ASSERT(!aLoadState->LoadReplace());
8393 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8394 aLoadState->TriggeringPrincipal());
8395 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8396 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8397 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8398 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8399 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8400 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8401 aLoadState->IsFormSubmission());
8402 MOZ_ASSERT(!aLoadState->HeadersStream());
8403 // If OnLinkClickSync was invoked inside the onload handler, the load
8404 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8405 // LOAD_LINK.
8406 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8407 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8408 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8409 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8411 RefPtr<nsDocShellLoadState> loadState =
8412 new nsDocShellLoadState(aLoadState->URI());
8414 // Set up our loadinfo so it will do the load as much like we would have
8415 // as possible.
8416 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8417 loadState->SetOriginalURI(aLoadState->OriginalURI());
8419 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8420 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8422 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8423 loadState->SetKeepResultPrincipalURIIfSet(
8424 aLoadState->KeepResultPrincipalURIIfSet());
8425 // LoadReplace will always be false due to asserts above, skip setting
8426 // it.
8427 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8428 loadState->SetTriggeringSandboxFlags(
8429 aLoadState->TriggeringSandboxFlags());
8430 loadState->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
8431 loadState->SetTriggeringStorageAccess(
8432 aLoadState->TriggeringStorageAccess());
8433 loadState->SetCsp(aLoadState->Csp());
8434 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8435 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8436 // Explicit principal because we do not want any guesses as to what the
8437 // principal to inherit is: it should be aTriggeringPrincipal.
8438 loadState->SetPrincipalIsExplicit(true);
8439 loadState->SetLoadType(aLoadState->LoadType());
8440 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8441 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8443 loadState->SetHasValidUserGestureActivation(
8444 aLoadState->HasValidUserGestureActivation());
8446 // Propagate POST data to the new load.
8447 loadState->SetPostDataStream(aLoadState->PostDataStream());
8448 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8450 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8451 aLoadState->Target(), // window name
8452 u""_ns, // Features
8453 loadState,
8454 true, // aForceNoOpener
8455 getter_AddRefs(newBC));
8456 MOZ_ASSERT(!newBC);
8457 return rv;
8460 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8461 aLoadState->Target(), // window name
8462 u""_ns, // Features
8463 getter_AddRefs(newBC));
8465 // In some cases the Open call doesn't actually result in a new
8466 // window being opened. We can detect these cases by examining the
8467 // document in |newBC|, if any.
8468 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8469 newBC ? newBC->GetDOMWindow() : nullptr;
8470 if (piNewWin) {
8471 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8472 if (!newDoc || newDoc->IsInitialDocument()) {
8473 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8477 if (newBC) {
8478 targetContext = newBC;
8481 NS_ENSURE_SUCCESS(rv, rv);
8482 NS_ENSURE_TRUE(targetContext, rv);
8484 // If our target BrowsingContext is still pending initialization, ignore the
8485 // navigation request targeting it.
8486 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8487 return NS_OK;
8490 aLoadState->SetTargetBrowsingContext(targetContext);
8491 if (aLoadState->IsFormSubmission()) {
8492 aLoadState->SetLoadType(
8493 GetLoadTypeForFormSubmission(targetContext, aLoadState));
8497 // Transfer the load to the target BrowsingContext... Clear the window target
8498 // name to the empty string to prevent recursive retargeting!
8500 // No window target
8501 aLoadState->SetTarget(u""_ns);
8502 // No forced download
8503 aLoadState->SetFileName(VoidString());
8504 return targetContext->InternalLoad(aLoadState);
8507 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8508 nsAutoCString result;
8509 if (NS_FAILED(aURI->GetRef(result))) {
8510 result.SetIsVoid(true);
8512 return result;
8515 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8516 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8518 bool equal = false;
8519 if (mCurrentURI &&
8520 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8521 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8522 flags |= LOCATION_CHANGE_HASHCHANGE;
8525 return flags;
8528 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8529 SameDocumentNavigationState& aState) {
8530 MOZ_ASSERT(aLoadState);
8531 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8532 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8533 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8534 LOAD_FLAGS_REPLACE_HISTORY) ||
8535 aLoadState->LoadType() == LOAD_HISTORY ||
8536 aLoadState->LoadType() == LOAD_LINK)) {
8537 return false;
8540 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8542 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8543 if (NS_SUCCEEDED(rvURINew)) {
8544 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8547 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8548 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8549 if (NS_SUCCEEDED(rvURIOld)) {
8550 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8552 if (NS_SUCCEEDED(rvURIOld)) {
8553 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8554 &aState.mSameExceptHashes))) {
8555 aState.mSameExceptHashes = false;
8560 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8561 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8562 nsCOMPtr<nsIURI> currentExposableURI =
8563 nsIOService::CreateExposableURI(currentURI);
8564 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8565 if (NS_SUCCEEDED(rvURIOld)) {
8566 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8568 if (NS_SUCCEEDED(rvURIOld)) {
8569 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8570 aLoadState->URI(), &aState.mSameExceptHashes))) {
8571 aState.mSameExceptHashes = false;
8573 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8574 // have to perform a special check here to avoid an actual navigation. If
8575 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8576 // fact that the new URI is currently http), then set mSameExceptHashes to
8577 // true and only perform a fragment navigation.
8578 if (!aState.mSameExceptHashes) {
8579 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
8580 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8581 if (!docLoadInfo->GetLoadErrorPage() &&
8582 nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8583 currentExposableURI, aLoadState->URI(), docLoadInfo)) {
8584 uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
8585 if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
8586 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
8587 // At this point the requested URI is for sure a fragment
8588 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8589 // enabled. Also it is not interfering the upgrade order of
8590 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8591 // Since we are on an HTTPS site the fragment
8592 // navigation should also be an HTTPS.
8593 // For that reason we should upgrade the URI to HTTPS.
8594 aState.mSecureUpgradeURI = true;
8595 aState.mSameExceptHashes = true;
8603 if (mozilla::SessionHistoryInParent()) {
8604 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8605 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8606 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8608 MOZ_LOG(gSHLog, LogLevel::Debug,
8609 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8610 this, aState.mHistoryNavBetweenSameDoc));
8611 } else {
8612 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8613 // We're doing a history load.
8615 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8616 &aState.mHistoryNavBetweenSameDoc);
8620 // A same document navigation happens when we navigate between two SHEntries
8621 // for the same document. We do a same document navigation under two
8622 // circumstances. Either
8624 // a) we're navigating between two different SHEntries which share a
8625 // document, or
8627 // b) we're navigating to a new shentry whose URI differs from the
8628 // current URI only in its hash, the new hash is non-empty, and
8629 // we're not doing a POST.
8631 // The restriction that the SHEntries in (a) must be different ensures
8632 // that history.go(0) and the like trigger full refreshes, rather than
8633 // same document navigations.
8634 if (!mozilla::SessionHistoryInParent()) {
8635 bool doSameDocumentNavigation =
8636 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8637 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8638 aState.mSameExceptHashes && aState.mNewURIHasRef);
8639 MOZ_LOG(gSHLog, LogLevel::Debug,
8640 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8641 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8642 return doSameDocumentNavigation;
8645 if (aState.mHistoryNavBetweenSameDoc &&
8646 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8647 return true;
8650 MOZ_LOG(
8651 gSHLog, LogLevel::Debug,
8652 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8653 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8654 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8655 !aLoadState->PostDataStream() ? "true" : "false",
8656 aState.mSameExceptHashes ? "true" : "false",
8657 aState.mNewURIHasRef ? "true" : "false"));
8658 return !aLoadState->LoadIsFromSessionHistory() &&
8659 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8660 aState.mNewURIHasRef;
8663 nsresult nsDocShell::HandleSameDocumentNavigation(
8664 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState,
8665 bool& aSameDocument) {
8666 aSameDocument = true;
8667 #ifdef DEBUG
8668 SameDocumentNavigationState state;
8669 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8670 #endif
8672 MOZ_LOG(gSHLog, LogLevel::Debug,
8673 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8674 mCurrentURI->GetSpecOrDefault().get(),
8675 aLoadState->URI()->GetSpecOrDefault().get()));
8677 RefPtr<Document> doc = GetDocument();
8678 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8679 doc->DoNotifyPossibleTitleChange();
8681 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8683 // We need to upgrade the new URI from http: to https:
8684 nsCOMPtr<nsIURI> newURI = aLoadState->URI();
8685 if (aState.mSecureUpgradeURI) {
8686 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
8687 MOZ_LOG(gSHLog, LogLevel::Debug,
8688 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
8691 if (StaticPrefs::dom_security_setdocumenturi()) {
8692 // check if aLoadState->URI(), principalURI, mCurrentURI are same origin
8693 // skip handling otherwise
8694 nsCOMPtr<nsIPrincipal> origPrincipal = doc->NodePrincipal();
8695 nsCOMPtr<nsIURI> principalURI = origPrincipal->GetURI();
8696 if (origPrincipal->GetIsNullPrincipal()) {
8697 nsCOMPtr<nsIPrincipal> precursor = origPrincipal->GetPrecursorPrincipal();
8698 if (precursor) {
8699 principalURI = precursor->GetURI();
8703 auto isLoadableViaInternet = [](nsIURI* uri) {
8704 return (uri && (net::SchemeIsHTTP(uri) || net::SchemeIsHTTPS(uri)));
8707 if (isLoadableViaInternet(principalURI) &&
8708 isLoadableViaInternet(mCurrentURI) && isLoadableViaInternet(newURI)) {
8709 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
8710 if (!NS_SUCCEEDED(
8711 ssm->CheckSameOriginURI(newURI, principalURI, false, false)) ||
8712 !NS_SUCCEEDED(ssm->CheckSameOriginURI(mCurrentURI, principalURI,
8713 false, false))) {
8714 MOZ_LOG(gSHLog, LogLevel::Debug,
8715 ("nsDocShell[%p]: possible violation of the same origin policy "
8716 "during same document navigation",
8717 this));
8718 aSameDocument = false;
8719 return NS_OK;
8724 #ifdef DEBUG
8725 if (aState.mSameExceptHashes) {
8726 bool sameExceptHashes = false;
8727 currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
8728 MOZ_ASSERT(sameExceptHashes);
8730 #endif
8732 // Save the position of the scrollers.
8733 nsPoint scrollPos = GetCurScrollPos();
8735 // Reset mLoadType to its original value once we exit this block, because this
8736 // same document navigation might have started after a normal, network load,
8737 // and we don't want to clobber its load type. See bug 737307.
8738 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8740 // If a non-same-document-navigation (i.e., a network load) is pending, make
8741 // this a replacement load, so that we don't add a SHEntry here and the
8742 // network load goes into the SHEntry it expects to.
8743 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8744 mLoadType = LOAD_NORMAL_REPLACE;
8745 } else {
8746 mLoadType = aLoadState->LoadType();
8749 mURIResultedInDocument = true;
8751 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8753 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8754 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8755 // onLocationChange() notifications to the browser to update back/forward
8756 // buttons.
8757 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8758 Nothing());
8759 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8760 mLoadingEntry.swap(oldLoadingEntry);
8761 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8762 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8763 *aLoadState->GetLoadingSessionHistoryInfo());
8764 mNeedToReportActiveAfterLoadingBecomesActive = false;
8767 // Set the doc's URI according to the new history entry's URI.
8768 doc->SetDocumentURI(newURI);
8770 /* This is a anchor traversal within the same page.
8771 * call OnNewURI() so that, this traversal will be
8772 * recorded in session and global history.
8774 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8775 newURIPartitionedPrincipalToInherit;
8776 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8777 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8778 if (mozilla::SessionHistoryInParent()) {
8779 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8780 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8781 newURIPartitionedPrincipalToInherit =
8782 mActiveEntry->GetPartitionedPrincipalToInherit();
8783 newCsp = mActiveEntry->GetCsp();
8784 } else {
8785 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8786 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8787 newURIPartitionedPrincipalToInherit =
8788 mOSHE->GetPartitionedPrincipalToInherit();
8789 newCsp = mOSHE->GetCsp();
8791 } else {
8792 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8793 newURIPrincipalToInherit = doc->NodePrincipal();
8794 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8795 newCsp = doc->GetCsp();
8798 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
8800 // Pass true for aCloneSHChildren, since we're not
8801 // changing documents here, so all of our subframes are
8802 // still relevant to the new session history entry.
8804 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8805 // flag on firing onLocationChange(...).
8806 // Anyway, aCloneSHChildren param is simply reflecting
8807 // doSameDocumentNavigation in this scope.
8809 // Note: we'll actually fire onLocationChange later, in order to preserve
8810 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8811 // 1668126)
8812 bool locationChangeNeeded = OnNewURI(
8813 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
8814 newURIPartitionedPrincipalToInherit, newCsp, true, true);
8816 nsCOMPtr<nsIInputStream> postData;
8817 uint32_t cacheKey = 0;
8819 bool scrollRestorationIsManual = false;
8820 if (!mozilla::SessionHistoryInParent()) {
8821 if (mOSHE) {
8822 /* save current position of scroller(s) (bug 59774) */
8823 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8824 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8825 // Get the postdata and page ident from the current page, if
8826 // the new load is being done via normal means. Note that
8827 // "normal means" can be checked for just by checking for
8828 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8829 // above -- it filters out some LOAD_CMD_NORMAL cases that we
8830 // wouldn't want here.
8831 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8832 postData = mOSHE->GetPostData();
8833 cacheKey = mOSHE->GetCacheKey();
8836 // Link our new SHEntry to the old SHEntry's back/forward
8837 // cache data, since the two SHEntries correspond to the
8838 // same document.
8839 if (mLSHE) {
8840 if (!aLoadState->LoadIsFromSessionHistory()) {
8841 // If we're not doing a history load, scroll restoration
8842 // should be inherited from the previous session history entry.
8843 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8844 scrollRestorationIsManual);
8846 mLSHE->AdoptBFCacheEntry(mOSHE);
8849 } else {
8850 if (mActiveEntry) {
8851 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8852 if (mBrowsingContext) {
8853 CollectWireframe();
8854 if (XRE_IsParentProcess()) {
8855 SessionHistoryEntry* entry =
8856 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8857 if (entry) {
8858 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8860 } else {
8861 mozilla::Unused << ContentChild::GetSingleton()
8862 ->SendSessionHistoryEntryScrollPosition(
8863 mBrowsingContext, scrollPos.x,
8864 scrollPos.y);
8868 if (mLoadingEntry) {
8869 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8870 // If we're not doing a history load, scroll restoration
8871 // should be inherited from the previous session history entry.
8872 // XXX This needs most probably tweaks once fragment navigation is
8873 // fixed to work with session-history-in-parent.
8874 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8875 scrollRestorationIsManual);
8880 // If we're doing a history load, use its scroll restoration state.
8881 if (aLoadState->LoadIsFromSessionHistory()) {
8882 if (mozilla::SessionHistoryInParent()) {
8883 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8884 ->mInfo.GetScrollRestorationIsManual();
8885 } else {
8886 scrollRestorationIsManual =
8887 aLoadState->SHEntry()->GetScrollRestorationIsManual();
8891 /* Assign mLSHE to mOSHE. This will either be a new entry created
8892 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8893 * loads.
8895 if (!mozilla::SessionHistoryInParent()) {
8896 if (mLSHE) {
8897 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8898 // Save the postData obtained from the previous page
8899 // in to the session history entry created for the
8900 // anchor page, so that any history load of the anchor
8901 // page will restore the appropriate postData.
8902 if (postData) {
8903 mOSHE->SetPostData(postData);
8906 // Make sure we won't just repost without hitting the
8907 // cache first
8908 if (cacheKey != 0) {
8909 mOSHE->SetCacheKey(cacheKey);
8913 /* Set the title for the SH entry for this target url so that
8914 * SH menus in go/back/forward buttons won't be empty for this.
8915 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
8916 * the code above.
8917 * Note, when session history lives in the parent process, this does not
8918 * update the title there.
8920 SetTitleOnHistoryEntry(false);
8921 } else {
8922 if (aLoadState->LoadIsFromSessionHistory()) {
8923 MOZ_LOG(
8924 gSHLog, LogLevel::Debug,
8925 ("Moving the loading entry to the active entry on nsDocShell %p to "
8926 "%s",
8927 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
8929 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
8930 if (mActiveEntry) {
8931 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
8934 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
8935 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
8936 if (currentLayoutHistoryState) {
8937 // Restore the existing nsILayoutHistoryState object, since it is
8938 // possibly being used by the layout. When doing a new load, the
8939 // shared state is copied from the existing active entry, so this
8940 // special case is needed only with the history loads.
8941 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
8944 if (cacheKey != 0) {
8945 mActiveEntry->SetCacheKey(cacheKey);
8947 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
8948 // does require a non-null uri if this is for a refresh load of the same
8949 // URI, but in that case mCurrentURI won't be null here.
8950 mBrowsingContext->SessionHistoryCommit(
8951 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
8952 true, true,
8953 /* No expiration update on the same document loads*/
8954 false, cacheKey);
8955 // FIXME Need to set postdata.
8957 // Set the title for the SH entry for this target url so that
8958 // SH menus in go/back/forward buttons won't be empty for this.
8959 // Note, when session history lives in the parent process, this does not
8960 // update the title there.
8961 SetTitleOnHistoryEntry(false);
8962 } else {
8963 Maybe<bool> scrollRestorationIsManual;
8964 if (mActiveEntry) {
8965 scrollRestorationIsManual.emplace(
8966 mActiveEntry->GetScrollRestorationIsManual());
8968 // Get the postdata and page ident from the current page, if the new
8969 // load is being done via normal means. Note that "normal means" can be
8970 // checked for just by checking for LOAD_CMD_NORMAL, given the loadType
8971 // and allowScroll check above -- it filters out some LOAD_CMD_NORMAL
8972 // cases that we wouldn't want here.
8973 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8974 postData = mActiveEntry->GetPostData();
8975 cacheKey = mActiveEntry->GetCacheKey();
8979 MOZ_LOG(gSHLog, LogLevel::Debug,
8980 ("Creating an active entry on nsDocShell %p to %s", this,
8981 newURI->GetSpecOrDefault().get()));
8982 if (mActiveEntry) {
8983 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, newURI);
8984 } else {
8985 mActiveEntry = MakeUnique<SessionHistoryInfo>(
8986 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
8987 newURIPartitionedPrincipalToInherit, newCsp, mContentTypeHint);
8990 // Save the postData obtained from the previous page in to the session
8991 // history entry created for the anchor page, so that any history load of
8992 // the anchor page will restore the appropriate postData.
8993 if (postData) {
8994 mActiveEntry->SetPostData(postData);
8997 // Make sure we won't just repost without hitting the
8998 // cache first
8999 if (cacheKey != 0) {
9000 mActiveEntry->SetCacheKey(cacheKey);
9003 // Set the title for the SH entry for this target url so that
9004 // SH menus in go/back/forward buttons won't be empty for this.
9005 mActiveEntry->SetTitle(mTitle);
9007 if (scrollRestorationIsManual.isSome()) {
9008 mActiveEntry->SetScrollRestorationIsManual(
9009 scrollRestorationIsManual.value());
9012 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9013 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
9014 } else {
9015 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
9016 // FIXME We should probably just compute mChildOffset in the parent
9017 // instead of passing it over IPC here.
9018 mBrowsingContext->SetActiveSessionHistoryEntry(
9019 Some(scrollPos), mActiveEntry.get(), mLoadType, cacheKey);
9020 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9025 if (locationChangeNeeded) {
9026 FireOnLocationChange(this, nullptr, newURI, locationChangeFlags);
9029 /* Restore the original LSHE if we were loading something
9030 * while same document navigation was initiated.
9032 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
9033 mLoadingEntry.swap(oldLoadingEntry);
9035 /* Set the title for the Global History entry for this anchor url.
9037 UpdateGlobalHistoryTitle(newURI);
9039 SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
9041 // Inform the favicon service that the favicon for oldURI also
9042 // applies to newURI.
9043 CopyFavicon(currentURI, newURI, UsePrivateBrowsing());
9045 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9046 nsCOMPtr<nsPIDOMWindowInner> win =
9047 scriptGlobal ? scriptGlobal->GetCurrentInnerWindow() : nullptr;
9049 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9050 // the function decides whether a scroll is appropriate based on the
9051 // arguments it receives. But even if we don't end up scrolling,
9052 // ScrollToAnchor performs other important tasks, such as informing
9053 // the presShell that we have a new hash. See bug 680257.
9054 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
9055 aState.mNewHash, aLoadState->LoadType());
9056 NS_ENSURE_SUCCESS(rv, rv);
9058 /* restore previous position of scroller(s), if we're moving
9059 * back in history (bug 59774)
9061 nscoord bx = 0;
9062 nscoord by = 0;
9063 bool needsScrollPosUpdate = false;
9064 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
9065 (aLoadState->LoadType() == LOAD_HISTORY ||
9066 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
9067 !scrollRestorationIsManual) {
9068 needsScrollPosUpdate = true;
9069 if (mozilla::SessionHistoryInParent()) {
9070 mActiveEntry->GetScrollPosition(&bx, &by);
9071 } else {
9072 mOSHE->GetScrollPosition(&bx, &by);
9076 // Dispatch the popstate and hashchange events, as appropriate.
9078 // The event dispatch below can cause us to re-enter script and
9079 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9080 // reference to avoid null derefs. See bug 914521.
9081 if (win) {
9082 // Fire a hashchange event URIs differ, and only in their hashes.
9083 bool doHashchange = aState.mSameExceptHashes &&
9084 (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
9085 !aState.mCurrentHash.Equals(aState.mNewHash));
9087 if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
9088 win->DispatchSyncPopState();
9091 if (needsScrollPosUpdate && win->HasActiveDocument()) {
9092 SetCurScrollPosEx(bx, by);
9095 if (doHashchange) {
9096 // Note that currentURI hasn't changed because it's on the
9097 // stack, so we can just use it directly as the old URI.
9098 win->DispatchAsyncHashchange(currentURI, newURI);
9102 return NS_OK;
9105 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
9106 nsDocShellLoadState* aLoadState) {
9107 if (!aLoadState->AllowFocusMove()) {
9108 return false;
9110 if (!aLoadState->HasValidUserGestureActivation()) {
9111 return false;
9113 const auto& sourceBC = aLoadState->SourceBrowsingContext();
9114 if (!sourceBC || !sourceBC->IsActive()) {
9115 // If the navigation didn't come from a foreground tab, then we don't steal
9116 // focus.
9117 return false;
9119 auto* bc = aDocShell->GetBrowsingContext();
9120 if (sourceBC.get() == bc) {
9121 // If it comes from the same tab / frame, don't steal focus either.
9122 return false;
9124 auto* fm = nsFocusManager::GetFocusManager();
9125 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
9126 // If we're already on the foreground tab of the foreground window, then we
9127 // don't need to do this. This helps to e.g. not steal focus from the
9128 // browser chrome unnecessarily.
9129 return false;
9131 if (auto* doc = aDocShell->GetExtantDocument()) {
9132 if (doc->IsInitialDocument()) {
9133 // If we're the initial load for the browsing context, the browser
9134 // chrome determines what to focus. This is important because the
9135 // browser chrome may want to e.g focus the url-bar
9136 return false;
9139 // Take loadDivertedInBackground into account so the behavior would be the
9140 // same as how the tab first opened.
9141 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9144 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
9145 BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) {
9146 MOZ_ASSERT(aLoadState->IsFormSubmission());
9148 // https://html.spec.whatwg.org/#form-submission-algorithm
9149 // 22. Let historyHandling be "push".
9150 // 23. If form document equals targetNavigable's active document, and
9151 // form document has not yet completely loaded, then set
9152 // historyHandling to "replace".
9153 return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument
9154 ? LOAD_NORMAL_REPLACE
9155 : LOAD_LINK;
9158 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
9159 Maybe<uint32_t> aCacheKey) {
9160 MOZ_ASSERT(aLoadState, "need a load state!");
9161 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
9162 "need a valid TriggeringPrincipal");
9164 if (!aLoadState->TriggeringPrincipal()) {
9165 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9166 return NS_ERROR_FAILURE;
9168 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
9169 return NS_ERROR_NOT_AVAILABLE;
9172 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
9174 mOriginalUriString.Truncate();
9176 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9177 ("DOCSHELL %p InternalLoad %s\n", this,
9178 aLoadState->URI()->GetSpecOrDefault().get()));
9180 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
9182 // Cancel loads coming from Docshells that are being destroyed.
9183 if (mIsBeingDestroyed) {
9184 return NS_ERROR_NOT_AVAILABLE;
9187 nsresult rv = EnsureScriptEnvironment();
9188 if (NS_FAILED(rv)) {
9189 return rv;
9192 // If we have a target to move to, do that now.
9193 if (!aLoadState->Target().IsEmpty()) {
9194 return PerformRetargeting(aLoadState);
9197 // This is the non-retargeting load path, we've already set the right loadtype
9198 // for form submissions in nsDocShell::OnLinkClickSync.
9199 if (aLoadState->TargetBrowsingContext().IsNull()) {
9200 aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
9203 MOZ_DIAGNOSTIC_ASSERT(
9204 aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
9205 "Load must be targeting this BrowsingContext");
9207 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
9209 // If we don't have a target, we're loading into ourselves, and our load
9210 // delegate may want to intercept that load.
9211 SameDocumentNavigationState sameDocumentNavigationState;
9212 bool sameDocument =
9213 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
9214 !aLoadState->GetPendingRedirectedChannel();
9216 // Note: We do this check both here and in BrowsingContext::
9217 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9218 // available in the process triggering the load, and we don't want the target
9219 // process to have to trust the triggering process to do the appropriate
9220 // checks for the BrowsingContext's sandbox flags.
9221 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
9223 NS_ENSURE_STATE(!HasUnloadedParent());
9225 rv = CheckLoadingPermissions();
9226 if (NS_FAILED(rv)) {
9227 return rv;
9230 if (mFiredUnloadEvent) {
9231 if (IsOKToLoadURI(aLoadState->URI())) {
9232 MOZ_ASSERT(aLoadState->Target().IsEmpty(),
9233 "Shouldn't have a window target here!");
9235 // If this is a replace load, make whatever load triggered
9236 // the unload event also a replace load, so we don't
9237 // create extra history entries.
9238 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
9239 LOAD_FLAGS_REPLACE_HISTORY)) {
9240 mLoadType = LOAD_NORMAL_REPLACE;
9243 // Do this asynchronously
9244 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
9245 return Dispatch(ev.forget());
9248 // Just ignore this load attempt
9249 return NS_OK;
9252 // If we are loading a URI that should inherit a security context (basically
9253 // javascript: at this point), and the caller has said that principal
9254 // inheritance is allowed, there are a few possible cases:
9256 // 1) We are provided with the principal to inherit. In that case, we just use
9257 // it.
9259 // 2) The load is coming from some other application. In this case we don't
9260 // want to inherit from whatever document we have loaded now, since the
9261 // load is unrelated to it.
9263 // 3) It's a load from our application, but does not provide an explicit
9264 // principal to inherit. In that case, we want to inherit the principal of
9265 // our current document, or of our parent document (if any) if we don't
9266 // have a current document.
9268 bool inherits;
9270 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
9271 !aLoadState->PrincipalToInherit() &&
9272 (aLoadState->HasInternalLoadFlags(
9273 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
9274 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9275 aLoadState->URI(), &inherits)) &&
9276 inherits) {
9277 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
9279 // If principalToInherit is still null (e.g. if some of the conditions of
9280 // were not satisfied), then no inheritance of any sort will happen: the
9281 // load will just get a principal based on the URI being loaded.
9284 // If this docshell is owned by a frameloader, make sure to cancel
9285 // possible frameloader initialization before loading a new page.
9286 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
9287 if (parent) {
9288 RefPtr<Document> doc = parent->GetDocument();
9289 if (doc) {
9290 doc->TryCancelFrameLoaderInitialization(this);
9294 // Before going any further vet loads initiated by external programs.
9295 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9296 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
9298 // Disallow external chrome: loads targetted at content windows
9299 if (SchemeIsChrome(aLoadState->URI())) {
9300 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9301 return NS_ERROR_FAILURE;
9304 // clear the decks to prevent context bleed-through (bug 298255)
9305 rv = CreateAboutBlankDocumentViewer(nullptr, nullptr, nullptr, nullptr,
9306 /* aIsInitialDocument */ false);
9307 if (NS_FAILED(rv)) {
9308 return NS_ERROR_FAILURE;
9312 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
9313 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
9314 mURIResultedInDocument = false; // reset the clock...
9316 // See if this is actually a load between two history entries for the same
9317 // document. If the process fails, or if we successfully navigate within the
9318 // same document, return.
9319 if (sameDocument) {
9320 nsresult rv = HandleSameDocumentNavigation(
9321 aLoadState, sameDocumentNavigationState, sameDocument);
9322 NS_ENSURE_SUCCESS(rv, rv);
9323 if (shouldTakeFocus) {
9324 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9326 if (sameDocument) {
9327 return rv;
9331 // mDocumentViewer->PermitUnload can destroy |this| docShell, which
9332 // causes the next call of CanSavePresentation to crash.
9333 // Hold onto |this| until we return, to prevent a crash from happening.
9334 // (bug#331040)
9335 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9337 // Don't init timing for javascript:, since it generally doesn't
9338 // actually start a load or anything. If it does, we'll init
9339 // timing then, from OnStateChange.
9341 // XXXbz mTiming should know what channel it's for, so we don't
9342 // need this hackery.
9343 bool toBeReset = false;
9344 bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
9346 if (!isJavaScript) {
9347 toBeReset = MaybeInitTiming();
9349 bool isNotDownload = aLoadState->FileName().IsVoid();
9350 if (mTiming && isNotDownload) {
9351 mTiming->NotifyBeforeUnload();
9353 // Check if the page doesn't want to be unloaded. The javascript:
9354 // protocol handler deals with this for javascript: URLs.
9355 if (!isJavaScript && isNotDownload &&
9356 !aLoadState->NotifiedBeforeUnloadListeners() && mDocumentViewer) {
9357 bool okToUnload;
9359 // Check if request is exempted from HTTPSOnlyMode and if https-first is
9360 // enabled, if so it means:
9361 // * https-first failed to upgrade request to https
9362 // * we already asked for permission to unload and the user accepted
9363 // otherwise we wouldn't be here.
9364 bool isPrivateWin = GetOriginAttributes().mPrivateBrowsingId > 0;
9365 bool isHistoryOrReload = false;
9366 uint32_t loadType = aLoadState->LoadType();
9368 // Check if request is a reload.
9369 if (loadType == LOAD_RELOAD_NORMAL ||
9370 loadType == LOAD_RELOAD_BYPASS_CACHE ||
9371 loadType == LOAD_RELOAD_BYPASS_PROXY ||
9372 loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
9373 loadType == LOAD_HISTORY) {
9374 isHistoryOrReload = true;
9377 // If it isn't a reload, the request already failed to be upgraded and
9378 // https-first is enabled then don't ask the user again for permission to
9379 // unload and just unload.
9380 if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSFirstMode() &&
9381 nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
9382 rv = mDocumentViewer->PermitUnload(
9383 nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload,
9384 &okToUnload);
9385 } else {
9386 rv = mDocumentViewer->PermitUnload(&okToUnload);
9389 if (NS_SUCCEEDED(rv) && !okToUnload) {
9390 // The user chose not to unload the page, interrupt the
9391 // load.
9392 MaybeResetInitTiming(toBeReset);
9393 return NS_OK;
9397 if (mTiming && isNotDownload) {
9398 mTiming->NotifyUnloadAccepted(mCurrentURI);
9401 // In e10s, in the parent process, we refuse to load anything other than
9402 // "safe" resources that we ship or trust enough to give "special" URLs.
9403 // Similar check will be performed by the ParentProcessDocumentChannel if in
9404 // use.
9405 if (XRE_IsE10sParentProcess() &&
9406 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
9407 !CanLoadInParentProcess(aLoadState->URI())) {
9408 return NS_ERROR_FAILURE;
9411 // Whenever a top-level browsing context is navigated, the user agent MUST
9412 // lock the orientation of the document to the document's default
9413 // orientation. We don't explicitly check for a top-level browsing context
9414 // here because orientation is only set on top-level browsing contexts.
9415 if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) {
9416 MOZ_ASSERT(mBrowsingContext->IsTop());
9417 MOZ_ALWAYS_SUCCEEDS(
9418 mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None));
9419 if (mBrowsingContext->IsActive()) {
9420 ScreenOrientation::UpdateActiveOrientationLock(
9421 hal::ScreenOrientation::None);
9425 // Check for saving the presentation here, before calling Stop().
9426 // This is necessary so that we can catch any pending requests.
9427 // Since the new request has not been created yet, we pass null for the
9428 // new request parameter.
9429 // Also pass nullptr for the document, since it doesn't affect the return
9430 // value for our purposes here.
9431 bool savePresentation =
9432 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr,
9433 /* aReportBFCacheComboTelemetry */ true);
9435 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9436 // separate check for SHIP so that we know if there are ongoing requests
9437 // before calling Stop() below.
9438 if (mozilla::SessionHistoryInParent()) {
9439 Document* document = GetDocument();
9440 uint32_t flags = 0;
9441 if (document && !document->CanSavePresentation(nullptr, flags, true)) {
9442 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9443 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9444 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9445 // Also, we want to report all the flags to the parent process here (and
9446 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9447 // telemetry data correctly.
9448 document->DisallowBFCaching(flags);
9452 // Don't stop current network activity for javascript: URL's since
9453 // they might not result in any data, and thus nothing should be
9454 // stopped in those cases. In the case where they do result in
9455 // data, the javascript: URL channel takes care of stopping
9456 // current network activity.
9457 if (!isJavaScript && isNotDownload) {
9458 // Stop any current network activity.
9459 // Also stop content if this is a zombie doc. otherwise
9460 // the onload will be delayed by other loads initiated in the
9461 // background by the first document that
9462 // didn't fully load before the next load was initiated.
9463 // If not a zombie, don't stop content until data
9464 // starts arriving from the new URI...
9466 if ((mDocumentViewer && mDocumentViewer->GetPreviousViewer()) ||
9467 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
9468 rv = Stop(nsIWebNavigation::STOP_ALL);
9469 } else {
9470 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9473 if (NS_FAILED(rv)) {
9474 return rv;
9478 mLoadType = aLoadState->LoadType();
9480 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9481 // been called. But when loading an error page, do not clear the
9482 // mLSHE for the real page.
9483 if (mLoadType != LOAD_ERROR_PAGE) {
9484 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
9485 Nothing());
9486 if (aLoadState->LoadIsFromSessionHistory() &&
9487 !mozilla::SessionHistoryInParent()) {
9488 // We're making history navigation or a reload. Make sure our history ID
9489 // points to the same ID as SHEntry's docshell ID.
9490 nsID historyID = {};
9491 aLoadState->SHEntry()->GetDocshellID(historyID);
9493 Unused << mBrowsingContext->SetHistoryID(historyID);
9497 mSavingOldViewer = savePresentation;
9499 // If we have a saved content viewer in history, restore and show it now.
9500 if (aLoadState->LoadIsFromSessionHistory() &&
9501 (mLoadType & LOAD_CMD_HISTORY)) {
9502 // https://html.spec.whatwg.org/#history-traversal:
9503 // To traverse the history
9504 // "If entry has a different Document object than the current entry, then
9505 // run the following substeps: Remove any tasks queued by the history
9506 // traversal task source..."
9507 // Same document object case was handled already above with
9508 // HandleSameDocumentNavigation call.
9509 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
9510 if (shistory) {
9511 shistory->RemovePendingHistoryNavigations();
9513 if (!mozilla::SessionHistoryInParent()) {
9514 // It's possible that the previous viewer of mDocumentViewer is the
9515 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9516 // If that's the case, we need to go ahead and force it into its shentry
9517 // so we can restore it.
9518 if (mDocumentViewer) {
9519 nsCOMPtr<nsIDocumentViewer> prevViewer =
9520 mDocumentViewer->GetPreviousViewer();
9521 if (prevViewer) {
9522 #ifdef DEBUG
9523 nsCOMPtr<nsIDocumentViewer> prevPrevViewer =
9524 prevViewer->GetPreviousViewer();
9525 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9526 #endif
9527 nsCOMPtr<nsISHEntry> viewerEntry;
9528 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9529 if (viewerEntry == aLoadState->SHEntry()) {
9530 // Make sure this viewer ends up in the right place
9531 mDocumentViewer->SetPreviousViewer(nullptr);
9532 prevViewer->Destroy();
9536 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9537 bool restoring;
9538 rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
9539 if (restoring) {
9540 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
9541 return rv;
9543 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
9545 // We failed to restore the presentation, so clean up.
9546 // Both the old and new history entries could potentially be in
9547 // an inconsistent state.
9548 if (NS_FAILED(rv)) {
9549 if (oldEntry) {
9550 oldEntry->SyncPresentationState();
9553 aLoadState->SHEntry()->SyncPresentationState();
9558 bool isTopLevelDoc = mBrowsingContext->IsTopContent();
9560 OriginAttributes attrs = GetOriginAttributes();
9561 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
9563 PredictorLearn(aLoadState->URI(), nullptr,
9564 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
9565 PredictorPredict(aLoadState->URI(), nullptr,
9566 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
9568 nsCOMPtr<nsIRequest> req;
9569 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
9571 if (NS_SUCCEEDED(rv)) {
9572 if (shouldTakeFocus) {
9573 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9577 if (NS_FAILED(rv)) {
9578 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9579 UnblockEmbedderLoadEventForFailure();
9580 nsCOMPtr<nsIURI> uri = aLoadState->URI();
9581 if (DisplayLoadError(rv, uri, nullptr, chan) &&
9582 // FIXME: At this point code was using internal load flags, but checking
9583 // non-internal load flags?
9584 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
9585 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9588 // We won't report any error if this is an unknown protocol error. The
9589 // reason behind this is that it will allow enumeration of external
9590 // protocols if we report an error for each unknown protocol.
9591 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
9592 return NS_OK;
9596 return rv;
9599 /* static */
9600 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
9601 nsCOMPtr<nsIURI> uri = aURI;
9602 // In e10s, in the parent process, we refuse to load anything other than
9603 // "safe" resources that we ship or trust enough to give "special" URLs.
9604 bool canLoadInParent = false;
9605 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9606 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
9607 canLoadInParent) {
9608 // We allow UI resources.
9609 return true;
9611 // For about: and extension-based URIs, which don't get
9612 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9613 while (uri && uri->SchemeIs("view-source")) {
9614 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
9615 if (nested) {
9616 nested->GetInnerURI(getter_AddRefs(uri));
9617 } else {
9618 break;
9621 // Allow about: URIs, and allow moz-extension ones if we're running
9622 // extension content in the parent process.
9623 if (!uri || uri->SchemeIs("about") ||
9624 (!StaticPrefs::extensions_webextensions_remote() &&
9625 uri->SchemeIs("moz-extension"))) {
9626 return true;
9628 #ifdef MOZ_THUNDERBIRD
9629 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
9630 uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
9631 uri->SchemeIs("snews")) {
9632 return true;
9634 #endif
9635 nsAutoCString scheme;
9636 uri->GetScheme(scheme);
9637 // Allow ext+foo URIs (extension-registered custom protocols). See
9638 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9639 if (StringBeginsWith(scheme, "ext+"_ns) &&
9640 !StaticPrefs::extensions_webextensions_remote()) {
9641 return true;
9643 // Final exception for some legacy automated tests:
9644 if (xpc::IsInAutomation() &&
9645 StaticPrefs::security_allow_unsafe_parent_loads()) {
9646 return true;
9648 return false;
9651 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
9652 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
9653 RefPtr<Document> document;
9654 bool inheritedFromCurrent = false;
9656 if (aConsiderCurrentDocument && mDocumentViewer) {
9657 document = mDocumentViewer->GetDocument();
9658 inheritedFromCurrent = true;
9661 if (!document) {
9662 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9663 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9664 if (parentItem) {
9665 document = parentItem->GetDocument();
9669 if (!document) {
9670 if (!aConsiderCurrentDocument) {
9671 return nullptr;
9674 // Make sure we end up with _something_ as the principal no matter
9675 // what.If this fails, we'll just get a null docViewer and bail.
9676 EnsureDocumentViewer();
9677 if (!mDocumentViewer) {
9678 return nullptr;
9680 document = mDocumentViewer->GetDocument();
9683 //-- Get the document's principal
9684 if (document) {
9685 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
9686 ? document->PartitionedPrincipal()
9687 : document->NodePrincipal();
9689 // Don't allow loads in typeContent docShells to inherit the system
9690 // principal from existing documents.
9691 if (inheritedFromCurrent && mItemType == typeContent &&
9692 docPrincipal->IsSystemPrincipal()) {
9693 return nullptr;
9696 return docPrincipal;
9699 return nullptr;
9702 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9703 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9704 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9705 const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9706 nsCOMPtr<nsIChannel> channel;
9707 if (aSrcdoc.IsVoid()) {
9708 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9709 nullptr, // PerformanceStorage
9710 nullptr, // loadGroup
9711 aCallbacks, aLoadFlags));
9713 if (aBaseURI) {
9714 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9715 if (vsc) {
9716 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9719 } else if (SchemeIsViewSource(aURI)) {
9720 // Instantiate view source handler protocol, if it doesn't exist already.
9721 nsCOMPtr<nsIIOService> io(do_GetIOService());
9722 MOZ_ASSERT(io);
9723 nsCOMPtr<nsIProtocolHandler> handler;
9724 nsresult rv =
9725 io->GetProtocolHandler("view-source", getter_AddRefs(handler));
9726 if (NS_FAILED(rv)) {
9727 return rv;
9730 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9731 if (!vsh) {
9732 return NS_ERROR_FAILURE;
9735 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9736 getter_AddRefs(channel)));
9737 } else {
9738 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
9739 aSrcdoc, "text/html"_ns, aLoadInfo,
9740 true));
9741 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9742 MOZ_ASSERT(isc);
9743 isc->SetBaseURI(aBaseURI);
9746 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9747 nsresult rv = channel->SetLoadFlags(aLoadFlags);
9748 NS_ENSURE_SUCCESS(rv, rv);
9751 channel.forget(aChannel);
9752 return NS_OK;
9755 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9756 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9757 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9758 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9759 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9760 nsIChannel** aChannel) {
9761 MOZ_ASSERT(aLoadInfo);
9763 nsString srcdoc = VoidString();
9764 bool isSrcdoc =
9765 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9766 if (isSrcdoc) {
9767 srcdoc = aLoadState->SrcdocData();
9770 aLoadInfo->SetTriggeringRemoteType(
9771 aLoadState->GetEffectiveTriggeringRemoteType());
9773 if (aLoadState->PrincipalToInherit()) {
9774 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9776 aLoadInfo->SetLoadTriggeredFromExternal(
9777 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
9778 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
9779 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9780 aLoadInfo->SetOriginalFrameSrcLoad(
9781 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9783 bool inheritAttrs = false;
9784 if (aLoadState->PrincipalToInherit()) {
9785 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9786 aLoadState->PrincipalToInherit(), aLoadState->URI(),
9787 true, // aInheritForAboutBlank
9788 isSrcdoc);
9791 // Strip the target query parameters before creating the channel.
9792 aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext);
9794 OriginAttributes attrs;
9796 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9797 // true. Otherwise we just use the origin attributes from docshell.
9798 if (inheritAttrs) {
9799 MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9800 "We should have PrincipalToInherit here.");
9801 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9802 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9803 // have the same origin attributes with docshell.
9804 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9805 attrs == aOriginAttributes);
9806 } else {
9807 attrs = aOriginAttributes;
9808 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9809 aLoadState->URI());
9812 aRv = aLoadInfo->SetOriginAttributes(attrs);
9813 if (NS_WARN_IF(NS_FAILED(aRv))) {
9814 return false;
9817 if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9818 aLoadInfo->SetIsFromProcessingFrameAttributes();
9821 // Propagate the IsFormSubmission flag to the loadInfo.
9822 if (aLoadState->IsFormSubmission()) {
9823 aLoadInfo->SetIsFormSubmission(true);
9826 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
9828 nsCOMPtr<nsIChannel> channel;
9829 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9830 aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9831 aLoadState->BaseURI());
9832 NS_ENSURE_SUCCESS(aRv, false);
9834 if (!channel) {
9835 return false;
9838 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9839 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9840 // HTTPS_ONLY_EXEMPT.
9841 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
9843 // hack
9844 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9845 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9846 do_QueryInterface(channel));
9847 nsCOMPtr<nsIURI> referrer;
9848 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9849 if (referrerInfo) {
9850 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9852 if (httpChannelInternal) {
9853 if (aLoadState->HasInternalLoadFlags(
9854 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9855 aRv = httpChannelInternal->SetThirdPartyFlags(
9856 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9857 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9859 if (aLoadState->FirstParty()) {
9860 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9861 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9862 } else {
9863 aRv = httpChannelInternal->SetDocumentURI(referrer);
9864 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9866 aRv = httpChannelInternal->SetRedirectMode(
9867 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9868 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9871 if (httpChannel) {
9872 if (aLoadState->HeadersStream()) {
9873 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9875 // Set the referrer explicitly
9876 // Referrer is currenly only set for link clicks here.
9877 if (referrerInfo) {
9878 aRv = httpChannel->SetReferrerInfo(referrerInfo);
9879 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9882 // Mark the http channel as UrgentStart for top level document loading in
9883 // active tab.
9884 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9885 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9886 if (cos) {
9887 cos->AddClassFlags(nsIClassOfService::UrgentStart);
9892 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
9893 : aLoadState->URI());
9895 const nsACString& typeHint = aLoadState->TypeHint();
9896 if (!typeHint.IsVoid()) {
9897 channel->SetContentType(typeHint);
9900 const nsAString& fileName = aLoadState->FileName();
9901 if (!fileName.IsVoid()) {
9902 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9903 NS_ENSURE_SUCCESS(aRv, false);
9904 if (!fileName.IsEmpty()) {
9905 aRv = channel->SetContentDispositionFilename(fileName);
9906 NS_ENSURE_SUCCESS(aRv, false);
9910 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
9911 nsCOMPtr<nsIURI> referrer;
9912 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9913 if (referrerInfo) {
9914 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9916 // save true referrer for those who need it (e.g. xpinstall whitelisting)
9917 // Currently only http and ftp channels support this.
9918 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
9920 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
9921 props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true);
9925 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
9926 auto loadType = aLoadState->LoadType();
9928 if (loadType == LOAD_RELOAD_NORMAL &&
9929 StaticPrefs::
9930 browser_soft_reload_only_force_validate_top_level_document()) {
9931 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
9932 if (cachingChannel) {
9933 cachingChannel->SetForceValidateCacheContent(true);
9937 // figure out if we need to set the post data stream on the channel...
9938 if (aLoadState->PostDataStream()) {
9939 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
9940 do_QueryInterface(channel)) {
9941 // XXX it's a bit of a hack to rewind the postdata stream here but
9942 // it has to be done in case the post data is being reused multiple
9943 // times.
9944 nsCOMPtr<nsISeekableStream> postDataSeekable =
9945 do_QueryInterface(aLoadState->PostDataStream());
9946 if (postDataSeekable) {
9947 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
9948 NS_ENSURE_SUCCESS(aRv, false);
9951 // we really need to have a content type associated with this stream!!
9952 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
9954 // Ownership of the stream has transferred to the channel, clear our
9955 // reference.
9956 aLoadState->SetPostDataStream(nullptr);
9959 /* If there is a valid postdata *and* it is a History Load,
9960 * set up the cache key on the channel, to retrieve the
9961 * data *only* from the cache. If it is a normal reload, the
9962 * cache is free to go to the server for updated postdata.
9964 if (cacheChannel && aCacheKey != 0) {
9965 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
9966 cacheChannel->SetCacheKey(aCacheKey);
9967 uint32_t loadFlags;
9968 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
9969 channel->SetLoadFlags(loadFlags |
9970 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
9972 } else if (loadType == LOAD_RELOAD_NORMAL) {
9973 cacheChannel->SetCacheKey(aCacheKey);
9976 } else {
9977 /* If there is no postdata, set the cache key on the channel, and
9978 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
9979 * will be free to get it from net if it is not found in cache.
9980 * New cache may use it creatively on CGI pages with GET
9981 * method and even on those that say "no-cache"
9983 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
9984 loadType == LOAD_RELOAD_CHARSET_CHANGE ||
9985 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
9986 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
9987 if (cacheChannel && aCacheKey != 0) {
9988 cacheChannel->SetCacheKey(aCacheKey);
9993 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
9994 // Allow execution against our context if the principals match
9995 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
9998 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
9999 timedChannel->SetTimingEnabled(true);
10001 nsString initiatorType;
10002 switch (aLoadInfo->InternalContentPolicyType()) {
10003 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
10004 initiatorType = u"embed"_ns;
10005 break;
10006 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
10007 initiatorType = u"object"_ns;
10008 break;
10009 default: {
10010 const auto& embedderElementType =
10011 aBrowsingContext->GetEmbedderElementType();
10012 if (embedderElementType) {
10013 initiatorType = *embedderElementType;
10015 break;
10019 if (!initiatorType.IsEmpty()) {
10020 timedChannel->SetInitiatorType(initiatorType);
10024 nsCOMPtr<nsIURI> rpURI;
10025 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10026 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
10027 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
10028 if (originalResultPrincipalURI &&
10029 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
10030 // Unconditionally override, we want the replay to be equal to what has
10031 // been captured.
10032 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
10035 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
10036 // The LOAD_REPLACE flag and its handling here will be removed as part
10037 // of bug 1319110. For now preserve its restoration here to not break
10038 // any code expecting it being set specially on redirected channels.
10039 // If the flag has originally been set to change result of
10040 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10041 // any harm.
10042 uint32_t loadFlags;
10043 aRv = channel->GetLoadFlags(&loadFlags);
10044 NS_ENSURE_SUCCESS(aRv, false);
10045 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10048 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10049 if (csp) {
10050 // Navigational requests that are same origin need to be upgraded in case
10051 // upgrade-insecure-requests is present. Please note that for document
10052 // navigations that bit is re-computed in case we encounter a server
10053 // side redirect so the navigation is not same-origin anymore.
10054 bool upgradeInsecureRequests = false;
10055 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10056 if (upgradeInsecureRequests) {
10057 // only upgrade if the navigation is same origin
10058 nsCOMPtr<nsIPrincipal> resultPrincipal;
10059 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10060 channel, getter_AddRefs(resultPrincipal));
10061 NS_ENSURE_SUCCESS(aRv, false);
10062 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10063 aLoadState->TriggeringPrincipal(), resultPrincipal)) {
10064 aLoadInfo->SetUpgradeInsecureRequests(true);
10068 // For document loads we store the CSP that potentially needs to
10069 // be inherited by the new document, e.g. in case we are loading
10070 // an opaque origin like a data: URI. The actual inheritance
10071 // check happens within Document::InitCSP().
10072 // Please create an actual copy of the CSP (do not share the same
10073 // reference) otherwise a Meta CSP of an opaque origin will
10074 // incorrectly be propagated to the embedding document.
10075 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
10076 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
10077 aLoadInfo->SetCSPToInherit(cspToInherit);
10080 channel.forget(aChannel);
10081 return true;
10084 bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
10085 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
10086 return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
10087 (aPrincipalToInherit == GetInheritedPrincipal(false)) &&
10088 (!mDocumentViewer || !mDocumentViewer->GetDocument() ||
10089 mDocumentViewer->GetDocument()->IsInitialDocument());
10092 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
10093 Maybe<uint32_t> aCacheKey,
10094 nsIRequest** aRequest) {
10095 // Double-check that we're still around to load this URI.
10096 if (mIsBeingDestroyed) {
10097 // Return NS_OK despite not doing anything to avoid throwing exceptions
10098 // from nsLocation::SetHref if the unload handler of the existing page
10099 // tears us down.
10100 return NS_OK;
10103 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
10104 if (NS_WARN_IF(!uriLoader)) {
10105 return NS_ERROR_UNEXPECTED;
10108 // Persist and sync layout history state before we load a new uri, as this
10109 // might be our last chance to do so, in the content process.
10110 PersistLayoutHistoryState();
10111 SynchronizeLayoutHistoryState();
10113 nsresult rv;
10114 nsContentPolicyType contentPolicyType = DetermineContentType();
10116 if (IsSubframe()) {
10117 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10118 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10119 "DoURILoad thinks this is a frame and InternalLoad does not");
10121 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10122 // Only allow URLs able to return data in iframes.
10123 if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) {
10124 // The context to check user-interaction with for the purposes of
10125 // popup-blocking.
10127 // We generally want to check the context that initiated the navigation.
10128 WindowContext* sourceWindowContext = [&] {
10129 const MaybeDiscardedBrowsingContext& sourceBC =
10130 aLoadState->SourceBrowsingContext();
10131 if (!sourceBC.IsNullOrDiscarded()) {
10132 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
10133 return wc;
10136 return mBrowsingContext->GetParentWindowContext();
10137 }();
10139 MOZ_ASSERT(sourceWindowContext);
10140 // FIXME: We can't check user-interaction against an OOP window. This is
10141 // the next best thing we can really do. The load state keeps whether
10142 // the navigation had a user interaction in process
10143 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10144 // consume it, which we want to prevent popup-spamming from the same
10145 // click event.
10146 WindowContext* context =
10147 sourceWindowContext->IsInProcess()
10148 ? sourceWindowContext
10149 : mBrowsingContext->GetCurrentWindowContext();
10150 const bool popupBlocked = [&] {
10151 const bool active = mBrowsingContext->IsActive();
10153 // For same-origin-with-top windows, we grant a single free popup
10154 // without user activation, see bug 1680721.
10156 // We consume the flag now even if there's no user activation.
10157 const bool hasFreePass = [&] {
10158 if (!active ||
10159 !(context->IsInProcess() && context->SameOriginWithTop())) {
10160 return false;
10162 nsGlobalWindowInner* win =
10163 context->TopWindowContext()->GetInnerWindow();
10164 return win && win->TryOpenExternalProtocolIframe();
10165 }();
10167 if (context->IsInProcess() &&
10168 context->ConsumeTransientUserGestureActivation()) {
10169 // If the user has interacted with the page, consume it.
10170 return false;
10173 // TODO(emilio): Can we remove this check? It seems like what prompted
10174 // this code (bug 1514547) should be covered by transient user
10175 // activation, see bug 1514547.
10176 if (active &&
10177 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10178 return false;
10181 if (sourceWindowContext->CanShowPopup()) {
10182 return false;
10185 if (hasFreePass) {
10186 return false;
10189 return true;
10190 }();
10192 // No error must be returned when iframes are blocked.
10193 if (popupBlocked) {
10194 nsAutoString message;
10195 nsresult rv = nsContentUtils::GetLocalizedString(
10196 nsContentUtils::eDOM_PROPERTIES,
10197 "ExternalProtocolFrameBlockedNoUserActivation", message);
10198 if (NS_SUCCEEDED(rv)) {
10199 nsContentUtils::ReportToConsoleByWindowID(
10200 message, nsIScriptError::warningFlag, "DOM"_ns,
10201 context->InnerWindowId());
10203 return NS_OK;
10208 // Only allow view-source scheme in top-level docshells. view-source is
10209 // the only scheme to which this applies at the moment due to potential
10210 // timing attacks to read data from cross-origin iframes. If this widens
10211 // we should add a protocol flag for whether the scheme is allowed in
10212 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10213 nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
10214 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10215 while (nestedURI) {
10216 // view-source should always be an nsINestedURI, loop and check the
10217 // scheme on this and all inner URIs that are also nested URIs.
10218 if (SchemeIsViewSource(tempURI)) {
10219 return NS_ERROR_UNKNOWN_PROTOCOL;
10221 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10222 nestedURI = do_QueryInterface(tempURI);
10224 } else {
10225 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10226 "DoURILoad thinks this is a document and InternalLoad does not");
10229 // We want to inherit aLoadState->PrincipalToInherit() when:
10230 // 1. ChannelShouldInheritPrincipal returns true.
10231 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10232 // configured as unique opaque origin.
10233 bool inheritPrincipal = false;
10235 if (aLoadState->PrincipalToInherit()) {
10236 bool isSrcdoc =
10237 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
10238 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10239 aLoadState->PrincipalToInherit(), aLoadState->URI(),
10240 true, // aInheritForAboutBlank
10241 isSrcdoc);
10243 inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
10246 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
10247 const bool isAboutBlankLoadOntoInitialAboutBlank =
10248 IsAboutBlankLoadOntoInitialAboutBlank(aLoadState->URI(), inheritPrincipal,
10249 aLoadState->PrincipalToInherit());
10251 // FIXME We still have a ton of codepaths that don't pass through
10252 // DocumentLoadListener, so probably need to create session history info
10253 // in more places.
10254 if (aLoadState->GetLoadingSessionHistoryInfo()) {
10255 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
10256 } else if (isAboutBlankLoadOntoInitialAboutBlank &&
10257 mozilla::SessionHistoryInParent()) {
10258 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
10259 // loads have it, and later history behavior depends on it existing.
10260 UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
10261 aLoadState->URI(), aLoadState->TriggeringPrincipal(),
10262 aLoadState->PrincipalToInherit(),
10263 aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
10264 mContentTypeHint);
10265 mozilla::dom::LoadingSessionHistoryInfo info(*entry);
10266 SetLoadingSessionHistoryInfo(info, true);
10269 // open a channel for the url
10271 // If we have a pending channel, use the channel we've already created here.
10272 // We don't need to set up load flags for our channel, as it has already been
10273 // created.
10275 if (nsCOMPtr<nsIChannel> channel =
10276 aLoadState->GetPendingRedirectedChannel()) {
10277 // If we have a request outparameter, shove our channel into it.
10278 if (aRequest) {
10279 nsCOMPtr<nsIRequest> outRequest = channel;
10280 outRequest.forget(aRequest);
10283 return OpenRedirectedChannel(aLoadState);
10286 // There are two cases we care about:
10287 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10288 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10289 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10290 // element for the load. loadingPrincipal is the NodePrincipal of the
10291 // frame element.
10292 nsCOMPtr<nsINode> loadingNode;
10293 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10294 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10295 nsCOMPtr<nsISupports> topLevelLoadingContext;
10297 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10298 loadingNode = nullptr;
10299 loadingPrincipal = nullptr;
10300 loadingWindow = mScriptGlobal;
10301 if (XRE_IsContentProcess()) {
10302 // In e10s the child process doesn't have access to the element that
10303 // contains the browsing context (because that element is in the chrome
10304 // process).
10305 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
10306 topLevelLoadingContext = ToSupports(browserChild);
10307 } else {
10308 // This is for loading non-e10s tabs and toplevel windows of various
10309 // sorts.
10310 // For the toplevel window cases, requestingElement will be null.
10311 nsCOMPtr<Element> requestingElement =
10312 loadingWindow->GetFrameElementInternal();
10313 topLevelLoadingContext = requestingElement;
10315 } else {
10316 loadingWindow = nullptr;
10317 loadingNode = mScriptGlobal->GetFrameElementInternal();
10318 if (loadingNode) {
10319 // If we have a loading node, then use that as our loadingPrincipal.
10320 loadingPrincipal = loadingNode->NodePrincipal();
10321 #ifdef DEBUG
10322 // Get the docshell type for requestingElement.
10323 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
10324 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10325 // requestingElement docshell type = current docshell type.
10326 MOZ_ASSERT(
10327 mItemType == elementDocShell->ItemType(),
10328 "subframes should have the same docshell type as their parent");
10329 #endif
10330 } else {
10331 if (mIsBeingDestroyed) {
10332 // If this isn't a top-level load and mScriptGlobal's frame element is
10333 // null, then the element got removed from the DOM while we were trying
10334 // to load this resource. This docshell is scheduled for destruction
10335 // already, so bail out here.
10336 return NS_OK;
10338 // If we are not being destroyed and we do not have access to the loading
10339 // node, then we are a remote subframe. Set the loading principal
10340 // to be a null principal and then set it correctly in the parent.
10341 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
10345 if (!aLoadState->TriggeringPrincipal()) {
10346 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10347 return NS_ERROR_FAILURE;
10350 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
10351 nsSecurityFlags securityFlags =
10352 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
10354 if (mLoadType == LOAD_ERROR_PAGE) {
10355 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10358 if (inheritPrincipal) {
10359 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10362 // Must never have a parent for TYPE_DOCUMENT loads
10363 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10364 !mBrowsingContext->GetParent());
10365 // Subdocuments must have a parent
10366 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
10367 mBrowsingContext->GetParent());
10368 mBrowsingContext->SetTriggeringAndInheritPrincipals(
10369 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
10370 aLoadState->GetLoadIdentifier());
10371 RefPtr<LoadInfo> loadInfo =
10372 (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10373 ? new LoadInfo(loadingWindow, aLoadState->URI(),
10374 aLoadState->TriggeringPrincipal(),
10375 topLevelLoadingContext, securityFlags, sandboxFlags)
10376 : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
10377 loadingNode, securityFlags, contentPolicyType,
10378 Maybe<mozilla::dom::ClientInfo>(),
10379 Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
10380 sandboxFlags);
10381 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
10383 if (isAboutBlankLoadOntoInitialAboutBlank) {
10384 // Match the DocumentChannel case where the default for third-partiness
10385 // differs from the default in LoadInfo construction here.
10386 // toolkit/components/antitracking/test/browser/browser_aboutblank.js
10387 // fails without this.
10388 BrowsingContext* top = mBrowsingContext->Top();
10389 if (top == mBrowsingContext) {
10390 // If we're at the top, this must be a window.open()ed
10391 // window, and we can't be third-party relative to ourselves.
10392 loadInfo->SetIsThirdPartyContextToTopWindow(false);
10393 } else {
10394 if (Document* topDoc = top->GetDocument()) {
10395 bool thirdParty = false;
10396 mozilla::Unused << topDoc->GetPrincipal()->IsThirdPartyPrincipal(
10397 aLoadState->PrincipalToInherit(), &thirdParty);
10398 loadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
10399 } else {
10400 // If top is in a different process, we have to be third-party relative
10401 // to it.
10402 loadInfo->SetIsThirdPartyContextToTopWindow(true);
10407 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess()) {
10408 if (context->HasValidTransientUserGestureActivation()) {
10409 aLoadState->SetHasValidUserGestureActivation(true);
10411 if (!aLoadState->TriggeringWindowId()) {
10412 aLoadState->SetTriggeringWindowId(context->Id());
10414 if (!aLoadState->TriggeringStorageAccess()) {
10415 Document* contextDoc = context->GetExtantDoc();
10416 if (contextDoc) {
10417 aLoadState->SetTriggeringStorageAccess(
10418 contextDoc->UsingStorageAccess());
10423 // in case this docshell load was triggered by a valid transient user gesture,
10424 // or also the load originates from external, then we pass that information on
10425 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10426 if (aLoadState->HasValidUserGestureActivation() ||
10427 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
10428 loadInfo->SetHasValidUserGestureActivation(true);
10431 loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
10432 loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
10433 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
10434 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
10436 uint32_t cacheKey = 0;
10437 if (aCacheKey) {
10438 cacheKey = *aCacheKey;
10439 } else if (mozilla::SessionHistoryInParent()) {
10440 if (mLoadingEntry) {
10441 cacheKey = mLoadingEntry->mInfo.GetCacheKey();
10442 } else if (mActiveEntry) { // for reload cases
10443 cacheKey = mActiveEntry->GetCacheKey();
10445 } else {
10446 if (mLSHE) {
10447 cacheKey = mLSHE->GetCacheKey();
10448 } else if (mOSHE) { // for reload cases
10449 cacheKey = mOSHE->GetCacheKey();
10453 bool uriModified;
10454 if (mLSHE || mLoadingEntry) {
10455 if (mLoadingEntry) {
10456 uriModified = mLoadingEntry->mInfo.GetURIWasModified();
10457 } else {
10458 uriModified = mLSHE->GetURIWasModified();
10460 } else {
10461 uriModified = false;
10464 bool isEmbeddingBlockedError = false;
10465 if (mFailedChannel) {
10466 nsresult status;
10467 mFailedChannel->GetStatus(&status);
10468 isEmbeddingBlockedError = status == NS_ERROR_XFO_VIOLATION ||
10469 status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION;
10472 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
10473 mBrowsingContext, Some(uriModified), Some(isEmbeddingBlockedError));
10475 nsCOMPtr<nsIChannel> channel;
10476 if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
10477 !isAboutBlankLoadOntoInitialAboutBlank) {
10478 channel = DocumentChannel::CreateForDocument(
10479 aLoadState, loadInfo, loadFlags, this, cacheKey, uriModified,
10480 isEmbeddingBlockedError);
10481 MOZ_ASSERT(channel);
10483 // Disable keyword fixup when using DocumentChannel, since
10484 // DocumentLoadListener will handle this for us (in the parent process).
10485 mAllowKeywordFixup = false;
10486 } else if (!CreateAndConfigureRealChannelForLoadState(
10487 mBrowsingContext, aLoadState, loadInfo, this, this,
10488 GetOriginAttributes(), loadFlags, cacheKey, rv,
10489 getter_AddRefs(channel))) {
10490 return rv;
10493 // Make sure to give the caller a channel if we managed to create one
10494 // This is important for correct error page/session history interaction
10495 if (aRequest) {
10496 NS_ADDREF(*aRequest = channel);
10499 const nsACString& typeHint = aLoadState->TypeHint();
10500 if (!typeHint.IsVoid()) {
10501 mContentTypeHint = typeHint;
10502 } else {
10503 mContentTypeHint.Truncate();
10506 // Load attributes depend on load type...
10507 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10508 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10509 // only want to force cache load for this channel, not the whole
10510 // loadGroup.
10511 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10512 if (cachingChannel) {
10513 cachingChannel->SetAllowStaleCacheContent(true);
10517 uint32_t openFlags =
10518 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
10519 return OpenInitializedChannel(channel, uriLoader, openFlags);
10522 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10523 const char* aFromRawSegment,
10524 uint32_t aToOffset, uint32_t aCount,
10525 uint32_t* aWriteCount) {
10526 // aFromSegment now contains aCount bytes of data.
10528 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10529 buf->Append(aFromRawSegment, aCount);
10531 // Indicate that we have consumed all of aFromSegment
10532 *aWriteCount = aCount;
10533 return NS_OK;
10536 /* static */ nsresult nsDocShell::AddHeadersToChannel(
10537 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
10538 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10539 NS_ENSURE_STATE(httpChannel);
10541 uint32_t numRead;
10542 nsAutoCString headersString;
10543 nsresult rv = aHeadersData->ReadSegments(
10544 AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10545 NS_ENSURE_SUCCESS(rv, rv);
10547 // used during the manipulation of the String from the InputStream
10548 nsAutoCString headerName;
10549 nsAutoCString headerValue;
10550 int32_t crlf;
10551 int32_t colon;
10554 // Iterate over the headersString: for each "\r\n" delimited chunk,
10555 // add the value as a header to the nsIHttpChannel
10558 static const char kWhitespace[] = "\b\t\r\n ";
10559 while (true) {
10560 crlf = headersString.Find("\r\n");
10561 if (crlf == kNotFound) {
10562 return NS_OK;
10565 const nsACString& oneHeader = StringHead(headersString, crlf);
10567 colon = oneHeader.FindChar(':');
10568 if (colon == kNotFound) {
10569 return NS_ERROR_UNEXPECTED;
10572 headerName = StringHead(oneHeader, colon);
10573 headerValue = Substring(oneHeader, colon + 1);
10575 headerName.Trim(kWhitespace);
10576 headerValue.Trim(kWhitespace);
10578 headersString.Cut(0, crlf + 2);
10581 // FINALLY: we can set the header!
10584 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10585 NS_ENSURE_SUCCESS(rv, rv);
10588 MOZ_ASSERT_UNREACHABLE("oops");
10589 return NS_ERROR_UNEXPECTED;
10592 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10593 BrowsingContext* aBrowsingContext, uint32_t aLoadType) {
10594 MOZ_ASSERT(aBrowsingContext);
10596 uint32_t openFlags = 0;
10597 if (aLoadType == LOAD_LINK) {
10598 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10600 if (!aBrowsingContext->GetAllowContentRetargeting()) {
10601 openFlags |= nsIURILoader::DONT_RETARGET;
10604 return openFlags;
10607 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
10608 nsIURILoader* aURILoader,
10609 uint32_t aOpenFlags) {
10610 nsresult rv = NS_OK;
10612 // If anything fails here, make sure to clear our initial ClientSource.
10613 auto cleanupInitialClient =
10614 MakeScopeExit([&] { mInitialClientSource.reset(); });
10616 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10617 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10619 MaybeCreateInitialClientSource();
10621 // Let the client channel helper know if we are using DocumentChannel,
10622 // since redirects get handled in the parent process in that case.
10623 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
10624 if (docChannel && XRE_IsContentProcess()) {
10625 // Tell the content process nsDocumentOpenInfo to not try to do
10626 // any sort of targeting.
10627 aOpenFlags |= nsIURILoader::DONT_RETARGET;
10630 // Since we are loading a document we need to make sure the proper reserved
10631 // and initial client data is stored on the nsILoadInfo. The
10632 // ClientChannelHelper does this and ensures that it is propagated properly
10633 // on redirects. We pass no reserved client here so that the helper will
10634 // create the reserved ClientSource if necessary.
10635 Maybe<ClientInfo> noReservedClient;
10636 if (docChannel) {
10637 // When using DocumentChannel, all redirect handling is done in the parent,
10638 // so we just need the child variant to watch for the internal redirect
10639 // to the final channel.
10640 rv = AddClientChannelHelperInChild(aChannel,
10641 GetMainThreadSerialEventTarget());
10642 docChannel->SetInitialClientInfo(GetInitialClientInfo());
10643 } else {
10644 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
10645 GetInitialClientInfo(),
10646 GetMainThreadSerialEventTarget());
10648 NS_ENSURE_SUCCESS(rv, rv);
10650 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
10651 NS_ENSURE_SUCCESS(rv, rv);
10653 // We're about to load a new page and it may take time before necko
10654 // gives back any data, so main thread might have a chance to process a
10655 // collector slice
10656 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
10658 // Success. Keep the initial ClientSource if it exists.
10659 cleanupInitialClient.release();
10661 return NS_OK;
10664 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
10665 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
10666 MOZ_ASSERT(channel);
10668 // If anything fails here, make sure to clear our initial ClientSource.
10669 auto cleanupInitialClient =
10670 MakeScopeExit([&] { mInitialClientSource.reset(); });
10672 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10673 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10675 MaybeCreateInitialClientSource();
10677 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
10679 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
10680 if (loadInfo->GetExternalContentPolicyType() ==
10681 ExtContentPolicy::TYPE_DOCUMENT) {
10682 li->UpdateBrowsingContextID(mBrowsingContext->Id());
10683 } else if (loadInfo->GetExternalContentPolicyType() ==
10684 ExtContentPolicy::TYPE_SUBDOCUMENT) {
10685 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
10687 // TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
10689 // If we did a process switch, then we should have an existing allocated
10690 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10691 CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget());
10693 RefPtr<nsDocumentOpenInfo> loader =
10694 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10695 channel->SetLoadGroup(mLoadGroup);
10697 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10699 nsresult rv = NS_OK;
10700 if (XRE_IsParentProcess()) {
10701 // If we're in the parent, the we don't have an nsIChildChannel, just
10702 // the original channel, which is already open in this process.
10704 // DocumentLoadListener expects to get an nsIParentChannel, so
10705 // we create a wrapper around the channel and nsIStreamListener
10706 // that forwards functionality as needed, and then we register
10707 // it under the provided identifier.
10708 RefPtr<ParentChannelWrapper> wrapper =
10709 new ParentChannelWrapper(channel, loader);
10710 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10712 mLoadGroup->AddRequest(channel, nullptr);
10713 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10714 do_QueryInterface(channel)) {
10715 // Our channel was redirected from another process, so doesn't need to
10716 // be opened again. However, it does need its listener hooked up
10717 // correctly.
10718 rv = childChannel->CompleteRedirectSetup(loader);
10719 } else {
10720 // It's possible for the redirected channel to not implement
10721 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10722 // can just open the local instance and it will work.
10723 rv = channel->AsyncOpen(loader);
10725 if (rv == NS_ERROR_NO_CONTENT) {
10726 return NS_OK;
10728 NS_ENSURE_SUCCESS(rv, rv);
10730 // Success. Keep the initial ClientSource if it exists.
10731 cleanupInitialClient.release();
10732 return NS_OK;
10735 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
10736 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10737 nsACString& aNewHash, uint32_t aLoadType) {
10738 if (!mCurrentURI) {
10739 return NS_OK;
10742 RefPtr<PresShell> presShell = GetPresShell();
10743 if (!presShell) {
10744 // If we failed to get the shell, or if there is no shell,
10745 // nothing left to do here.
10746 return NS_OK;
10749 nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
10750 if (rootScroll) {
10751 rootScroll->ClearDidHistoryRestore();
10754 // If we have no new anchor, we do not want to scroll, unless there is a
10755 // current anchor and we are doing a history load. So return if we have no
10756 // new anchor, and there is no current anchor or the load is not a history
10757 // load.
10758 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10759 return NS_OK;
10762 // Both the new and current URIs refer to the same page. We can now
10763 // browse to the hash stored in the new URI.
10765 // If it's a load from history, we don't have any anchor jumping to do.
10766 // Scrollbar position will be restored by the caller based on positions stored
10767 // in session history.
10768 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10770 if (aNewHash.IsEmpty()) {
10771 // 2. If fragment is the empty string, then return the special value top of
10772 // the document.
10774 // Tell the shell it's at an anchor without scrolling.
10775 presShell->GoToAnchor(u""_ns, false);
10777 if (scroll) {
10778 // Scroll to the top of the page. Ignore the return value; failure to
10779 // scroll here (e.g. if there is no root scrollframe) is not grounds for
10780 // canceling the load!
10781 SetCurScrollPosEx(0, 0);
10784 return NS_OK;
10787 // 3. Let potentialIndicatedElement be the result of finding a potential
10788 // indicated element given document and fragment.
10789 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10790 auto rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
10792 // 4. If potentialIndicatedElement is not null, then return
10793 // potentialIndicatedElement.
10794 if (NS_SUCCEEDED(rv)) {
10795 return NS_OK;
10798 // 5. Let fragmentBytes be the result of percent-decoding fragment.
10799 nsAutoCString fragmentBytes;
10800 const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(),
10801 /* aFlags = */ 0, fragmentBytes);
10803 if (!unescaped) {
10804 // Another attempt is only necessary if characters were unescaped.
10805 return NS_OK;
10808 if (fragmentBytes.IsEmpty()) {
10809 // When aNewHash contains "%00", the unescaped string may be empty, and
10810 // GoToAnchor asserts if we ask it to scroll to an empty ref.
10811 presShell->GoToAnchor(u""_ns, false);
10812 return NS_OK;
10815 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
10816 // fragmentBytes.
10817 nsAutoString decodedFragment;
10818 rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
10819 NS_ENSURE_SUCCESS(rv, rv);
10821 // 7. Set potentialIndicatedElement to the result of finding a potential
10822 // indicated element given document and decodedFragment.
10824 // Ignore the return value of GoToAnchor, since it will return an error if
10825 // there is no such anchor in the document, which is actually a success
10826 // condition for us (we want to update the session history with the new URI no
10827 // matter whether we actually scrolled somewhere).
10828 presShell->GoToAnchor(decodedFragment, scroll, ScrollFlags::ScrollSmoothAuto);
10830 return NS_OK;
10833 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10834 nsIPrincipal* aTriggeringPrincipal,
10835 nsIPrincipal* aPrincipalToInherit,
10836 nsIPrincipal* aPartitionedPrincipalToInherit,
10837 nsIContentSecurityPolicy* aCsp,
10838 bool aAddToGlobalHistory, bool aCloneSHChildren) {
10839 MOZ_ASSERT(aURI, "uri is null");
10840 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10842 MOZ_ASSERT(!aPrincipalToInherit ||
10843 (aPrincipalToInherit && aTriggeringPrincipal));
10845 #if defined(DEBUG)
10846 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10847 nsAutoCString chanName;
10848 if (aChannel) {
10849 aChannel->GetName(chanName);
10850 } else {
10851 chanName.AssignLiteral("<no channel>");
10854 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10855 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10856 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10858 #endif
10860 bool equalUri = false;
10862 // Get the post data and the HTTP response code from the channel.
10863 uint32_t responseStatus = 0;
10864 nsCOMPtr<nsIInputStream> inputStream;
10865 if (aChannel) {
10866 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10868 // Check if the HTTPChannel is hiding under a multiPartChannel
10869 if (!httpChannel) {
10870 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10873 if (httpChannel) {
10874 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10875 if (uploadChannel) {
10876 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10879 // If the response status indicates an error, unlink this session
10880 // history entry from any entries sharing its document.
10881 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10882 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10883 mLSHE->AbandonBFCacheEntry();
10884 // FIXME Do the same for mLoadingEntry
10889 // Determine if this type of load should update history.
10890 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
10892 // We don't update session history on reload unless we're loading
10893 // an iframe in shift-reload case.
10894 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
10896 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10897 // root browsing context.
10898 // FIXME If session history in the parent is enabled then we only do this if
10899 // the session history object is in process, otherwise we can't really
10900 // use the mLSHE anyway. Once session history is only stored in the
10901 // parent then this code will probably be removed anyway.
10902 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
10903 if (!rootSH) {
10904 updateSHistory = false;
10905 updateGHistory = false; // XXX Why global history too?
10908 // Check if the url to be loaded is the same as the one already loaded.
10909 if (mCurrentURI) {
10910 aURI->Equals(mCurrentURI, &equalUri);
10913 #ifdef DEBUG
10914 bool shAvailable = (rootSH != nullptr);
10916 // XXX This log message is almost useless because |updateSHistory|
10917 // and |updateGHistory| are not correct at this point.
10919 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10920 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10921 " equalURI=%i\n",
10922 shAvailable, updateSHistory, updateGHistory, equalUri));
10923 #endif
10925 /* If the url to be loaded is the same as the one already there,
10926 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10927 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10928 * AddToSessionHistory() won't mess with the current SHEntry and
10929 * if this page has any frame children, it also will be handled
10930 * properly. see bug 83684
10932 * NB: If mOSHE is null but we have a current URI, then it probably
10933 * means that we must be at the transient about:blank content viewer;
10934 * we should let the normal load continue, since there's nothing to
10935 * replace. Sometimes this happens after a session restore (eg process
10936 * switch) and mCurrentURI is not about:blank; we assume we can let the load
10937 * continue (Bug 1301399).
10939 * XXX Hopefully changing the loadType at this time will not hurt
10940 * anywhere. The other way to take care of sequentially repeating
10941 * frameset pages is to add new methods to nsIDocShellTreeItem.
10942 * Hopefully I don't have to do that.
10944 if (equalUri &&
10945 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
10946 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
10947 mLoadType == LOAD_STOP_CONTENT) &&
10948 !inputStream) {
10949 mLoadType = LOAD_NORMAL_REPLACE;
10952 // If this is a refresh to the currently loaded url, we don't
10953 // have to update session or global history.
10954 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10955 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
10958 /* If the user pressed shift-reload, cache will create a new cache key
10959 * for the page. Save the new cacheKey in Session History.
10960 * see bug 90098
10962 if (aChannel && IsForceReloadType(mLoadType)) {
10963 MOZ_ASSERT(!updateSHistory || IsSubframe(),
10964 "We shouldn't be updating session history for forced"
10965 " reloads unless we're in a newly created iframe!");
10967 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
10968 uint32_t cacheKey = 0;
10969 // Get the Cache Key and store it in SH.
10970 if (cacheChannel) {
10971 cacheChannel->GetCacheKey(&cacheKey);
10973 // If we already have a loading history entry, store the new cache key
10974 // in it. Otherwise, since we're doing a reload and won't be updating
10975 // our history entry, store the cache key in our current history entry.
10976 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
10978 if (!mozilla::SessionHistoryInParent()) {
10979 // Since we're force-reloading, clear all the sub frame history.
10980 ClearFrameHistory(mLSHE);
10981 ClearFrameHistory(mOSHE);
10985 if (!mozilla::SessionHistoryInParent()) {
10986 // Clear subframe history on refresh.
10987 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
10988 // this case. One should re-validate after bug 1331865 fixed.
10989 if (mLoadType == LOAD_REFRESH) {
10990 ClearFrameHistory(mLSHE);
10991 ClearFrameHistory(mOSHE);
10994 if (updateSHistory) {
10995 // Update session history if necessary...
10996 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
10997 /* This is a fresh page getting loaded for the first time
10998 *.Create a Entry for it and add it to SH, if this is the
10999 * rootDocShell
11001 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11002 aPrincipalToInherit,
11003 aPartitionedPrincipalToInherit, aCsp,
11004 aCloneSHChildren, getter_AddRefs(mLSHE));
11006 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11007 // Even if we don't add anything to SHistory, ensure the current index
11008 // points to the same SHEntry as our mLSHE.
11010 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11011 mLSHE);
11015 // If this is a POST request, we do not want to include this in global
11016 // history.
11017 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11018 !net::ChannelIsPost(aChannel)) {
11019 nsCOMPtr<nsIURI> previousURI;
11020 uint32_t previousFlags = 0;
11022 if (mLoadType & LOAD_CMD_RELOAD) {
11023 // On a reload request, we don't set redirecting flags.
11024 previousURI = aURI;
11025 } else {
11026 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11029 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11032 // If this was a history load or a refresh, or it was a history load but
11033 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11034 // in session history.
11035 if (!mozilla::SessionHistoryInParent() && rootSH &&
11036 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11037 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11038 mPreviousEntryIndex = rootSH->Index();
11039 if (!mozilla::SessionHistoryInParent()) {
11040 rootSH->LegacySHistory()->UpdateIndex();
11042 mLoadedEntryIndex = rootSH->Index();
11043 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11044 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11045 mLoadedEntryIndex));
11048 // aCloneSHChildren exactly means "we are not loading a new document".
11049 uint32_t locationFlags =
11050 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11052 bool onLocationChangeNeeded =
11053 SetCurrentURI(aURI, aChannel, false,
11054 /* aIsInitialAboutBlank */ false, locationFlags);
11055 // Make sure to store the referrer from the channel, if any
11056 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11057 if (httpChannel) {
11058 mReferrerInfo = httpChannel->GetReferrerInfo();
11060 return onLocationChangeNeeded;
11063 Maybe<Wireframe> nsDocShell::GetWireframe() {
11064 const bool collectWireFrame =
11065 mozilla::SessionHistoryInParent() &&
11066 StaticPrefs::browser_history_collectWireframes() &&
11067 mBrowsingContext->IsTopContent() && mActiveEntry;
11069 if (!collectWireFrame) {
11070 return Nothing();
11073 RefPtr<Document> doc = mDocumentViewer->GetDocument();
11074 Nullable<Wireframe> wireframe;
11075 doc->GetWireframeWithoutFlushing(false, wireframe);
11076 if (wireframe.IsNull()) {
11077 return Nothing();
11079 return Some(wireframe.Value());
11082 bool nsDocShell::CollectWireframe() {
11083 Maybe<Wireframe> wireframe = GetWireframe();
11084 if (wireframe.isNothing()) {
11085 return false;
11088 if (XRE_IsParentProcess()) {
11089 SessionHistoryEntry* entry =
11090 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11091 if (entry) {
11092 entry->SetWireframe(wireframe);
11094 } else {
11095 mozilla::Unused
11096 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11097 mBrowsingContext, wireframe.ref());
11100 return true;
11103 //*****************************************************************************
11104 // nsDocShell: Session History
11105 //*****************************************************************************
11107 NS_IMETHODIMP
11108 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11109 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11110 MOZ_LOG(gSHLog, LogLevel::Debug,
11111 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11112 NS_ConvertUTF16toUTF8(aTitle).get(),
11113 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11114 // Implements History.pushState and History.replaceState
11116 // Here's what we do, roughly in the order specified by HTML5. The specific
11117 // steps we are executing are at
11118 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11119 // and
11120 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11121 // This function basically implements #dom-history-pushstate and
11122 // UpdateURLAndHistory implements #url-and-history-update-steps.
11124 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11125 // step 5.
11126 // B. If the third argument is present, #dom-history-pushstate step 7.
11127 // 7.1. Resolve the url, relative to our document.
11128 // 7.2. If (a) fails, raise a SECURITY_ERR
11129 // 7.4. Compare the resulting absolute URL to the document's address. If
11130 // any part of the URLs difer other than the <path>, <query>, and
11131 // <fragment> components, raise a SECURITY_ERR and abort.
11132 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11133 // Remove from the session history all entries after the current entry,
11134 // as we would after a regular navigation, and save the current
11135 // entry's scroll position (bug 590573).
11136 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11137 // either add a state object entry to the session history after the
11138 // current entry with the following properties, or modify the current
11139 // session history entry to set
11140 // a. cloned data as the state object,
11141 // b. if the third argument was present, the absolute URL found in
11142 // step 2
11143 // Also clear the new history entry's POST data (see bug 580069).
11144 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11145 // replaceState), notify bfcache that we've navigated to a new page.
11146 // F. If the third argument is present, set the document's current address
11147 // to the absolute URL found in step B. This is
11148 // #url-and-history-update-steps step 4.
11150 // It's important that this function not run arbitrary scripts after step A
11151 // and before completing step E. For example, if a script called
11152 // history.back() before we completed step E, bfcache might destroy an
11153 // active content viewer. Since EvictOutOfRangeDocumentViewers at the end of
11154 // step E might run script, we can't just put a script blocker around the
11155 // critical section.
11157 // Note that we completely ignore the aTitle parameter.
11159 nsresult rv;
11161 // Don't clobber the load type of an existing network load.
11162 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11164 // pushState effectively becomes replaceState when we've started a network
11165 // load but haven't adopted its document yet. This mirrors what we do with
11166 // changes to the hash at this stage of the game.
11167 if (JustStartedNetworkLoad()) {
11168 aReplace = true;
11171 RefPtr<Document> document = GetDocument();
11172 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11174 // Step A: Serialize aData using structured clone.
11175 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11176 // step 5.
11177 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11179 // scContainer->Init might cause arbitrary JS to run, and this code might
11180 // navigate the page we're on, potentially to a different origin! (bug
11181 // 634834) To protect against this, we abort if our principal changes due
11182 // to the InitFromJSVal() call.
11184 RefPtr<Document> origDocument = GetDocument();
11185 if (!origDocument) {
11186 return NS_ERROR_DOM_SECURITY_ERR;
11188 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11190 scContainer = new nsStructuredCloneContainer();
11191 rv = scContainer->InitFromJSVal(aData, aCx);
11192 NS_ENSURE_SUCCESS(rv, rv);
11194 RefPtr<Document> newDocument = GetDocument();
11195 if (!newDocument) {
11196 return NS_ERROR_DOM_SECURITY_ERR;
11198 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11200 bool principalsEqual = false;
11201 origPrincipal->Equals(newPrincipal, &principalsEqual);
11202 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11205 // Check that the state object isn't too long.
11206 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11207 if (maxStateObjSize < 0) {
11208 maxStateObjSize = 0;
11211 uint64_t scSize;
11212 rv = scContainer->GetSerializedNBytes(&scSize);
11213 NS_ENSURE_SUCCESS(rv, rv);
11215 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11217 // Step B: Resolve aURL.
11218 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11219 // step 7.
11220 bool equalURIs = true;
11221 nsCOMPtr<nsIURI> currentURI;
11222 if (mCurrentURI) {
11223 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11224 } else {
11225 currentURI = mCurrentURI;
11227 nsCOMPtr<nsIURI> newURI;
11228 if (aURL.Length() == 0) {
11229 newURI = currentURI;
11230 } else {
11231 // 7.1: Resolve aURL relative to mURI
11233 nsIURI* docBaseURI = document->GetDocBaseURI();
11234 if (!docBaseURI) {
11235 return NS_ERROR_FAILURE;
11238 nsAutoCString spec;
11239 docBaseURI->GetSpec(spec);
11241 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11242 document->GetDocumentCharacterSet(), docBaseURI);
11244 // 7.2: If 2a fails, raise a SECURITY_ERR
11245 if (NS_FAILED(rv)) {
11246 return NS_ERROR_DOM_SECURITY_ERR;
11249 // 7.4 and 7.5: Same-origin check.
11250 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11251 // In addition to checking that the security manager says that
11252 // the new URI has the same origin as our current URI, we also
11253 // check that the two URIs have the same userpass. (The
11254 // security manager says that |http://foo.com| and
11255 // |http://me@foo.com| have the same origin.) currentURI
11256 // won't contain the password part of the userpass, so this
11257 // means that it's never valid to specify a password in a
11258 // pushState or replaceState URI.
11260 nsCOMPtr<nsIScriptSecurityManager> secMan =
11261 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11262 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11264 // It's very important that we check that newURI is of the same
11265 // origin as currentURI, not docBaseURI, because a page can
11266 // set docBaseURI arbitrarily to any domain.
11267 nsAutoCString currentUserPass, newUserPass;
11268 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11269 NS_ERROR_FAILURE);
11270 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11271 bool isPrivateWin =
11272 document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
11274 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11275 isPrivateWin)) ||
11276 !currentUserPass.Equals(newUserPass)) {
11277 return NS_ERROR_DOM_SECURITY_ERR;
11279 } else {
11280 // It's a file:// URI
11281 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11283 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11284 newURI, false, document->InnerWindowID()))) {
11285 return NS_ERROR_DOM_SECURITY_ERR;
11289 if (currentURI) {
11290 currentURI->Equals(newURI, &equalURIs);
11291 } else {
11292 equalURIs = false;
11295 } // end of same-origin check
11297 // Step 8: call "URL and history update steps"
11298 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11299 currentURI, equalURIs);
11300 NS_ENSURE_SUCCESS(rv, rv);
11302 return NS_OK;
11305 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11306 nsIStructuredCloneContainer* aData,
11307 const nsAString& aTitle, bool aReplace,
11308 nsIURI* aCurrentURI, bool aEqualURIs) {
11309 // Implements
11310 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11312 // If we have a pending title change, handle it before creating a new entry.
11313 aDocument->DoNotifyPossibleTitleChange();
11315 // Step 2, if aReplace is false: Create a new entry in the session
11316 // history. This will erase all SHEntries after the new entry and make this
11317 // entry the current one. This operation may modify mOSHE, which we need
11318 // later, so we keep a reference here.
11319 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11320 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11322 // If this push/replaceState changed the document's current URI and the new
11323 // URI differs from the old URI in more than the hash, or if the old
11324 // SHEntry's URI was modified in this way by a push/replaceState call
11325 // set URIWasModified to true for the current SHEntry (bug 669671).
11326 bool sameExceptHashes = true;
11327 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11328 bool uriWasModified;
11329 if (sameExceptHashes) {
11330 if (mozilla::SessionHistoryInParent()) {
11331 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11332 } else {
11333 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11335 } else {
11336 uriWasModified = true;
11339 mLoadType = LOAD_PUSHSTATE;
11341 nsCOMPtr<nsISHEntry> newSHEntry;
11342 if (!aReplace) {
11343 // Step 2.
11345 // Step 2.2, "Remove any tasks queued by the history traversal task
11346 // source that are associated with any Document objects in the
11347 // top-level browsing context's document family." This is very hard in
11348 // SessionHistoryInParent since we can't synchronously access the
11349 // pending navigations that are already sent to the parent. We can
11350 // abort any AsyncGo navigations that are waiting to be sent. If we
11351 // send a message to the parent, it would be processed after any
11352 // navigations previously sent. So long as we consider the "history
11353 // traversal task source" to be the list in this process we match the
11354 // spec. If we move the entire list to the parent, we can handle the
11355 // aborting of loads there, but we don't have a way to synchronously
11356 // remove entries as we do here for non-SHIP.
11357 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11358 if (shistory) {
11359 shistory->RemovePendingHistoryNavigations();
11362 nsPoint scrollPos = GetCurScrollPos();
11364 bool scrollRestorationIsManual;
11365 if (mozilla::SessionHistoryInParent()) {
11366 // FIXME Need to save the current scroll position on mActiveEntry.
11367 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11368 } else {
11369 // Save the current scroll position (bug 590573). Step 2.3.
11370 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11372 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11375 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11377 if (mozilla::SessionHistoryInParent()) {
11378 MOZ_LOG(gSHLog, LogLevel::Debug,
11379 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11380 nsString title(mActiveEntry->GetTitle());
11381 UpdateActiveEntry(false,
11382 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11383 /* aOriginalURI = */ nullptr,
11384 /* aReferrerInfo = */ nullptr,
11385 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11386 csp, title, scrollRestorationIsManual, aData,
11387 uriWasModified);
11388 } else {
11389 // Since we're not changing which page we have loaded, pass
11390 // true for aCloneChildren.
11391 nsresult rv = AddToSessionHistory(
11392 aNewURI, nullptr,
11393 aDocument->NodePrincipal(), // triggeringPrincipal
11394 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11395 NS_ENSURE_SUCCESS(rv, rv);
11397 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11399 // Session history entries created by pushState inherit scroll restoration
11400 // mode from the current entry.
11401 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11403 nsString title;
11404 mOSHE->GetTitle(title);
11406 // Set the new SHEntry's title (bug 655273).
11407 newSHEntry->SetTitle(title);
11409 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11410 // two entries correspond to the same document.
11411 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11412 NS_ERROR_FAILURE);
11414 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11415 // we'll just set mOSHE here.
11416 mOSHE = newSHEntry;
11418 } else if (mozilla::SessionHistoryInParent()) {
11419 MOZ_LOG(gSHLog, LogLevel::Debug,
11420 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11421 this, mActiveEntry.get()));
11422 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11423 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11424 // in our case. We could also set it to aNewURI, with the same result.
11425 // We don't use aTitle here, see bug 544535.
11426 nsString title;
11427 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11428 if (mActiveEntry) {
11429 title = mActiveEntry->GetTitle();
11430 referrerInfo = mActiveEntry->GetReferrerInfo();
11431 } else {
11432 referrerInfo = nullptr;
11434 UpdateActiveEntry(
11435 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11436 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
11437 aDocument->GetCsp(), title,
11438 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11439 uriWasModified);
11440 } else {
11441 // Step 3.
11442 newSHEntry = mOSHE;
11444 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11445 // Since we're not changing which page we have loaded, pass
11446 // true for aCloneChildren.
11447 if (!newSHEntry) {
11448 nsresult rv = AddToSessionHistory(
11449 aNewURI, nullptr,
11450 aDocument->NodePrincipal(), // triggeringPrincipal
11451 nullptr, nullptr, aDocument->GetCsp(), true,
11452 getter_AddRefs(newSHEntry));
11453 NS_ENSURE_SUCCESS(rv, rv);
11454 mOSHE = newSHEntry;
11457 newSHEntry->SetURI(aNewURI);
11458 newSHEntry->SetOriginalURI(aNewURI);
11459 // We replaced the URI of the entry, clear the unstripped URI as it
11460 // shouldn't be used for reloads anymore.
11461 newSHEntry->SetUnstrippedURI(nullptr);
11462 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11463 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11464 // in our case. We could also set it to aNewURI, with the same result.
11465 newSHEntry->SetResultPrincipalURI(nullptr);
11466 newSHEntry->SetLoadReplace(false);
11469 if (!mozilla::SessionHistoryInParent()) {
11470 // Step 2.4 and 3: Modify new/original session history entry and clear its
11471 // POST data, if there is any.
11472 newSHEntry->SetStateData(aData);
11473 newSHEntry->SetPostData(nullptr);
11475 newSHEntry->SetURIWasModified(uriWasModified);
11477 // Step E as described at the top of AddState: If aReplace is false,
11478 // indicating that we're doing a pushState rather than a replaceState,
11479 // notify bfcache that we've added a page to the history so it can evict
11480 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11481 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11482 // root session history if this call is coming from a document.open() in a
11483 // docshell subtree that disables session history.
11484 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11485 if (rootSH) {
11486 rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry,
11487 aReplace);
11491 // Step 4: If the document's URI changed, update document's URI and update
11492 // global history.
11494 // We need to call FireOnLocationChange so that the browser's address bar
11495 // gets updated and the back button is enabled, but we only need to
11496 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11497 // since SetCurrentURI will call FireOnLocationChange for us.
11499 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11500 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11501 // notification is allowed only when we know docshell is not loading a new
11502 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11503 // FireOnLocationChange(...) breaks security UI.
11505 // If the docshell is shutting down, don't update the document URI, as we
11506 // can't load into a docshell that is being destroyed.
11507 if (!aEqualURIs && !mIsBeingDestroyed) {
11508 aDocument->SetDocumentURI(aNewURI);
11509 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11510 /* aIsInitialAboutBlank */ false,
11511 GetSameDocumentNavigationFlags(aNewURI));
11513 AddURIVisit(aNewURI, aCurrentURI, 0);
11515 // AddURIVisit doesn't set the title for the new URI in global history,
11516 // so do that here.
11517 UpdateGlobalHistoryTitle(aNewURI);
11519 // Inform the favicon service that our old favicon applies to this new
11520 // URI.
11521 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11522 } else {
11523 FireDummyOnLocationChange();
11525 aDocument->SetStateObject(aData);
11527 return NS_OK;
11530 NS_IMETHODIMP
11531 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11532 if (mozilla::SessionHistoryInParent()) {
11533 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11534 return NS_OK;
11537 *aIsManual = false;
11538 if (mOSHE) {
11539 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11542 return NS_OK;
11545 NS_IMETHODIMP
11546 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11547 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11549 return NS_OK;
11552 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11553 nsISHEntry* aSHEntry, bool aIsManual) {
11554 if (aSHEntry) {
11555 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11558 if (mActiveEntry && mBrowsingContext) {
11559 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11560 if (XRE_IsParentProcess()) {
11561 SessionHistoryEntry* entry =
11562 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11563 if (entry) {
11564 entry->SetScrollRestorationIsManual(aIsManual);
11566 } else {
11567 mozilla::Unused << ContentChild::GetSingleton()
11568 ->SendSessionHistoryEntryScrollRestorationIsManual(
11569 mBrowsingContext, aIsManual);
11574 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11575 uint32_t aCacheKey) {
11576 if (aSHEntry) {
11577 aSHEntry->SetCacheKey(aCacheKey);
11580 if (mActiveEntry && mBrowsingContext) {
11581 mActiveEntry->SetCacheKey(aCacheKey);
11582 if (XRE_IsParentProcess()) {
11583 SessionHistoryEntry* entry =
11584 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11585 if (entry) {
11586 entry->SetCacheKey(aCacheKey);
11588 } else {
11589 mozilla::Unused
11590 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11591 mBrowsingContext, aCacheKey);
11596 /* static */
11597 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11598 // I believe none of the about: urls should go in the history. But then
11599 // that could just be me... If the intent is only deny about:blank then we
11600 // should just do a spec compare, rather than two gets of the scheme and
11601 // then the path. -Gagan
11602 nsresult rv;
11603 nsAutoCString buf;
11605 rv = aURI->GetScheme(buf);
11606 if (NS_FAILED(rv)) {
11607 return false;
11610 if (buf.EqualsLiteral("about")) {
11611 rv = aURI->GetPathQueryRef(buf);
11612 if (NS_FAILED(rv)) {
11613 return false;
11616 if (buf.EqualsLiteral("blank")) {
11617 return false;
11619 // We only want to add about:newtab if it's not privileged, and
11620 // if it is not configured to show the blank page.
11621 if (buf.EqualsLiteral("newtab")) {
11622 if (!StaticPrefs::browser_newtabpage_enabled()) {
11623 return false;
11626 NS_ENSURE_TRUE(aChannel, false);
11627 nsCOMPtr<nsIPrincipal> resultPrincipal;
11628 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11629 aChannel, getter_AddRefs(resultPrincipal));
11630 NS_ENSURE_SUCCESS(rv, false);
11631 return !resultPrincipal->IsSystemPrincipal();
11635 return true;
11638 nsresult nsDocShell::AddToSessionHistory(
11639 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11640 nsIPrincipal* aPrincipalToInherit,
11641 nsIPrincipal* aPartitionedPrincipalToInherit,
11642 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11643 nsISHEntry** aNewEntry) {
11644 MOZ_ASSERT(aURI, "uri is null");
11645 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11646 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11648 #if defined(DEBUG)
11649 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11650 nsAutoCString chanName;
11651 if (aChannel) {
11652 aChannel->GetName(chanName);
11653 } else {
11654 chanName.AssignLiteral("<no channel>");
11657 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11658 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11659 aURI->GetSpecOrDefault().get(), chanName.get()));
11661 #endif
11663 nsresult rv = NS_OK;
11664 nsCOMPtr<nsISHEntry> entry;
11667 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11668 * the existing SH entry in the page and replace the url and
11669 * other vitalities.
11671 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11672 !mBrowsingContext->IsTop()) {
11673 // This is a subframe
11674 entry = mOSHE;
11675 if (entry) {
11676 entry->ClearEntry();
11680 // Create a new entry if necessary.
11681 if (!entry) {
11682 entry = new nsSHEntry();
11685 // Get the post data & referrer
11686 nsCOMPtr<nsIInputStream> inputStream;
11687 nsCOMPtr<nsIURI> originalURI;
11688 nsCOMPtr<nsIURI> resultPrincipalURI;
11689 nsCOMPtr<nsIURI> unstrippedURI;
11690 bool loadReplace = false;
11691 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11692 uint32_t cacheKey = 0;
11693 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11694 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11695 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11696 aPartitionedPrincipalToInherit;
11697 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11698 bool expired = false; // by default the page is not expired
11699 bool discardLayoutState = false;
11700 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11701 bool userActivation = false;
11703 if (aChannel) {
11704 cacheChannel = do_QueryInterface(aChannel);
11706 /* If there is a caching channel, get the Cache Key and store it
11707 * in SH.
11709 if (cacheChannel) {
11710 cacheChannel->GetCacheKey(&cacheKey);
11712 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11714 // Check if the httpChannel is hiding under a multipartChannel
11715 if (!httpChannel) {
11716 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11718 if (httpChannel) {
11719 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11720 if (uploadChannel) {
11721 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11723 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11724 uint32_t loadFlags;
11725 aChannel->GetLoadFlags(&loadFlags);
11726 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11727 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11728 MOZ_ASSERT(NS_SUCCEEDED(rv));
11730 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11733 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11734 if (!triggeringPrincipal) {
11735 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11737 if (!csp) {
11738 csp = loadInfo->GetCspToInherit();
11741 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11743 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
11745 userActivation = loadInfo->GetHasValidUserGestureActivation();
11747 // For now keep storing just the principal in the SHEntry.
11748 if (!principalToInherit) {
11749 if (loadInfo->GetLoadingSandboxed()) {
11750 if (loadInfo->GetLoadingPrincipal()) {
11751 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11752 loadInfo->GetLoadingPrincipal());
11753 } else {
11754 // get the OriginAttributes
11755 OriginAttributes attrs;
11756 loadInfo->GetOriginAttributes(&attrs);
11757 principalToInherit = NullPrincipal::Create(attrs);
11759 } else {
11760 principalToInherit = loadInfo->PrincipalToInherit();
11764 if (!partitionedPrincipalToInherit) {
11765 // XXXehsan is it correct to fall back to the principal to inherit in all
11766 // cases? For example, what about the cases where we are using the load
11767 // info's principal to inherit? Do we need to add a similar concept to
11768 // load info for partitioned principal?
11769 partitionedPrincipalToInherit = principalToInherit;
11773 nsAutoString srcdoc;
11774 bool srcdocEntry = false;
11775 nsCOMPtr<nsIURI> baseURI;
11777 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11778 if (inStrmChan) {
11779 bool isSrcdocChannel;
11780 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11781 if (isSrcdocChannel) {
11782 inStrmChan->GetSrcdocData(srcdoc);
11783 srcdocEntry = true;
11784 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11785 } else {
11786 srcdoc.SetIsVoid(true);
11789 /* If cache got a 'no-store', ask SH not to store
11790 * HistoryLayoutState. By default, SH will set this
11791 * flag to true and save HistoryLayoutState.
11793 bool saveLayoutState = !discardLayoutState;
11795 if (cacheChannel) {
11796 // Check if the page has expired from cache
11797 uint32_t expTime = 0;
11798 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11799 uint32_t now = PRTimeToSeconds(PR_Now());
11800 if (expTime <= now) {
11801 expired = true;
11805 // Title is set in nsDocShell::SetTitle()
11806 entry->Create(aURI, // uri
11807 u""_ns, // Title
11808 inputStream, // Post data stream
11809 cacheKey, // CacheKey
11810 mContentTypeHint, // Content-type
11811 triggeringPrincipal, // Channel or provided principal
11812 principalToInherit, partitionedPrincipalToInherit, csp,
11813 HistoryID(), GetCreatedDynamically(), originalURI,
11814 resultPrincipalURI, unstrippedURI, loadReplace, referrerInfo,
11815 srcdoc, srcdocEntry, baseURI, saveLayoutState, expired,
11816 userActivation);
11818 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11819 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11820 Maybe<int32_t> previousEntryIndex;
11821 Maybe<int32_t> loadedEntryIndex;
11822 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11823 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11824 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11826 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11827 if (previousEntryIndex.isSome()) {
11828 mPreviousEntryIndex = previousEntryIndex.value();
11830 if (loadedEntryIndex.isSome()) {
11831 mLoadedEntryIndex = loadedEntryIndex.value();
11834 // aCloneChildren implies that we are retaining the same document, thus we
11835 // need to signal to the top WC that the new SHEntry may receive a fresh
11836 // user interaction flag.
11837 if (aCloneChildren) {
11838 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11839 if (topWc && !topWc->IsDiscarded()) {
11840 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11843 } else {
11844 // This is a subframe, make sure that this new SHEntry will be
11845 // marked with user interaction.
11846 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11847 if (topWc && !topWc->IsDiscarded()) {
11848 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11850 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11851 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11852 aCloneChildren);
11856 // Return the new SH entry...
11857 if (aNewEntry) {
11858 *aNewEntry = nullptr;
11859 if (NS_SUCCEEDED(rv)) {
11860 entry.forget(aNewEntry);
11864 return rv;
11867 void nsDocShell::UpdateActiveEntry(
11868 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11869 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
11870 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
11871 const nsAString& aTitle, bool aScrollRestorationIsManual,
11872 nsIStructuredCloneContainer* aData, bool aURIWasModified) {
11873 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11874 MOZ_ASSERT(aURI, "uri is null");
11875 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
11876 "This code only deals with pushState");
11877 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
11879 MOZ_LOG(gSHLog, LogLevel::Debug,
11880 ("Creating an active entry on nsDocShell %p to %s", this,
11881 aURI->GetSpecOrDefault().get()));
11883 // Even if we're replacing an existing entry we create new a
11884 // SessionHistoryInfo. In the parent process we'll keep the existing
11885 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11886 // entry keeps identity but its data is replaced.
11887 bool replace = aReplace && mActiveEntry;
11889 if (!replace) {
11890 CollectWireframe();
11893 if (mActiveEntry) {
11894 // Link this entry to the previous active entry.
11895 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
11896 } else {
11897 mActiveEntry = MakeUnique<SessionHistoryInfo>(
11898 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
11900 mActiveEntry->SetOriginalURI(aOriginalURI);
11901 mActiveEntry->SetUnstrippedURI(nullptr);
11902 mActiveEntry->SetReferrerInfo(aReferrerInfo);
11903 mActiveEntry->SetTitle(aTitle);
11904 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
11905 mActiveEntry->SetURIWasModified(aURIWasModified);
11906 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
11908 if (replace) {
11909 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
11910 } else {
11911 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
11912 // FIXME We should probably just compute mChildOffset in the parent
11913 // instead of passing it over IPC here.
11914 mBrowsingContext->SetActiveSessionHistoryEntry(
11915 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
11916 /* aCacheKey = */ 0);
11917 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
11921 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
11922 bool aUserActivation) {
11923 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11925 nsresult rv;
11926 RefPtr<nsDocShellLoadState> loadState;
11927 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
11928 NS_ENSURE_SUCCESS(rv, rv);
11930 // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if
11931 // that's the only thing holding a ref to aEntry that will cause aEntry to
11932 // die while we're loading it. So hold a strong ref to aEntry here, just
11933 // in case.
11934 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11936 loadState->SetHasValidUserGestureActivation(
11937 loadState->HasValidUserGestureActivation() || aUserActivation);
11939 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
11942 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
11943 uint32_t aLoadType,
11944 bool aUserActivation) {
11945 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
11946 loadState->SetHasValidUserGestureActivation(
11947 loadState->HasValidUserGestureActivation() || aUserActivation);
11949 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
11952 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
11953 uint32_t aLoadType,
11954 bool aLoadingCurrentEntry) {
11955 if (!IsNavigationAllowed()) {
11956 return NS_OK;
11959 // We are setting load type afterwards so we don't have to
11960 // send it in an IPC message
11961 aLoadState->SetLoadType(aLoadType);
11963 nsresult rv;
11964 if (SchemeIsJavascript(aLoadState->URI())) {
11965 // We're loading a URL that will execute script from inside asyncOpen.
11966 // Replace the current document with about:blank now to prevent
11967 // anything from the current document from leaking into any JavaScript
11968 // code in the URL.
11969 // Don't cache the presentation if we're going to just reload the
11970 // current entry. Caching would lead to trying to save the different
11971 // content viewers in the same nsISHEntry object.
11972 rv = CreateAboutBlankDocumentViewer(
11973 aLoadState->PrincipalToInherit(),
11974 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
11975 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
11977 if (NS_FAILED(rv)) {
11978 // The creation of the intermittent about:blank content
11979 // viewer failed for some reason (potentially because the
11980 // user prevented it). Interrupt the history load.
11981 return NS_OK;
11984 if (!aLoadState->TriggeringPrincipal()) {
11985 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
11986 // URIs will pick it up from the about:blank page we just loaded,
11987 // and we don't really want even that in this case.
11988 nsCOMPtr<nsIPrincipal> principal =
11989 NullPrincipal::Create(GetOriginAttributes());
11990 aLoadState->SetTriggeringPrincipal(principal);
11994 /* If there is a valid postdata *and* the user pressed
11995 * reload or shift-reload, take user's permission before we
11996 * repost the data to the server.
11998 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
11999 bool repost;
12000 rv = ConfirmRepost(&repost);
12001 if (NS_FAILED(rv)) {
12002 return rv;
12005 // If the user pressed cancel in the dialog, return. We're done here.
12006 if (!repost) {
12007 return NS_BINDING_ABORTED;
12011 // If there is no valid triggeringPrincipal, we deny the load
12012 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
12013 "need a valid triggeringPrincipal to load from history");
12014 if (!aLoadState->TriggeringPrincipal()) {
12015 return NS_ERROR_FAILURE;
12018 return InternalLoad(aLoadState); // No nsIRequest
12021 NS_IMETHODIMP
12022 nsDocShell::PersistLayoutHistoryState() {
12023 nsresult rv = NS_OK;
12025 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12026 bool scrollRestorationIsManual;
12027 if (mozilla::SessionHistoryInParent()) {
12028 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12029 } else {
12030 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12032 nsCOMPtr<nsILayoutHistoryState> layoutState;
12033 if (RefPtr<PresShell> presShell = GetPresShell()) {
12034 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12035 } else if (scrollRestorationIsManual) {
12036 // Even if we don't have layout anymore, we may want to reset the
12037 // current scroll state in layout history.
12038 GetLayoutHistoryState(getter_AddRefs(layoutState));
12041 if (scrollRestorationIsManual && layoutState) {
12042 layoutState->ResetScrollState();
12046 return rv;
12049 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12050 nsISHEntry* aNewEntry) {
12051 if (aOldEntry == mOSHE) {
12052 mOSHE = aNewEntry;
12055 if (aOldEntry == mLSHE) {
12056 mLSHE = aNewEntry;
12060 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12061 const Maybe<nsISHEntry*>& aOSHE) {
12062 // We want to hold on to the reference in mLSHE before we update it.
12063 // Otherwise, SetHistoryEntry could release the last reference to
12064 // the entry while aOSHE is pointing to it.
12065 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12066 if (aLSHE.isSome()) {
12067 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12068 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12070 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12071 if (aOSHE.isSome()) {
12072 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12073 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12077 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12078 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12079 // We need to sync up the docshell and session history trees for
12080 // subframe navigation. If the load was in a subframe, we forward up to
12081 // the root docshell, which will then recursively sync up all docshells
12082 // to their corresponding entries in the new session history tree.
12083 // If we don't do this, then we can cache a content viewer on the wrong
12084 // cloned entry, and subsequently restore it at the wrong time.
12085 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12086 if (topBC->IsDiscarded()) {
12087 topBC = nullptr;
12089 RefPtr<BrowsingContext> currBC =
12090 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12091 if (topBC && *aPtr) {
12092 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12094 nsCOMPtr<nsISHEntry> entry(aEntry);
12095 entry.swap(*aPtr);
12096 return entry.forget();
12099 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12100 RefPtr<ChildSHistory> childSHistory =
12101 mBrowsingContext->Top()->GetChildSessionHistory();
12102 return childSHistory.forget();
12105 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12106 nsIHttpChannel** aReturn) {
12107 NS_ENSURE_ARG_POINTER(aReturn);
12108 if (!aChannel) {
12109 return NS_ERROR_FAILURE;
12112 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12113 if (multiPartChannel) {
12114 nsCOMPtr<nsIChannel> baseChannel;
12115 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12116 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12117 *aReturn = httpChannel;
12118 NS_IF_ADDREF(*aReturn);
12120 return NS_OK;
12123 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12124 // By default layout State will be saved.
12125 if (!aChannel) {
12126 return false;
12129 // figure out if SH should be saving layout state
12130 bool noStore = false;
12131 Unused << aChannel->IsNoStoreResponse(&noStore);
12132 return noStore;
12135 NS_IMETHODIMP
12136 nsDocShell::GetEditor(nsIEditor** aEditor) {
12137 NS_ENSURE_ARG_POINTER(aEditor);
12138 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12139 htmlEditor.forget(aEditor);
12140 return NS_OK;
12143 NS_IMETHODIMP
12144 nsDocShell::SetEditor(nsIEditor* aEditor) {
12145 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12146 // If TextEditor comes, throw an error.
12147 if (aEditor && !htmlEditor) {
12148 return NS_ERROR_INVALID_ARG;
12150 return SetHTMLEditorInternal(htmlEditor);
12153 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12154 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12157 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12158 if (!aHTMLEditor && !mEditorData) {
12159 return NS_OK;
12162 nsresult rv = EnsureEditorData();
12163 if (NS_FAILED(rv)) {
12164 return rv;
12167 return mEditorData->SetHTMLEditor(aHTMLEditor);
12170 NS_IMETHODIMP
12171 nsDocShell::GetEditable(bool* aEditable) {
12172 NS_ENSURE_ARG_POINTER(aEditable);
12173 *aEditable = mEditorData && mEditorData->GetEditable();
12174 return NS_OK;
12177 NS_IMETHODIMP
12178 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12179 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12181 if (mEditorData) {
12182 *aHasEditingSession = !!mEditorData->GetEditingSession();
12183 } else {
12184 *aHasEditingSession = false;
12187 return NS_OK;
12190 NS_IMETHODIMP
12191 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12192 nsresult rv = EnsureEditorData();
12193 if (NS_FAILED(rv)) {
12194 return rv;
12197 return mEditorData->MakeEditable(aInWaitForUriLoad);
12200 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12201 bool needToAddURIVisit = true;
12202 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12203 if (props) {
12204 mozilla::Unused << props->GetPropertyAsBool(
12205 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12208 return needToAddURIVisit;
12211 /* static */ void nsDocShell::ExtractLastVisit(
12212 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12213 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12214 if (!props) {
12215 return;
12218 nsresult rv;
12219 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12220 if (NS_SUCCEEDED(rv)) {
12221 uri.forget(aURI);
12223 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12224 aChannelRedirectFlags);
12226 NS_WARNING_ASSERTION(
12227 NS_SUCCEEDED(rv),
12228 "Could not fetch previous flags, URI will be treated like referrer");
12230 } else {
12231 // There is no last visit for this channel, so this must be the first
12232 // link. Link the visit to the referrer of this request, if any.
12233 // Treat referrer as null if there is an error getting it.
12234 NS_GetReferrerFromChannel(aChannel, aURI);
12238 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12239 uint32_t aChannelRedirectFlags) {
12240 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12241 if (!props || !aURI) {
12242 return;
12245 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12246 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12247 aChannelRedirectFlags);
12250 /* static */ void nsDocShell::InternalAddURIVisit(
12251 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12252 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12253 nsIWidget* aWidget, uint32_t aLoadType, bool aWasUpgraded) {
12254 MOZ_ASSERT(aURI, "Visited URI is null!");
12255 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12256 "Do not add error or bypass pages to global history");
12258 bool usePrivateBrowsing = false;
12259 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12261 // Only content-type docshells save URI visits. Also don't do
12262 // anything here if we're not supposed to use global history.
12263 if (!aBrowsingContext->IsContent() ||
12264 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12265 return;
12268 nsCOMPtr<IHistory> history = components::History::Service();
12270 if (history) {
12271 uint32_t visitURIFlags = 0;
12273 if (aBrowsingContext->IsTop()) {
12274 visitURIFlags |= IHistory::TOP_LEVEL;
12277 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12278 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12279 } else if (aChannelRedirectFlags &
12280 nsIChannelEventSink::REDIRECT_PERMANENT) {
12281 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12282 } else {
12283 MOZ_ASSERT(!aChannelRedirectFlags,
12284 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12285 "if any flags in aChannelRedirectFlags is set.");
12288 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12289 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12290 if (aResponseStatus == 301 || aResponseStatus == 308) {
12291 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12294 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12295 // simple retry attempt by the user is unlikely to solve them.
12296 // 408 is special cased, since may actually indicate a temporary
12297 // connection problem.
12298 else if (aResponseStatus != 408 &&
12299 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12300 aResponseStatus == 505)) {
12301 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12304 if (aWasUpgraded) {
12305 visitURIFlags |=
12306 IHistory::REDIRECT_SOURCE | IHistory::REDIRECT_SOURCE_UPGRADED;
12309 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12310 visitURIFlags,
12311 aBrowsingContext->BrowserId());
12315 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12316 uint32_t aChannelRedirectFlags,
12317 uint32_t aResponseStatus) {
12318 nsPIDOMWindowOuter* outer = GetWindow();
12319 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12321 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12322 aResponseStatus, mBrowsingContext, widget, mLoadType,
12323 false);
12326 //*****************************************************************************
12327 // nsDocShell: Helper Routines
12328 //*****************************************************************************
12330 NS_IMETHODIMP
12331 nsDocShell::SetLoadType(uint32_t aLoadType) {
12332 mLoadType = aLoadType;
12333 return NS_OK;
12336 NS_IMETHODIMP
12337 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12338 *aLoadType = mLoadType;
12339 return NS_OK;
12342 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12343 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12344 *aRepost = true;
12345 return NS_OK;
12348 nsCOMPtr<nsIPromptCollection> prompter =
12349 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12350 if (!prompter) {
12351 return NS_ERROR_NOT_AVAILABLE;
12354 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12357 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12358 nsIStringBundle** aStringBundle) {
12359 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12360 NS_ERROR_FAILURE);
12362 nsCOMPtr<nsIStringBundleService> stringBundleService =
12363 mozilla::components::StringBundle::Service();
12364 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12366 NS_ENSURE_SUCCESS(
12367 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12368 NS_ERROR_FAILURE);
12370 return NS_OK;
12373 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12374 PresShell* presShell = GetPresShell();
12375 NS_ENSURE_TRUE(presShell, nullptr);
12377 return presShell->GetRootScrollFrameAsScrollable();
12380 nsresult nsDocShell::EnsureScriptEnvironment() {
12381 if (mScriptGlobal) {
12382 return NS_OK;
12385 if (mIsBeingDestroyed) {
12386 return NS_ERROR_NOT_AVAILABLE;
12389 #ifdef DEBUG
12390 NS_ASSERTION(!mInEnsureScriptEnv,
12391 "Infinite loop! Calling EnsureScriptEnvironment() from "
12392 "within EnsureScriptEnvironment()!");
12394 // Yeah, this isn't re-entrant safe, but that's ok since if we
12395 // re-enter this method, we'll infinitely loop...
12396 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12397 mInEnsureScriptEnv = true;
12398 #endif
12400 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12401 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12403 uint32_t chromeFlags;
12404 browserChrome->GetChromeFlags(&chromeFlags);
12406 // If our window is modal and we're not opened as chrome, make
12407 // this window a modal content window.
12408 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12409 MOZ_ASSERT(mScriptGlobal);
12411 // Ensure the script object is set up to run script.
12412 return mScriptGlobal->EnsureScriptEnvironment();
12415 nsresult nsDocShell::EnsureEditorData() {
12416 MOZ_ASSERT(!mIsBeingDestroyed);
12418 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12419 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12420 // We shouldn't recreate the editor data if it already exists, or
12421 // we're shutting down, or we already have a detached editor data
12422 // stored in the session history. We should only have one editordata
12423 // per docshell.
12424 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12427 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12430 nsresult nsDocShell::EnsureFind() {
12431 if (!mFind) {
12432 mFind = new nsWebBrowserFind();
12435 // we promise that the nsIWebBrowserFind that we return has been set
12436 // up to point to the focused, or content window, so we have to
12437 // set that up each time.
12439 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12440 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12442 // default to our window
12443 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12444 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12445 nsFocusManager::GetFocusedDescendant(ourWindow,
12446 nsFocusManager::eIncludeAllDescendants,
12447 getter_AddRefs(windowToSearch));
12449 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12450 if (!findInFrames) {
12451 return NS_ERROR_NO_INTERFACE;
12454 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12455 if (NS_FAILED(rv)) {
12456 return rv;
12458 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12459 if (NS_FAILED(rv)) {
12460 return rv;
12463 return NS_OK;
12466 NS_IMETHODIMP
12467 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12468 NS_ENSURE_ARG(aDoomed);
12469 *aDoomed = mIsBeingDestroyed;
12470 return NS_OK;
12473 NS_IMETHODIMP
12474 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12475 NS_ENSURE_ARG(aResult);
12476 *aResult = mIsExecutingOnLoadHandler;
12477 return NS_OK;
12480 NS_IMETHODIMP
12481 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12482 nsCOMPtr<nsILayoutHistoryState> state;
12483 if (mozilla::SessionHistoryInParent()) {
12484 if (mActiveEntry) {
12485 state = mActiveEntry->GetLayoutHistoryState();
12487 } else {
12488 if (mOSHE) {
12489 state = mOSHE->GetLayoutHistoryState();
12492 state.forget(aLayoutHistoryState);
12493 return NS_OK;
12496 NS_IMETHODIMP
12497 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12498 if (mOSHE) {
12499 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12501 if (mActiveEntry) {
12502 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12504 return NS_OK;
12507 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12508 nsIInterfaceRequestor* aRequestor) {
12509 if (aRequestor) {
12510 mWeakPtr = do_GetWeakReference(aRequestor);
12514 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12515 mWeakPtr = nullptr;
12518 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12520 NS_IMETHODIMP
12521 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12522 void** aSink) {
12523 NS_ENSURE_ARG_POINTER(aSink);
12524 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12525 if (ifReq) {
12526 return ifReq->GetInterface(aIID, aSink);
12528 *aSink = nullptr;
12529 return NS_NOINTERFACE;
12532 //*****************************************************************************
12533 // nsDocShell::nsIAuthPromptProvider
12534 //*****************************************************************************
12536 NS_IMETHODIMP
12537 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12538 void** aResult) {
12539 // a priority prompt request will override a false mAllowAuth setting
12540 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12542 if (!mAllowAuth && !priorityPrompt) {
12543 return NS_ERROR_NOT_AVAILABLE;
12546 // we're either allowing auth, or it's a proxy request
12547 nsresult rv;
12548 nsCOMPtr<nsIPromptFactory> wwatch =
12549 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12550 NS_ENSURE_SUCCESS(rv, rv);
12552 rv = EnsureScriptEnvironment();
12553 NS_ENSURE_SUCCESS(rv, rv);
12555 // Get the an auth prompter for our window so that the parenting
12556 // of the dialogs works as it should when using tabs.
12558 return wwatch->GetPrompt(mScriptGlobal, aIID,
12559 reinterpret_cast<void**>(aResult));
12562 //*****************************************************************************
12563 // nsDocShell::nsILoadContext
12564 //*****************************************************************************
12566 NS_IMETHODIMP
12567 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12568 CallGetInterface(this, aWindow);
12569 return NS_OK;
12572 NS_IMETHODIMP
12573 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12574 return mBrowsingContext->GetTopWindow(aWindow);
12577 NS_IMETHODIMP
12578 nsDocShell::GetTopFrameElement(Element** aElement) {
12579 return mBrowsingContext->GetTopFrameElement(aElement);
12582 NS_IMETHODIMP
12583 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12584 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12587 NS_IMETHODIMP
12588 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12589 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12592 NS_IMETHODIMP
12593 nsDocShell::GetIsContent(bool* aIsContent) {
12594 *aIsContent = (mItemType == typeContent);
12595 return NS_OK;
12598 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12599 MOZ_ASSERT(aURI, "Must have a URI!");
12601 if (!mFiredUnloadEvent) {
12602 return true;
12605 if (!mLoadingURI) {
12606 return false;
12609 bool isPrivateWin = false;
12610 Document* doc = GetDocument();
12611 if (doc) {
12612 isPrivateWin =
12613 doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
12616 nsCOMPtr<nsIScriptSecurityManager> secMan =
12617 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12618 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12619 aURI, mLoadingURI, false, isPrivateWin));
12623 // Routines for selection and clipboard
12625 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12626 nsIController** aResult) {
12627 NS_ENSURE_ARG_POINTER(aResult);
12628 *aResult = nullptr;
12630 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12632 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12633 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12635 return root->GetControllerForCommand(aCommand, false /* for any window */,
12636 aResult);
12639 NS_IMETHODIMP
12640 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12641 NS_ENSURE_ARG_POINTER(aResult);
12642 *aResult = false;
12644 nsresult rv = NS_ERROR_FAILURE;
12646 nsCOMPtr<nsIController> controller;
12647 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12648 if (controller) {
12649 rv = controller->IsCommandEnabled(aCommand, aResult);
12652 return rv;
12655 NS_IMETHODIMP
12656 nsDocShell::DoCommand(const char* aCommand) {
12657 nsresult rv = NS_ERROR_FAILURE;
12659 nsCOMPtr<nsIController> controller;
12660 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12661 if (controller) {
12662 rv = controller->DoCommand(aCommand);
12665 return rv;
12668 NS_IMETHODIMP
12669 nsDocShell::DoCommandWithParams(const char* aCommand,
12670 nsICommandParams* aParams) {
12671 nsCOMPtr<nsIController> controller;
12672 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12673 if (NS_WARN_IF(NS_FAILED(rv))) {
12674 return rv;
12677 nsCOMPtr<nsICommandController> commandController =
12678 do_QueryInterface(controller, &rv);
12679 if (NS_WARN_IF(NS_FAILED(rv))) {
12680 return rv;
12683 return commandController->DoCommandWithParams(aCommand, aParams);
12686 nsresult nsDocShell::EnsureCommandHandler() {
12687 if (!mCommandManager) {
12688 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12689 mCommandManager = new nsCommandManager(domWindow);
12692 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12695 // link handling
12697 class OnLinkClickEvent : public Runnable {
12698 public:
12699 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12700 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12701 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12703 NS_IMETHOD Run() override {
12704 AutoPopupStatePusher popupStatePusher(mPopupState);
12706 // We need to set up an AutoJSAPI here for the following reason: When we
12707 // do OnLinkClickSync we'll eventually end up in
12708 // nsGlobalWindow::OpenInternal which only does popup blocking if
12709 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12710 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12711 // concerned.
12712 AutoJSAPI jsapi;
12713 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12714 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12715 mTriggeringPrincipal);
12717 return NS_OK;
12720 private:
12721 RefPtr<nsDocShell> mHandler;
12722 nsCOMPtr<nsIContent> mContent;
12723 RefPtr<nsDocShellLoadState> mLoadState;
12724 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12725 PopupBlocker::PopupControlState mPopupState;
12726 bool mNoOpenerImplied;
12727 bool mIsTrusted;
12730 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12731 nsDocShellLoadState* aLoadState,
12732 bool aNoOpenerImplied, bool aIsTrusted,
12733 nsIPrincipal* aTriggeringPrincipal)
12734 : mozilla::Runnable("OnLinkClickEvent"),
12735 mHandler(aHandler),
12736 mContent(aContent),
12737 mLoadState(aLoadState),
12738 mTriggeringPrincipal(aTriggeringPrincipal),
12739 mPopupState(PopupBlocker::GetPopupControlState()),
12740 mNoOpenerImplied(aNoOpenerImplied),
12741 mIsTrusted(aIsTrusted) {}
12743 nsresult nsDocShell::OnLinkClick(
12744 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12745 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12746 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12747 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12748 #ifndef ANDROID
12749 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12750 #endif
12751 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12753 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12754 return NS_OK;
12757 // On history navigation through Back/Forward buttons, don't execute
12758 // automatic JavaScript redirection such as |anchorElement.click()| or
12759 // |formElement.submit()|.
12761 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12762 // nsDocShell::OnLinkClickSync(...) instead.
12763 if (ShouldBlockLoadingForBackButton()) {
12764 return NS_OK;
12767 if (aContent->IsEditable()) {
12768 return NS_OK;
12771 Document* ownerDoc = aContent->OwnerDoc();
12772 if (nsContentUtils::IsExternalProtocol(aURI)) {
12773 ownerDoc->EnsureNotEnteringAndExitFullscreen();
12776 bool noOpenerImplied = false;
12777 nsAutoString target(aTargetSpec);
12778 if (aFileName.IsVoid() &&
12779 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
12780 target = u"_blank";
12781 if (!aTargetSpec.Equals(target)) {
12782 noOpenerImplied = true;
12786 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12787 loadState->SetTarget(target);
12788 loadState->SetFileName(aFileName);
12789 loadState->SetPostDataStream(aPostDataStream);
12790 loadState->SetHeadersStream(aHeadersDataStream);
12791 loadState->SetFirstParty(true);
12792 loadState->SetTriggeringPrincipal(
12793 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12794 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12795 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12796 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12798 nsCOMPtr<nsIRunnable> ev =
12799 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12800 aIsTrusted, aTriggeringPrincipal);
12801 return Dispatch(ev.forget());
12804 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12805 nsIURI* aLinkURI, nsIContent* aContent,
12806 bool aIsUserTriggered) {
12807 if (net::SchemeIsJavascript(aLinkURI)) {
12808 return false;
12811 // External links from within app tabs should always open in new tabs
12812 // instead of replacing the app tab's page (Bug 575561)
12813 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12814 // get either host, just return false to use the original target.
12815 nsAutoCString linkHost;
12816 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12817 return false;
12820 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12821 // privileged code to change the default targeting behaviour. In particular,
12822 // if a user-initiated link click for the (or targetting the) top-level frame
12823 // is detected, we default the target to "_blank" to give it a new
12824 // top-level BrowsingContext.
12825 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
12826 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
12827 aOriginalTarget == u"_top"_ns)) {
12828 return true;
12831 // Don't modify non-default targets.
12832 if (!aOriginalTarget.IsEmpty()) {
12833 return false;
12836 // Only check targets that are in extension panels or app tabs.
12837 // (isAppTab will be false for app tab subframes).
12838 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12839 if (!mmGroup.EqualsLiteral("webext-browsers") &&
12840 !mBrowsingContext->IsAppTab()) {
12841 return false;
12844 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12845 if (!docURI) {
12846 return false;
12849 nsAutoCString docHost;
12850 if (NS_FAILED(docURI->GetHost(docHost))) {
12851 return false;
12854 if (linkHost.Equals(docHost)) {
12855 return false;
12858 // Special case: ignore "www" prefix if it is part of host string
12859 return linkHost.Length() < docHost.Length()
12860 ? !docHost.Equals("www."_ns + linkHost)
12861 : !linkHost.Equals("www."_ns + docHost);
12864 static bool ElementCanHaveNoopener(nsIContent* aContent) {
12865 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12866 // the HTML, XHTML, or SVG namespace.
12867 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
12868 nsGkAtoms::form) ||
12869 aContent->IsSVGElement(nsGkAtoms::a);
12872 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12873 nsDocShellLoadState* aLoadState,
12874 bool aNoOpenerImplied,
12875 nsIPrincipal* aTriggeringPrincipal) {
12876 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12877 return NS_OK;
12880 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12881 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12882 // but |HTMLFormElement::SubmitSubmission(...)|.
12883 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
12884 ShouldBlockLoadingForBackButton()) {
12885 return NS_OK;
12888 if (aContent->IsEditable()) {
12889 return NS_OK;
12892 // if the triggeringPrincipal is not passed explicitly, then we
12893 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12894 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
12895 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
12898 // defer to an external protocol handler if necessary...
12899 nsCOMPtr<nsIExternalProtocolService> extProtService =
12900 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12901 if (extProtService) {
12902 nsAutoCString scheme;
12903 aLoadState->URI()->GetScheme(scheme);
12904 if (!scheme.IsEmpty()) {
12905 // if the URL scheme does not correspond to an exposed protocol, then
12906 // we need to hand this link click over to the external protocol
12907 // handler.
12908 bool isExposed;
12909 nsresult rv =
12910 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12911 if (NS_SUCCEEDED(rv) && !isExposed) {
12912 return extProtService->LoadURI(
12913 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
12914 /* aTriggeredExternally */
12915 false,
12916 /* aHasValidUserGestureActivation */
12917 aContent->OwnerDoc()->HasValidTransientUserGestureActivation());
12922 uint32_t triggeringSandboxFlags = 0;
12923 uint64_t triggeringWindowId = 0;
12924 bool triggeringStorageAccess = false;
12925 if (mBrowsingContext) {
12926 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
12927 triggeringWindowId = aContent->OwnerDoc()->InnerWindowID();
12928 triggeringStorageAccess = aContent->OwnerDoc()->UsingStorageAccess();
12931 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12932 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
12933 bool triggeringPrincipalIsSystemPrincipal =
12934 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
12935 if (elementCanHaveNoopener) {
12936 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
12937 nsAutoString relString;
12938 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
12939 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
12940 relString);
12942 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
12943 bool explicitOpenerSet = false;
12945 // The opener behaviour follows a hierarchy, such that if a higher
12946 // priority behaviour is specified, it always takes priority. That
12947 // priority is currently: norefrerer > noopener > opener > default
12949 while (tok.hasMoreTokens()) {
12950 const nsAString& token = tok.nextToken();
12951 if (token.LowerCaseEqualsLiteral("noreferrer")) {
12952 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
12953 INTERNAL_LOAD_FLAGS_NO_OPENER;
12954 // noreferrer cannot be overwritten by a 'rel=opener'.
12955 explicitOpenerSet = true;
12956 break;
12959 if (token.LowerCaseEqualsLiteral("noopener")) {
12960 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12961 explicitOpenerSet = true;
12964 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12965 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
12966 explicitOpenerSet = true;
12970 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12971 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
12972 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12975 if (aNoOpenerImplied) {
12976 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12980 // Get the owner document of the link that was clicked, this will be
12981 // the document that the link is in, or the last document that the
12982 // link was in. From that document, we'll get the URI to use as the
12983 // referrer, since the current URI in this docshell may be a
12984 // new document that we're in the process of loading.
12985 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
12987 // Now check that the referrerDoc's inner window is the current inner
12988 // window for mScriptGlobal. If it's not, then we don't want to
12989 // follow this link.
12990 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
12991 if (!mScriptGlobal || !referrerInner ||
12992 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
12993 // We're no longer the current inner window
12994 return NS_OK;
12997 // referrer could be null here in some odd cases, but that's ok,
12998 // we'll just load the link w/o sending a referrer in those cases.
13000 // If this is an anchor element, grab its type property to use as a hint
13001 nsAutoString typeHint;
13002 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13003 if (anchor) {
13004 anchor->GetType(typeHint);
13005 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13006 nsAutoCString type, dummy;
13007 NS_ParseRequestContentType(utf8Hint, type, dummy);
13008 CopyUTF8toUTF16(type, typeHint);
13011 uint32_t loadType = LOAD_LINK;
13012 if (aLoadState->IsFormSubmission()) {
13013 if (aLoadState->Target().IsEmpty()) {
13014 // We set the right load type here for form submissions with an empty
13015 // target. Form submission with a non-empty target are handled in
13016 // nsDocShell::PerformRetargeting after we've selected the correct target
13017 // BC.
13018 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
13020 } else {
13021 // Link click can be triggered inside an onload handler, and we don't want
13022 // to add history entry in this case.
13023 bool inOnLoadHandler = false;
13024 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13025 if (inOnLoadHandler) {
13026 loadType = LOAD_NORMAL_REPLACE;
13030 nsCOMPtr<nsIReferrerInfo> referrerInfo =
13031 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
13032 : new ReferrerInfo(*referrerDoc);
13033 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
13035 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
13036 aLoadState->SetTriggeringWindowId(triggeringWindowId);
13037 aLoadState->SetTriggeringStorageAccess(triggeringStorageAccess);
13038 aLoadState->SetReferrerInfo(referrerInfo);
13039 aLoadState->SetInternalLoadFlags(flags);
13040 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
13041 aLoadState->SetLoadType(loadType);
13042 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
13043 aLoadState->SetHasValidUserGestureActivation(
13044 context && context->HasValidTransientUserGestureActivation());
13046 nsresult rv = InternalLoad(aLoadState);
13048 if (NS_SUCCEEDED(rv)) {
13049 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
13050 referrerInfo);
13053 return rv;
13056 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13057 const nsAString& aTargetSpec) {
13058 if (aContent->IsEditable()) {
13059 return NS_OK;
13062 nsresult rv = NS_ERROR_FAILURE;
13064 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13065 if (!browserChrome) {
13066 return rv;
13069 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13070 nsAutoCString spec;
13071 rv = exposableURI->GetDisplaySpec(spec);
13072 NS_ENSURE_SUCCESS(rv, rv);
13074 NS_ConvertUTF8toUTF16 uStr(spec);
13076 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13077 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13079 rv = browserChrome->SetLinkStatus(uStr);
13080 return rv;
13083 nsresult nsDocShell::OnLeaveLink() {
13084 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13085 nsresult rv = NS_ERROR_FAILURE;
13087 if (browserChrome) {
13088 rv = browserChrome->SetLinkStatus(u""_ns);
13090 return rv;
13093 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13094 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13095 UserActivation::IsHandlingUserInput() ||
13096 !Preferences::GetBool("accessibility.blockjsredirection")) {
13097 return false;
13100 bool canGoForward = false;
13101 GetCanGoForward(&canGoForward);
13102 return canGoForward;
13105 bool nsDocShell::PluginsAllowedInCurrentDoc() {
13106 if (!mDocumentViewer) {
13107 return false;
13110 Document* doc = mDocumentViewer->GetDocument();
13111 if (!doc) {
13112 return false;
13115 return doc->GetAllowPlugins();
13118 //----------------------------------------------------------------------
13119 // Web Shell Services API
13121 // This functions is only called when a new charset is detected in loading a
13122 // document.
13123 nsresult nsDocShell::CharsetChangeReloadDocument(
13124 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13125 // XXX hack. keep the aCharset and aSource wait to pick it up
13126 nsCOMPtr<nsIDocumentViewer> viewer;
13127 NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer)), NS_ERROR_FAILURE);
13128 if (viewer) {
13129 int32_t source;
13130 Unused << viewer->GetReloadEncodingAndSource(&source);
13131 if (aSource > source) {
13132 viewer->SetReloadEncodingAndSource(aEncoding, aSource);
13133 if (eCharsetReloadRequested != mCharsetReloadState) {
13134 mCharsetReloadState = eCharsetReloadRequested;
13135 switch (mLoadType) {
13136 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13137 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13138 LOAD_FLAGS_BYPASS_PROXY);
13139 case LOAD_RELOAD_BYPASS_CACHE:
13140 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13141 default:
13142 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13147 // return failure if this request is not accepted due to mCharsetReloadState
13148 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13151 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13152 if (eCharsetReloadRequested != mCharsetReloadState) {
13153 Stop(nsIWebNavigation::STOP_ALL);
13154 return NS_OK;
13156 // return failer if this request is not accepted due to mCharsetReloadState
13157 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13160 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13161 #if NS_PRINT_PREVIEW
13162 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mDocumentViewer);
13163 return viewer->ExitPrintPreview();
13164 #else
13165 return NS_OK;
13166 #endif
13169 /* [infallible] */
13170 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13171 bool* aIsTopLevelContentDocShell) {
13172 *aIsTopLevelContentDocShell = false;
13174 if (mItemType == typeContent) {
13175 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13178 return NS_OK;
13181 // Implements nsILoadContext.originAttributes
13182 NS_IMETHODIMP
13183 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13184 JS::MutableHandle<JS::Value> aVal) {
13185 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13188 // Implements nsIDocShell.GetOriginAttributes()
13189 NS_IMETHODIMP
13190 nsDocShell::GetOriginAttributes(JSContext* aCx,
13191 JS::MutableHandle<JS::Value> aVal) {
13192 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13195 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13196 nsIURI* aURI) {
13197 MOZ_ASSERT(aPrincipal);
13198 MOZ_ASSERT(aURI);
13200 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13201 return false;
13204 nsCOMPtr<nsIDocShellTreeItem> parent;
13205 GetInProcessSameTypeParent(getter_AddRefs(parent));
13206 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13207 nsPIDOMWindowInner* parentInner =
13208 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13210 StorageAccess storage =
13211 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13213 // If the partitioned service worker is enabled, service worker is allowed to
13214 // control the window if partition is enabled.
13215 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13216 RefPtr<Document> doc = parentInner->GetExtantDoc();
13218 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13219 return true;
13223 return storage == StorageAccess::eAllow;
13226 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13227 MOZ_ASSERT(!mIsBeingDestroyed);
13228 return mBrowsingContext->SetOriginAttributes(aAttrs);
13231 NS_IMETHODIMP
13232 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13233 RefPtr<nsDocShell> self = this;
13234 RefPtr<ChildProcessChannelListener> cpcl =
13235 ChildProcessChannelListener::GetSingleton();
13237 // Call into InternalLoad with the pending channel when it is received.
13238 cpcl->RegisterCallback(
13239 aIdentifier, [self, aHistoryIndex](
13240 nsDocShellLoadState* aLoadState,
13241 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13242 aStreamFilterEndpoints,
13243 nsDOMNavigationTiming* aTiming) {
13244 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13245 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13246 aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
13247 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
13248 return NS_BINDING_ABORTED;
13251 self->mLoadType = aLoadState->LoadType();
13252 nsCOMPtr<nsIURI> previousURI;
13253 uint32_t previousFlags = 0;
13254 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13255 getter_AddRefs(previousURI), &previousFlags);
13256 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13257 previousURI, previousFlags);
13259 if (aTiming) {
13260 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13261 self->mBlankTiming = false;
13264 // If we're performing a history load, locate the correct history entry,
13265 // and set the relevant bits on our loadState.
13266 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13267 !mozilla::SessionHistoryInParent()) {
13268 nsCOMPtr<nsISHistory> legacySHistory =
13269 self->GetSessionHistory()->LegacySHistory();
13271 nsCOMPtr<nsISHEntry> entry;
13272 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13273 getter_AddRefs(entry));
13274 if (NS_SUCCEEDED(rv)) {
13275 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13276 aLoadState->SetLoadType(LOAD_HISTORY);
13277 aLoadState->SetSHEntry(entry);
13281 self->InternalLoad(aLoadState);
13283 if (aLoadState->GetOriginalURIString().isSome()) {
13284 // Save URI string in case it's needed later when
13285 // sending to search engine service in EndPageLoad()
13286 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13289 for (auto& endpoint : aStreamFilterEndpoints) {
13290 extensions::StreamFilterParent::Attach(
13291 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13294 // If the channel isn't pending, then it means that InternalLoad
13295 // never connected it, and we shouldn't try to continue. This
13296 // can happen even if InternalLoad returned NS_OK.
13297 bool pending = false;
13298 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13299 NS_ASSERTION(pending, "We should have connected the pending channel!");
13300 if (!pending) {
13301 return NS_BINDING_ABORTED;
13303 return NS_OK;
13305 return NS_OK;
13308 NS_IMETHODIMP
13309 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13310 JSContext* aCx) {
13311 OriginAttributes attrs;
13312 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13313 return NS_ERROR_INVALID_ARG;
13316 return SetOriginAttributes(attrs);
13319 NS_IMETHODIMP
13320 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13321 if (PresShell* presShell = GetPresShell()) {
13322 *aOut = presShell->AsyncPanZoomEnabled();
13323 return NS_OK;
13326 // If we don't have a presShell, fall back to the default platform value of
13327 // whether or not APZ is enabled.
13328 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13329 return NS_OK;
13332 bool nsDocShell::HasUnloadedParent() {
13333 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13334 wc = wc->GetParentWindowContext()) {
13335 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13336 wc->GetBrowsingContext()->IsDiscarded()) {
13337 // If a parent is OOP and the parent WindowContext is no
13338 // longer current, we can assume the parent was unloaded.
13339 return true;
13342 if (wc->GetBrowsingContext()->IsInProcess() &&
13343 (!wc->GetBrowsingContext()->GetDocShell() ||
13344 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13345 return true;
13348 return false;
13351 /* static */
13352 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13353 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13354 aLoadType & LOAD_CMD_HISTORY);
13357 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13358 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13359 return;
13362 // Global history is interested into sub-frame visits only for link-coloring
13363 // purposes, thus title updates are skipped for those.
13365 // Moreover, some iframe documents (such as the ones created via
13366 // document.open()) inherit the document uri of the caller, which would cause
13367 // us to override a previously set page title with one from the subframe.
13368 if (IsSubframe()) {
13369 return;
13372 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13373 history->SetURITitle(aURI, mTitle);
13377 bool nsDocShell::IsInvisible() { return mInvisible; }
13379 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13381 /* static */
13382 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13383 const nsString& aKeyword) {
13384 if (aProvider.IsEmpty()) {
13385 return;
13387 nsresult rv;
13388 nsCOMPtr<nsISupportsString> isupportsString =
13389 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13390 NS_ENSURE_SUCCESS_VOID(rv);
13392 rv = isupportsString->SetData(aProvider);
13393 NS_ENSURE_SUCCESS_VOID(rv);
13395 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13396 if (obsSvc) {
13397 // Note that "keyword-search" refers to a search via the url
13398 // bar, not a bookmarks keyword search.
13399 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13403 NS_IMETHODIMP
13404 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13405 bool* aShouldIntercept) {
13406 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13407 aShouldIntercept);
13410 NS_IMETHODIMP
13411 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13412 return mInterceptController->ChannelIntercepted(aChannel);
13415 bool nsDocShell::InFrameSwap() {
13416 RefPtr<nsDocShell> shell = this;
13417 do {
13418 if (shell->mInFrameSwap) {
13419 return true;
13421 shell = shell->GetInProcessParentDocshell();
13422 } while (shell);
13423 return false;
13426 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13427 return std::move(mInitialClientSource);
13430 NS_IMETHODIMP
13431 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13432 if (!NS_SUCCEEDED(EnsureEditorData())) {
13433 return NS_ERROR_FAILURE;
13436 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13437 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13440 NS_IMETHODIMP
13441 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13442 *aBrowserChild = GetBrowserChild().take();
13443 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13446 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13447 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13448 return tc.forget();
13451 nsCommandManager* nsDocShell::GetCommandManager() {
13452 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13453 return mCommandManager;
13456 NS_IMETHODIMP_(void)
13457 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13458 mBrowsingContext->GetOriginAttributes(aAttrs);
13461 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13462 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13463 return docShell->GetHTMLEditorInternal();
13466 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13467 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13468 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13471 #define MATRIX_LENGTH 20
13473 NS_IMETHODIMP
13474 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13475 if (aMatrix.Length() == MATRIX_LENGTH) {
13476 mColorMatrix.reset(new gfx::Matrix5x4());
13477 static_assert(
13478 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13479 "Size mismatch for our memcpy");
13480 memcpy(mColorMatrix->components, aMatrix.Elements(),
13481 sizeof(mColorMatrix->components));
13482 } else if (aMatrix.Length() == 0) {
13483 mColorMatrix.reset();
13484 } else {
13485 return NS_ERROR_INVALID_ARG;
13488 PresShell* presShell = GetPresShell();
13489 if (!presShell) {
13490 return NS_ERROR_FAILURE;
13493 nsIFrame* frame = presShell->GetRootFrame();
13494 if (!frame) {
13495 return NS_ERROR_FAILURE;
13498 frame->SchedulePaint();
13500 return NS_OK;
13503 NS_IMETHODIMP
13504 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13505 if (mColorMatrix) {
13506 aMatrix.SetLength(MATRIX_LENGTH);
13507 static_assert(
13508 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13509 "Size mismatch for our memcpy");
13510 memcpy(aMatrix.Elements(), mColorMatrix->components,
13511 MATRIX_LENGTH * sizeof(float));
13514 return NS_OK;
13517 #undef MATRIX_LENGTH
13519 NS_IMETHODIMP
13520 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13521 *aForceReload = IsForceReloading();
13522 return NS_OK;
13525 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13527 NS_IMETHODIMP
13528 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13529 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13530 return NS_OK;
13533 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13535 bool nsDocShell::GetIsAttemptingToNavigate() {
13536 // XXXbz the document.open spec says to abort even if there's just a
13537 // queued navigation task, sort of. It's not clear whether browsers
13538 // actually do that, and we didn't use to do it, so for now let's
13539 // not do that.
13540 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13541 if (mDocumentRequest) {
13542 // There's definitely a navigation in progress.
13543 return true;
13546 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13547 // until the script runs, which means they're not sending loadgroup
13548 // notifications and hence not getting set as mDocumentRequest. Look through
13549 // our loadgroup for document-level javascript: loads.
13550 if (!mLoadGroup) {
13551 return false;
13554 nsCOMPtr<nsISimpleEnumerator> requests;
13555 mLoadGroup->GetRequests(getter_AddRefs(requests));
13556 bool hasMore = false;
13557 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13558 nsCOMPtr<nsISupports> elem;
13559 requests->GetNext(getter_AddRefs(elem));
13560 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13561 if (!scriptChannel) {
13562 continue;
13565 if (scriptChannel->GetIsDocumentLoad()) {
13566 // This is a javascript: load that might lead to a new document,
13567 // hence a navigation.
13568 return true;
13572 return mCheckingSessionHistory;
13575 void nsDocShell::SetLoadingSessionHistoryInfo(
13576 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
13577 bool aNeedToReportActiveAfterLoadingBecomesActive) {
13578 // FIXME Would like to assert this, but can't yet.
13579 // MOZ_ASSERT(!mLoadingEntry);
13580 MOZ_LOG(gSHLog, LogLevel::Debug,
13581 ("Setting the loading entry on nsDocShell %p to %s", this,
13582 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13583 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13584 mNeedToReportActiveAfterLoadingBecomesActive =
13585 aNeedToReportActiveAfterLoadingBecomesActive;
13588 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13589 uint32_t aCacheKey,
13590 nsIURI* aPreviousURI) {
13591 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13593 MOZ_LOG(gSHLog, LogLevel::Debug,
13594 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13596 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
13597 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13598 mActiveEntryIsLoadingFromSessionHistory =
13599 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13600 if (mLoadingEntry) {
13601 MOZ_LOG(gSHLog, LogLevel::Debug,
13602 ("Moving the loading entry to the active entry on nsDocShell %p "
13603 "to %s",
13604 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13605 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13606 mLoadingEntry.swap(loadingEntry);
13607 if (!mActiveEntryIsLoadingFromSessionHistory) {
13608 if (mNeedToReportActiveAfterLoadingBecomesActive) {
13609 // Needed to pass various history length WPTs.
13610 mBrowsingContext->SetActiveSessionHistoryEntry(
13611 mozilla::Nothing(), mActiveEntry.get(), mLoadType,
13612 /* aUpdatedCacheKey = */ 0, false);
13614 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13617 mNeedToReportActiveAfterLoadingBecomesActive = false;
13619 if (mActiveEntry) {
13620 if (aCacheKey != 0) {
13621 mActiveEntry->SetCacheKey(aCacheKey);
13623 MOZ_ASSERT(loadingEntry);
13624 uint32_t loadType =
13625 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13627 if (loadingEntry->mLoadId != UINT64_MAX) {
13628 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13629 // does require a non-null uri if this is for a refresh load of the same
13630 // URI, but in that case mCurrentURI won't be null here.
13631 mBrowsingContext->SessionHistoryCommit(
13632 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
13633 aPersist, false, aExpired, aCacheKey);
13638 static bool IsFaviconLoad(nsIRequest* aRequest) {
13639 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13640 if (!channel) {
13641 return false;
13644 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13645 return li && li->InternalContentPolicyType() ==
13646 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13649 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13650 nsIRequest* aRequest) {
13651 // Ignore favicon loads, they don't need to block caching.
13652 if (IsFaviconLoad(aRequest)) {
13653 return;
13656 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13658 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13660 if (mBrowsingContext->GetCurrentWindowContext()) {
13661 // We have three states: no request, one request with an id and
13662 // eiher one request without an id or multiple requests. Nothing() is no
13663 // request, Some(non-zero) is one request with an id and Some(0) is one
13664 // request without an id or multiple requests.
13665 Maybe<uint64_t> singleChannelId;
13666 if (mRequestForBlockingFromBFCacheCount > 1) {
13667 singleChannelId = Some(0);
13668 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13669 nsCOMPtr<nsIIdentChannel> identChannel;
13670 if (aStartRequest) {
13671 identChannel = do_QueryInterface(aRequest);
13672 } else {
13673 // aChannel is the channel that's being removed, but we need to check if
13674 // the remaining channel in the loadgroup has an id.
13675 nsCOMPtr<nsISimpleEnumerator> requests;
13676 mLoadGroup->GetRequests(getter_AddRefs(requests));
13677 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13678 if (!IsFaviconLoad(request) &&
13679 !!(identChannel = do_QueryInterface(request))) {
13680 break;
13685 if (identChannel) {
13686 singleChannelId = Some(identChannel->ChannelId());
13687 } else {
13688 singleChannelId = Some(0);
13690 } else {
13691 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13692 singleChannelId = Nothing();
13695 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13696 nsAutoCString uri("[no uri]");
13697 if (mCurrentURI) {
13698 uri = mCurrentURI->GetSpecOrDefault();
13700 if (singleChannelId.isNothing()) {
13701 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13702 ("Loadgroup for %s doesn't have any requests relevant for "
13703 "blocking BFCache",
13704 uri.get()));
13705 } else if (singleChannelId.value() == 0) {
13706 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13707 ("Loadgroup for %s has multiple requests relevant for blocking "
13708 "BFCache",
13709 uri.get()));
13710 } else {
13711 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13712 ("Loadgroup for %s has one request with id %" PRIu64
13713 " relevant for blocking BFCache",
13714 uri.get(), singleChannelId.value()));
13718 if (mSingleChannelId != singleChannelId) {
13719 mSingleChannelId = singleChannelId;
13720 WindowGlobalChild* wgc =
13721 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13722 if (wgc) {
13723 wgc->SendSetSingleChannelId(singleChannelId);
13729 NS_IMETHODIMP
13730 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13731 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13732 nsAutoCString uri("[no uri]");
13733 if (mCurrentURI) {
13734 uri = mCurrentURI->GetSpecOrDefault();
13736 nsAutoCString name;
13737 aRequest->GetName(name);
13738 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13739 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13741 RecordSingleChannelId(true, aRequest);
13742 return nsDocLoader::OnStartRequest(aRequest);
13745 NS_IMETHODIMP
13746 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13747 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13748 nsAutoCString uri("[no uri]");
13749 if (mCurrentURI) {
13750 uri = mCurrentURI->GetSpecOrDefault();
13752 nsAutoCString name;
13753 aRequest->GetName(name);
13754 MOZ_LOG(
13755 gSHIPBFCacheLog, LogLevel::Verbose,
13756 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13758 RecordSingleChannelId(false, aRequest);
13759 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13762 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13763 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13765 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13766 nsCOMPtr<nsISimpleEnumerator> requests;
13767 mLoadGroup->GetRequests(getter_AddRefs(requests));
13768 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13769 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13770 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13771 static_cast<DocumentChannelChild*>(channel.get())
13772 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13775 mChannelToDisconnectOnPageHide = 0;