no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / docshell / base / nsDocShell.cpp
blobf2a9e0fa590119d9ce4d0efddd9ba6db9f9f44ff
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::GetCssErrorReportingEnabled(bool* aEnabled) {
1783 MOZ_ASSERT(aEnabled);
1784 *aEnabled = mCSSErrorReportingEnabled;
1785 return NS_OK;
1788 NS_IMETHODIMP
1789 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1790 mCSSErrorReportingEnabled = aEnabled;
1791 return NS_OK;
1794 NS_IMETHODIMP
1795 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1796 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1797 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1800 void nsDocShell::NotifyPrivateBrowsingChanged() {
1801 MOZ_ASSERT(!mIsBeingDestroyed);
1803 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1804 while (iter.HasMore()) {
1805 nsWeakPtr ref = iter.GetNext();
1806 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1807 if (!obs) {
1808 iter.Remove();
1809 } else {
1810 obs->PrivateModeChanged(UsePrivateBrowsing());
1815 NS_IMETHODIMP
1816 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1817 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1820 NS_IMETHODIMP
1821 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1822 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1825 NS_IMETHODIMP
1826 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1827 NS_ENSURE_ARG_POINTER(aResult);
1829 *aResult = mHasLoadedNonBlankURI;
1830 return NS_OK;
1833 NS_IMETHODIMP
1834 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1835 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1836 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1839 NS_IMETHODIMP
1840 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1841 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1844 NS_IMETHODIMP
1845 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1846 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1847 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1850 NS_IMETHODIMP
1851 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1852 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1855 NS_IMETHODIMP
1856 nsDocShell::AddWeakPrivacyTransitionObserver(
1857 nsIPrivacyTransitionObserver* aObserver) {
1858 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1859 if (!weakObs) {
1860 return NS_ERROR_NOT_AVAILABLE;
1862 mPrivacyObservers.AppendElement(weakObs);
1863 return NS_OK;
1866 NS_IMETHODIMP
1867 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1868 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1869 if (!weakObs) {
1870 return NS_ERROR_FAILURE;
1872 mReflowObservers.AppendElement(weakObs);
1873 return NS_OK;
1876 NS_IMETHODIMP
1877 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1878 nsWeakPtr obs = do_GetWeakReference(aObserver);
1879 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1882 NS_IMETHODIMP
1883 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1884 DOMHighResTimeStamp aStart,
1885 DOMHighResTimeStamp aEnd) {
1886 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1887 while (iter.HasMore()) {
1888 nsWeakPtr ref = iter.GetNext();
1889 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1890 if (!obs) {
1891 iter.Remove();
1892 } else if (aInterruptible) {
1893 obs->ReflowInterruptible(aStart, aEnd);
1894 } else {
1895 obs->Reflow(aStart, aEnd);
1898 return NS_OK;
1901 NS_IMETHODIMP
1902 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1903 NS_ENSURE_ARG_POINTER(aReturn);
1905 *aReturn = mAllowMetaRedirects;
1906 return NS_OK;
1909 NS_IMETHODIMP
1910 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1911 mAllowMetaRedirects = aValue;
1912 return NS_OK;
1915 NS_IMETHODIMP
1916 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1917 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1919 *aAllowSubframes = mAllowSubframes;
1920 return NS_OK;
1923 NS_IMETHODIMP
1924 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1925 mAllowSubframes = aAllowSubframes;
1926 return NS_OK;
1929 NS_IMETHODIMP
1930 nsDocShell::GetAllowImages(bool* aAllowImages) {
1931 NS_ENSURE_ARG_POINTER(aAllowImages);
1933 *aAllowImages = mAllowImages;
1934 return NS_OK;
1937 NS_IMETHODIMP
1938 nsDocShell::SetAllowImages(bool aAllowImages) {
1939 mAllowImages = aAllowImages;
1940 return NS_OK;
1943 NS_IMETHODIMP
1944 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1945 *aAllowMedia = mAllowMedia;
1946 return NS_OK;
1949 NS_IMETHODIMP
1950 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1951 mAllowMedia = aAllowMedia;
1953 // Mute or unmute audio contexts attached to the inner window.
1954 if (mScriptGlobal) {
1955 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1956 if (aAllowMedia) {
1957 innerWin->UnmuteAudioContexts();
1958 } else {
1959 innerWin->MuteAudioContexts();
1964 return NS_OK;
1967 NS_IMETHODIMP
1968 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1969 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1970 return NS_OK;
1973 NS_IMETHODIMP
1974 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
1975 mAllowDNSPrefetch = aAllowDNSPrefetch;
1976 return NS_OK;
1979 NS_IMETHODIMP
1980 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
1981 *aAllowWindowControl = mAllowWindowControl;
1982 return NS_OK;
1985 NS_IMETHODIMP
1986 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
1987 mAllowWindowControl = aAllowWindowControl;
1988 return NS_OK;
1991 NS_IMETHODIMP
1992 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
1993 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
1994 return NS_OK;
1997 NS_IMETHODIMP
1998 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
1999 BrowsingContext::Transaction txn;
2000 txn.SetAllowContentRetargeting(aAllowContentRetargeting);
2001 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
2002 return txn.Commit(mBrowsingContext);
2005 NS_IMETHODIMP
2006 nsDocShell::GetAllowContentRetargetingOnChildren(
2007 bool* aAllowContentRetargetingOnChildren) {
2008 *aAllowContentRetargetingOnChildren =
2009 mBrowsingContext->GetAllowContentRetargetingOnChildren();
2010 return NS_OK;
2013 NS_IMETHODIMP
2014 nsDocShell::SetAllowContentRetargetingOnChildren(
2015 bool aAllowContentRetargetingOnChildren) {
2016 return mBrowsingContext->SetAllowContentRetargetingOnChildren(
2017 aAllowContentRetargetingOnChildren);
2020 NS_IMETHODIMP
2021 nsDocShell::GetMayEnableCharacterEncodingMenu(
2022 bool* aMayEnableCharacterEncodingMenu) {
2023 *aMayEnableCharacterEncodingMenu = false;
2024 if (!mDocumentViewer) {
2025 return NS_OK;
2027 Document* doc = mDocumentViewer->GetDocument();
2028 if (!doc) {
2029 return NS_OK;
2031 if (doc->WillIgnoreCharsetOverride()) {
2032 return NS_OK;
2035 *aMayEnableCharacterEncodingMenu = true;
2036 return NS_OK;
2039 NS_IMETHODIMP
2040 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
2041 DocShellEnumeratorDirection aDirection,
2042 nsTArray<RefPtr<nsIDocShell>>& aResult) {
2043 aResult.Clear();
2045 nsDocShellEnumerator docShellEnum(
2046 (aDirection == ENUMERATE_FORWARDS)
2047 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2048 : nsDocShellEnumerator::EnumerationDirection::Backwards,
2049 aItemType, *this);
2051 nsresult rv = docShellEnum.BuildDocShellArray(aResult);
2052 if (NS_FAILED(rv)) {
2053 return rv;
2056 return NS_OK;
2059 NS_IMETHODIMP
2060 nsDocShell::GetAppType(AppType* aAppType) {
2061 *aAppType = mAppType;
2062 return NS_OK;
2065 NS_IMETHODIMP
2066 nsDocShell::SetAppType(AppType aAppType) {
2067 mAppType = aAppType;
2068 return NS_OK;
2071 NS_IMETHODIMP
2072 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2073 *aAllowAuth = mAllowAuth;
2074 return NS_OK;
2077 NS_IMETHODIMP
2078 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2079 mAllowAuth = aAllowAuth;
2080 return NS_OK;
2083 NS_IMETHODIMP
2084 nsDocShell::GetZoom(float* aZoom) {
2085 NS_ENSURE_ARG_POINTER(aZoom);
2086 *aZoom = 1.0f;
2087 return NS_OK;
2090 NS_IMETHODIMP
2091 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2093 NS_IMETHODIMP
2094 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
2095 NS_ENSURE_ARG_POINTER(aBusyFlags);
2097 *aBusyFlags = mBusyFlags;
2098 return NS_OK;
2101 NS_IMETHODIMP
2102 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
2103 bool* aTookFocus) {
2104 NS_ENSURE_ARG_POINTER(aTookFocus);
2106 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2107 if (chromeFocus) {
2108 if (aForward) {
2109 *aTookFocus =
2110 NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2111 } else {
2112 *aTookFocus =
2113 NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2115 } else {
2116 *aTookFocus = false;
2119 return NS_OK;
2122 NS_IMETHODIMP
2123 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2124 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
2125 delegate.forget(aLoadURIDelegate);
2126 return NS_OK;
2129 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
2130 if (nsCOMPtr<nsILoadURIDelegate> result =
2131 do_QueryActor("LoadURIDelegate", GetDocument())) {
2132 return result.forget();
2135 return nullptr;
2138 NS_IMETHODIMP
2139 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2140 *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
2141 return NS_OK;
2144 NS_IMETHODIMP
2145 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2146 return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
2149 NS_IMETHODIMP
2150 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
2151 *aPreviousEntryIndex = mPreviousEntryIndex;
2152 return NS_OK;
2155 NS_IMETHODIMP
2156 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
2157 *aLoadedEntryIndex = mLoadedEntryIndex;
2158 return NS_OK;
2161 NS_IMETHODIMP
2162 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2163 // These indices are used for fastback cache eviction, to determine
2164 // which session history entries are candidates for content viewer
2165 // eviction. We need to adjust by the number of entries that we
2166 // just purged from history, so that we look at the right session history
2167 // entries during eviction.
2168 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
2169 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
2171 for (auto* child : mChildList.ForwardRange()) {
2172 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2173 if (shell) {
2174 shell->HistoryPurged(aNumEntries);
2178 return NS_OK;
2181 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2182 // These indices are used for fastback cache eviction, to determine
2183 // which session history entries are candidates for content viewer
2184 // eviction. We need to adjust by the number of entries that we
2185 // just purged from history, so that we look at the right session history
2186 // entries during eviction.
2187 if (aIndex == mPreviousEntryIndex) {
2188 mPreviousEntryIndex = -1;
2189 } else if (aIndex < mPreviousEntryIndex) {
2190 --mPreviousEntryIndex;
2192 if (mLoadedEntryIndex == aIndex) {
2193 mLoadedEntryIndex = 0;
2194 } else if (aIndex < mLoadedEntryIndex) {
2195 --mLoadedEntryIndex;
2198 for (auto* child : mChildList.ForwardRange()) {
2199 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2200 if (shell) {
2201 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2205 return NS_OK;
2208 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2209 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2210 return NS_OK;
2213 NS_IMETHODIMP
2214 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2215 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2216 if (!aValue && mItemType == typeChrome && !parent) {
2217 // Window dragging is always allowed for top level
2218 // chrome docshells.
2219 return NS_ERROR_FAILURE;
2221 mWindowDraggingAllowed = aValue;
2222 return NS_OK;
2225 NS_IMETHODIMP
2226 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2227 // window dragging regions in CSS (-moz-window-drag:drag)
2228 // can be slow. Default behavior is to only allow it for
2229 // chrome top level windows.
2230 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2231 if (mItemType == typeChrome && !parent) {
2232 // Top level chrome window
2233 *aValue = true;
2234 } else {
2235 *aValue = mWindowDraggingAllowed;
2237 return NS_OK;
2240 NS_IMETHODIMP
2241 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2242 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2243 return NS_OK;
2246 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2247 if (mDocumentViewer) {
2248 Document* doc = mDocumentViewer->GetDocument();
2249 if (doc) {
2250 return doc->GetChannel();
2253 return nullptr;
2256 NS_IMETHODIMP
2257 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2258 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2259 if (!weakObs) {
2260 return NS_ERROR_FAILURE;
2262 mScrollObservers.AppendElement(weakObs);
2263 return NS_OK;
2266 NS_IMETHODIMP
2267 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2268 nsWeakPtr obs = do_GetWeakReference(aObserver);
2269 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2272 void nsDocShell::NotifyAsyncPanZoomStarted() {
2273 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2274 while (iter.HasMore()) {
2275 nsWeakPtr ref = iter.GetNext();
2276 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2277 if (obs) {
2278 obs->AsyncPanZoomStarted();
2279 } else {
2280 iter.Remove();
2285 void nsDocShell::NotifyAsyncPanZoomStopped() {
2286 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2287 while (iter.HasMore()) {
2288 nsWeakPtr ref = iter.GetNext();
2289 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2290 if (obs) {
2291 obs->AsyncPanZoomStopped();
2292 } else {
2293 iter.Remove();
2298 NS_IMETHODIMP
2299 nsDocShell::NotifyScrollObservers() {
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->ScrollPositionChanged();
2306 } else {
2307 iter.Remove();
2310 return NS_OK;
2313 //*****************************************************************************
2314 // nsDocShell::nsIDocShellTreeItem
2315 //*****************************************************************************
2317 NS_IMETHODIMP
2318 nsDocShell::GetName(nsAString& aName) {
2319 aName = mBrowsingContext->Name();
2320 return NS_OK;
2323 NS_IMETHODIMP
2324 nsDocShell::SetName(const nsAString& aName) {
2325 return mBrowsingContext->SetName(aName);
2328 NS_IMETHODIMP
2329 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2330 NS_ENSURE_ARG_POINTER(aResult);
2331 *aResult = mBrowsingContext->NameEquals(aName);
2332 return NS_OK;
2335 NS_IMETHODIMP
2336 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2337 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2338 return NS_OK;
2341 NS_IMETHODIMP
2342 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2343 if (mWillChangeProcess) {
2344 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2345 return NS_ERROR_FAILURE;
2348 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2351 NS_IMETHODIMP
2352 nsDocShell::ClearCachedPlatform() {
2353 nsCOMPtr<nsPIDOMWindowInner> win =
2354 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2355 if (win) {
2356 Navigator* navigator = win->Navigator();
2357 if (navigator) {
2358 navigator->ClearPlatformCache();
2362 return NS_OK;
2365 NS_IMETHODIMP
2366 nsDocShell::ClearCachedUserAgent() {
2367 nsCOMPtr<nsPIDOMWindowInner> win =
2368 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2369 if (win) {
2370 Navigator* navigator = win->Navigator();
2371 if (navigator) {
2372 navigator->ClearUserAgentCache();
2376 return NS_OK;
2379 NS_IMETHODIMP
2380 nsDocShell::GetMetaViewportOverride(
2381 MetaViewportOverride* aMetaViewportOverride) {
2382 NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
2384 *aMetaViewportOverride = mMetaViewportOverride;
2385 return NS_OK;
2388 NS_IMETHODIMP
2389 nsDocShell::SetMetaViewportOverride(
2390 MetaViewportOverride aMetaViewportOverride) {
2391 // We don't have a way to verify this coming from Javascript, so this check is
2392 // still needed.
2393 if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
2394 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
2395 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
2396 return NS_ERROR_INVALID_ARG;
2399 mMetaViewportOverride = aMetaViewportOverride;
2401 // Inform our presShell that it needs to re-check its need for a viewport
2402 // override.
2403 if (RefPtr<PresShell> presShell = GetPresShell()) {
2404 presShell->MaybeRecreateMobileViewportManager(true);
2407 return NS_OK;
2410 /* virtual */
2411 int32_t nsDocShell::ItemType() { return mItemType; }
2413 NS_IMETHODIMP
2414 nsDocShell::GetItemType(int32_t* aItemType) {
2415 NS_ENSURE_ARG_POINTER(aItemType);
2417 MOZ_DIAGNOSTIC_ASSERT(
2418 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2419 *aItemType = mItemType;
2420 return NS_OK;
2423 NS_IMETHODIMP
2424 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2425 if (!mParent) {
2426 *aParent = nullptr;
2427 } else {
2428 CallQueryInterface(mParent, aParent);
2430 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2431 // don't want to throw; we just want to return null.
2432 return NS_OK;
2435 // With Fission, related nsDocShell objects may exist in a different process. In
2436 // that case, this method will return `nullptr`, despite a parent nsDocShell
2437 // object existing.
2439 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2440 // parent entry is not in the current process, and handle the case where the
2441 // parent nsDocShell is inaccessible.
2442 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2443 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2444 return docshell.forget().downcast<nsDocShell>();
2447 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2448 MOZ_ASSERT(!mIsBeingDestroyed);
2450 // If there is an existing document then there is no need to create
2451 // a client for a future initial about:blank document.
2452 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindow() &&
2453 mScriptGlobal->GetCurrentInnerWindow()->GetExtantDoc()) {
2454 MOZ_DIAGNOSTIC_ASSERT(
2455 mScriptGlobal->GetCurrentInnerWindow()->GetClientInfo().isSome());
2456 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2457 return;
2460 // Don't recreate the initial client source. We call this multiple times
2461 // when DoChannelLoad() is called before CreateAboutBlankDocumentViewer.
2462 if (mInitialClientSource) {
2463 return;
2466 // Don't pre-allocate the client when we are sandboxed. The inherited
2467 // principal does not take sandboxing into account.
2468 // TODO: Refactor sandboxing principal code out so we can use it here.
2469 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2470 return;
2473 // We cannot get inherited foreign partitioned principal here. Instead, we
2474 // directly check which principal we want to inherit for the service worker.
2475 nsIPrincipal* principal =
2476 aPrincipal
2477 ? aPrincipal
2478 : GetInheritedPrincipal(
2479 false, StoragePrincipalHelper::
2480 ShouldUsePartitionPrincipalForServiceWorker(this));
2482 // Sometimes there is no principal available when we are called from
2483 // CreateAboutBlankDocumentViewer. For example, sometimes the principal
2484 // is only extracted from the load context after the document is created
2485 // in Document::ResetToURI(). Ideally we would do something similar
2486 // here, but for now lets just avoid the issue by not preallocating the
2487 // client.
2488 if (!principal) {
2489 return;
2492 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2493 if (!win) {
2494 return;
2497 mInitialClientSource = ClientManager::CreateSource(
2498 ClientType::Window, GetMainThreadSerialEventTarget(), principal);
2499 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2501 // Mark the initial client as execution ready, but owned by the docshell.
2502 // If the client is actually used this will cause ClientSource to force
2503 // the creation of the initial about:blank by calling
2504 // nsDocShell::GetDocument().
2505 mInitialClientSource->DocShellExecutionReady(this);
2507 // Next, check to see if the parent is controlled.
2508 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2509 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2510 nsPIDOMWindowInner* parentInner =
2511 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2512 if (!parentInner) {
2513 return;
2516 nsCOMPtr<nsIURI> uri;
2517 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
2519 // We're done if there is no parent controller or if this docshell
2520 // is not permitted to control for some reason.
2521 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2522 if (controller.isNothing() ||
2523 !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2524 return;
2527 mInitialClientSource->InheritController(controller.ref());
2530 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2531 if (mInitialClientSource) {
2532 Maybe<ClientInfo> result;
2533 result.emplace(mInitialClientSource->Info());
2534 return result;
2537 nsPIDOMWindowInner* innerWindow =
2538 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2539 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2541 if (!doc || !doc->IsInitialDocument()) {
2542 return Maybe<ClientInfo>();
2545 return innerWindow->GetClientInfo();
2548 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2549 bool wasFrame = IsSubframe();
2551 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2552 NS_ENSURE_SUCCESS(rv, rv);
2554 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2555 if (wasFrame != IsSubframe() && priorityGroup) {
2556 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2559 // Curse ambiguous nsISupports inheritance!
2560 nsISupports* parent = GetAsSupports(aParent);
2562 // If parent is another docshell, we inherit all their flags for
2563 // allowing plugins, scripting etc.
2564 bool value;
2565 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2567 if (parentAsDocShell) {
2568 if (mAllowMetaRedirects &&
2569 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2570 SetAllowMetaRedirects(value);
2572 if (mAllowSubframes &&
2573 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2574 SetAllowSubframes(value);
2576 if (mAllowImages &&
2577 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2578 SetAllowImages(value);
2580 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2581 if (mAllowWindowControl &&
2582 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2583 SetAllowWindowControl(value);
2585 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2586 value = false;
2588 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2590 // We don't need to inherit metaViewportOverride, because the viewport
2591 // is only relevant for the outermost nsDocShell, not for any iframes
2592 // like this that might be embedded within it.
2595 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2596 if (parentURIListener) {
2597 mContentListener->SetParentContentListener(parentURIListener);
2600 return NS_OK;
2603 void nsDocShell::MaybeRestoreWindowName() {
2604 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2605 return;
2608 // We only restore window.name for the top-level content.
2609 if (!mBrowsingContext->IsTopContent()) {
2610 return;
2613 nsAutoString name;
2615 // Following implements https://html.spec.whatwg.org/#history-traversal:
2616 // Step 4.4. Check if the loading entry has a name.
2618 if (mLSHE) {
2619 mLSHE->GetName(name);
2622 if (mLoadingEntry) {
2623 name = mLoadingEntry->mInfo.GetName();
2626 if (name.IsEmpty()) {
2627 return;
2630 // Step 4.4.1. Set the name to the browsing context.
2631 Unused << mBrowsingContext->SetName(name);
2633 // Step 4.4.2. Clear the name of all entries that are contiguous and
2634 // same-origin with the loading entry.
2635 if (mLSHE) {
2636 nsSHistory::WalkContiguousEntries(
2637 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
2640 if (mLoadingEntry) {
2641 // Clear the name of the session entry in the child side. For parent side,
2642 // the clearing will be done when we commit the history to the parent.
2643 mLoadingEntry->mInfo.SetName(EmptyString());
2647 void nsDocShell::StoreWindowNameToSHEntries() {
2648 MOZ_ASSERT(mBrowsingContext->IsTopContent());
2650 nsAutoString name;
2651 mBrowsingContext->GetName(name);
2653 if (mOSHE) {
2654 nsSHistory::WalkContiguousEntries(
2655 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2658 if (mozilla::SessionHistoryInParent()) {
2659 if (XRE_IsParentProcess()) {
2660 SessionHistoryEntry* entry =
2661 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2662 if (entry) {
2663 nsSHistory::WalkContiguousEntries(
2664 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2666 } else {
2667 // Ask parent process to store the name in entries.
2668 mozilla::Unused
2669 << ContentChild::GetSingleton()
2670 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2671 mBrowsingContext, name);
2676 NS_IMETHODIMP
2677 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2678 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2679 *aParent = do_AddRef(parentBC->GetDocShell()).take();
2681 return NS_OK;
2684 NS_IMETHODIMP
2685 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2686 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2688 RefPtr<nsDocShell> root = this;
2689 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2690 while (parent) {
2691 root = parent;
2692 parent = root->GetInProcessParentDocshell();
2695 root.forget(aRootTreeItem);
2696 return NS_OK;
2699 NS_IMETHODIMP
2700 nsDocShell::GetInProcessSameTypeRootTreeItem(
2701 nsIDocShellTreeItem** aRootTreeItem) {
2702 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2703 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2705 nsCOMPtr<nsIDocShellTreeItem> parent;
2706 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2707 NS_ERROR_FAILURE);
2708 while (parent) {
2709 *aRootTreeItem = parent;
2710 NS_ENSURE_SUCCESS(
2711 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2712 NS_ERROR_FAILURE);
2714 NS_ADDREF(*aRootTreeItem);
2715 return NS_OK;
2718 NS_IMETHODIMP
2719 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2720 NS_ENSURE_ARG_POINTER(aTreeOwner);
2722 *aTreeOwner = mTreeOwner;
2723 NS_IF_ADDREF(*aTreeOwner);
2724 return NS_OK;
2727 NS_IMETHODIMP
2728 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2729 if (mIsBeingDestroyed && aTreeOwner) {
2730 return NS_ERROR_FAILURE;
2733 // Don't automatically set the progress based on the tree owner for frames
2734 if (!IsSubframe()) {
2735 nsCOMPtr<nsIWebProgress> webProgress =
2736 do_QueryInterface(GetAsSupports(this));
2738 if (webProgress) {
2739 nsCOMPtr<nsIWebProgressListener> oldListener =
2740 do_QueryInterface(mTreeOwner);
2741 nsCOMPtr<nsIWebProgressListener> newListener =
2742 do_QueryInterface(aTreeOwner);
2744 if (oldListener) {
2745 webProgress->RemoveProgressListener(oldListener);
2748 if (newListener) {
2749 webProgress->AddProgressListener(newListener,
2750 nsIWebProgress::NOTIFY_ALL);
2755 mTreeOwner = aTreeOwner; // Weak reference per API
2757 for (auto* childDocLoader : mChildList.ForwardRange()) {
2758 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
2759 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2761 if (child->ItemType() == mItemType) {
2762 child->SetTreeOwner(aTreeOwner);
2766 // If we're in the content process and have had a TreeOwner set on us, extract
2767 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2768 // that it hasn't changed.
2769 if (mTreeOwner && XRE_IsContentProcess()) {
2770 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2771 MOZ_ASSERT(newBrowserChild,
2772 "No BrowserChild actor for tree owner in Content!");
2774 if (mBrowserChild) {
2775 nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2776 do_QueryReferent(mBrowserChild);
2777 MOZ_RELEASE_ASSERT(
2778 oldBrowserChild == newBrowserChild,
2779 "Cannot change BrowserChild during nsDocShell lifetime!");
2780 } else {
2781 mBrowserChild = do_GetWeakReference(newBrowserChild);
2785 return NS_OK;
2788 NS_IMETHODIMP
2789 nsDocShell::GetHistoryID(nsID& aID) {
2790 aID = mBrowsingContext->GetHistoryID();
2791 return NS_OK;
2794 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
2796 NS_IMETHODIMP
2797 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2798 *aIsInUnload = mFiredUnloadEvent;
2799 return NS_OK;
2802 NS_IMETHODIMP
2803 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2804 NS_ENSURE_ARG_POINTER(aChildCount);
2805 *aChildCount = mChildList.Length();
2806 return NS_OK;
2809 NS_IMETHODIMP
2810 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2811 NS_ENSURE_ARG_POINTER(aChild);
2813 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2814 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2816 // Make sure we're not creating a loop in the docshell tree
2817 nsDocLoader* ancestor = this;
2818 do {
2819 if (childAsDocLoader == ancestor) {
2820 return NS_ERROR_ILLEGAL_VALUE;
2822 ancestor = ancestor->GetParent();
2823 } while (ancestor);
2825 // Make sure to remove the child from its current parent.
2826 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2827 if (childsParent) {
2828 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2829 NS_ENSURE_SUCCESS(rv, rv);
2832 // Make sure to clear the treeowner in case this child is a different type
2833 // from us.
2834 aChild->SetTreeOwner(nullptr);
2836 nsresult res = AddChildLoader(childAsDocLoader);
2837 NS_ENSURE_SUCCESS(res, res);
2838 NS_ASSERTION(!mChildList.IsEmpty(),
2839 "child list must not be empty after a successful add");
2841 /* Set the child's global history if the parent has one */
2842 if (mBrowsingContext->GetUseGlobalHistory()) {
2843 // childDocShell->SetUseGlobalHistory(true);
2844 // this should be set through BC inherit
2845 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
2848 if (aChild->ItemType() != mItemType) {
2849 return NS_OK;
2852 aChild->SetTreeOwner(mTreeOwner);
2854 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2855 if (!childAsDocShell) {
2856 return NS_OK;
2859 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2861 // Now take this document's charset and set the child's parentCharset field
2862 // to it. We'll later use that field, in the loading process, for the
2863 // charset choosing algorithm.
2864 // If we fail, at any point, we just return NS_OK.
2865 // This code has some performance impact. But this will be reduced when
2866 // the current charset will finally be stored as an Atom, avoiding the
2867 // alias resolution extra look-up.
2869 // we are NOT going to propagate the charset is this Chrome's docshell
2870 if (mItemType == nsIDocShellTreeItem::typeChrome) {
2871 return NS_OK;
2874 // get the parent's current charset
2875 if (!mDocumentViewer) {
2876 return NS_OK;
2878 Document* doc = mDocumentViewer->GetDocument();
2879 if (!doc) {
2880 return NS_OK;
2883 const Encoding* parentCS = doc->GetDocumentCharacterSet();
2884 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2885 // set the child's parentCharset
2886 childAsDocShell->SetParentCharset(parentCS, charsetSource,
2887 doc->NodePrincipal());
2889 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2890 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2892 return NS_OK;
2895 NS_IMETHODIMP
2896 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
2897 NS_ENSURE_ARG_POINTER(aChild);
2899 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2900 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2902 nsresult rv = RemoveChildLoader(childAsDocLoader);
2903 NS_ENSURE_SUCCESS(rv, rv);
2905 aChild->SetTreeOwner(nullptr);
2907 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
2910 NS_IMETHODIMP
2911 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
2912 NS_ENSURE_ARG_POINTER(aChild);
2914 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
2915 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
2917 child.forget(aChild);
2919 return NS_OK;
2922 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
2923 #ifdef DEBUG
2924 if (aIndex < 0) {
2925 NS_WARNING("Negative index passed to GetChildAt");
2926 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
2927 NS_WARNING("Too large an index passed to GetChildAt");
2929 #endif
2931 nsIDocumentLoader* child = ChildAt(aIndex);
2933 // child may be nullptr here.
2934 return static_cast<nsDocShell*>(child);
2937 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
2938 nsISHEntry* aNewEntry,
2939 int32_t aChildOffset, uint32_t aLoadType,
2940 bool aCloneChildren) {
2941 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2942 nsresult rv = NS_OK;
2944 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
2945 /* You get here if you are currently building a
2946 * hierarchy ie.,you just visited a frameset page
2948 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
2949 rv = mLSHE->AddChild(aNewEntry, aChildOffset);
2951 } else if (!aCloneRef) {
2952 /* This is an initial load in some subframe. Just append it if we can */
2953 if (mOSHE) {
2954 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
2956 } else {
2957 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
2958 if (shistory) {
2959 rv = shistory->LegacySHistory()->AddChildSHEntryHelper(
2960 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
2963 return rv;
2966 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
2967 int32_t aChildOffset,
2968 bool aCloneChildren) {
2969 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2970 /* You will get here when you are in a subframe and
2971 * a new url has been loaded on you.
2972 * The mOSHE in this subframe will be the previous url's
2973 * mOSHE. This mOSHE will be used as the identification
2974 * for this subframe in the CloneAndReplace function.
2977 // In this case, we will end up calling AddEntry, which increases the
2978 // current index by 1
2979 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
2980 if (rootSH) {
2981 mPreviousEntryIndex = rootSH->Index();
2984 nsresult rv;
2985 // XXX(farre): this is not Fission safe, expect errors. This never
2986 // get's executed once session history in the parent is enabled.
2987 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
2988 NS_WARNING_ASSERTION(
2989 parent || !UseRemoteSubframes(),
2990 "Failed to add child session history entry! This will be resolved once "
2991 "session history in the parent is enabled.");
2992 if (parent) {
2993 rv = nsDocShell::Cast(parent)->AddChildSHEntry(
2994 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
2997 if (rootSH) {
2998 mLoadedEntryIndex = rootSH->Index();
3000 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
3001 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
3002 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
3003 mLoadedEntryIndex));
3007 return rv;
3010 NS_IMETHODIMP
3011 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3012 *aOSHE = false;
3013 *aEntry = nullptr;
3014 if (mLSHE) {
3015 NS_ADDREF(*aEntry = mLSHE);
3016 } else if (mOSHE) {
3017 NS_ADDREF(*aEntry = mOSHE);
3018 *aOSHE = true;
3020 return NS_OK;
3023 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
3024 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
3025 mBrowsingContext) {
3026 if (XRE_IsContentProcess()) {
3027 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
3028 if (contentChild) {
3029 contentChild->SendSynchronizeLayoutHistoryState(
3030 mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
3032 } else {
3033 SessionHistoryEntry* entry =
3034 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
3035 if (entry) {
3036 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
3039 if (mLoadingEntry &&
3040 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
3041 mLoadingEntry->mInfo.SetLayoutHistoryState(
3042 mActiveEntry->GetLayoutHistoryState());
3046 return NS_OK;
3049 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
3050 if (mLoadGroup) {
3051 mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
3052 } else {
3053 NS_WARNING(
3054 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3055 "propagate the mode to");
3059 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3060 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3061 return mScriptGlobal;
3064 Document* nsDocShell::GetDocument() {
3065 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), nullptr);
3066 return mDocumentViewer->GetDocument();
3069 Document* nsDocShell::GetExtantDocument() {
3070 return mDocumentViewer ? mDocumentViewer->GetDocument() : nullptr;
3073 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3074 if (NS_FAILED(EnsureScriptEnvironment())) {
3075 return nullptr;
3077 return mScriptGlobal;
3080 NS_IMETHODIMP
3081 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3082 NS_ENSURE_ARG_POINTER(aWindow);
3084 nsresult rv = EnsureScriptEnvironment();
3085 NS_ENSURE_SUCCESS(rv, rv);
3087 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3088 window.forget(aWindow);
3089 return NS_OK;
3092 NS_IMETHODIMP
3093 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3094 RefPtr<ContentFrameMessageManager> mm;
3095 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3096 mm = browserChild->GetMessageManager();
3097 } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3098 mm = win->GetMessageManager();
3100 mm.forget(aMessageManager);
3101 return NS_OK;
3104 NS_IMETHODIMP
3105 nsDocShell::GetIsNavigating(bool* aOut) {
3106 *aOut = mIsNavigating;
3107 return NS_OK;
3110 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3111 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3112 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3113 if (!rootSH || !aEntry) {
3114 return;
3117 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3120 //-------------------------------------
3121 //-- Helper Method for Print discovery
3122 //-------------------------------------
3123 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
3124 if (!mBrowsingContext->Top()->GetIsPrinting()) {
3125 return false;
3127 if (aDisplayErrorDialog) {
3128 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3130 return true;
3133 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3134 bool aCheckIfUnloadFired) {
3135 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
3136 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3137 if (!isAllowed) {
3138 return false;
3140 if (!mDocumentViewer) {
3141 return true;
3143 bool firingBeforeUnload;
3144 mDocumentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3145 return !firingBeforeUnload;
3148 //*****************************************************************************
3149 // nsDocShell::nsIWebNavigation
3150 //*****************************************************************************
3152 NS_IMETHODIMP
3153 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3154 *aCanGoBack = false;
3155 if (!IsNavigationAllowed(false)) {
3156 return NS_OK; // JS may not handle returning of an error code
3158 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3159 if (rootSH) {
3160 *aCanGoBack = rootSH->CanGo(-1);
3161 MOZ_LOG(gSHLog, LogLevel::Verbose,
3162 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
3164 return NS_OK;
3166 return NS_ERROR_FAILURE;
3169 NS_IMETHODIMP
3170 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3171 *aCanGoForward = false;
3172 if (!IsNavigationAllowed(false)) {
3173 return NS_OK; // JS may not handle returning of an error code
3175 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3176 if (rootSH) {
3177 *aCanGoForward = rootSH->CanGo(1);
3178 MOZ_LOG(gSHLog, LogLevel::Verbose,
3179 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
3180 return NS_OK;
3182 return NS_ERROR_FAILURE;
3185 NS_IMETHODIMP
3186 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
3187 if (!IsNavigationAllowed()) {
3188 return NS_OK; // JS may not handle returning of an error code
3191 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3192 mIsNavigating = true;
3194 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3195 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3196 ErrorResult rv;
3197 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
3198 return rv.StealNSResult();
3201 NS_IMETHODIMP
3202 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
3203 if (!IsNavigationAllowed()) {
3204 return NS_OK; // JS may not handle returning of an error code
3207 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3208 mIsNavigating = true;
3210 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3211 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3212 ErrorResult rv;
3213 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
3214 return rv.StealNSResult();
3217 // XXX(nika): We may want to stop exposing this API in the child process? Going
3218 // to a specific index from multiple different processes could definitely race.
3219 NS_IMETHODIMP
3220 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
3221 if (!IsNavigationAllowed()) {
3222 return NS_OK; // JS may not handle returning of an error code
3225 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3226 mIsNavigating = true;
3228 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3229 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3231 ErrorResult rv;
3232 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
3233 rv);
3234 return rv.StealNSResult();
3237 nsresult nsDocShell::LoadURI(nsIURI* aURI,
3238 const LoadURIOptions& aLoadURIOptions) {
3239 if (!IsNavigationAllowed()) {
3240 return NS_OK; // JS may not handle returning of an error code
3242 RefPtr<nsDocShellLoadState> loadState;
3243 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3244 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3245 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
3246 if (NS_FAILED(rv) || !loadState) {
3247 return NS_ERROR_FAILURE;
3250 return LoadURI(loadState, true);
3253 NS_IMETHODIMP
3254 nsDocShell::LoadURIFromScript(nsIURI* aURI,
3255 JS::Handle<JS::Value> aLoadURIOptions,
3256 JSContext* aCx) {
3257 // generate dictionary for aLoadURIOptions and forward call
3258 LoadURIOptions loadURIOptions;
3259 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3260 return NS_ERROR_INVALID_ARG;
3262 return LoadURI(aURI, loadURIOptions);
3265 nsresult nsDocShell::FixupAndLoadURIString(
3266 const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) {
3267 if (!IsNavigationAllowed()) {
3268 return NS_OK; // JS may not handle returning of an error code
3271 RefPtr<nsDocShellLoadState> loadState;
3272 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3273 mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState));
3275 uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3276 if (NS_ERROR_MALFORMED_URI == rv) {
3277 MOZ_LOG(gSHLog, LogLevel::Debug,
3278 ("Creating an active entry on nsDocShell %p to %s (because "
3279 "we're showing an error page)",
3280 this, NS_ConvertUTF16toUTF8(aURIString).get()));
3282 // We need to store a session history entry. We don't have a valid URI, so
3283 // we use about:blank instead.
3284 nsCOMPtr<nsIURI> uri;
3285 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
3286 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
3287 if (aLoadURIOptions.mTriggeringPrincipal) {
3288 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
3289 } else {
3290 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
3292 if (mozilla::SessionHistoryInParent()) {
3293 mActiveEntry = MakeUnique<SessionHistoryInfo>(
3294 uri, triggeringPrincipal, nullptr, nullptr, nullptr,
3295 nsLiteralCString("text/html"));
3296 mBrowsingContext->SetActiveSessionHistoryEntry(
3297 Nothing(), mActiveEntry.get(), MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
3298 /* aUpdatedCacheKey = */ 0);
3300 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(),
3301 nullptr) &&
3302 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3303 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3307 if (NS_FAILED(rv) || !loadState) {
3308 return NS_ERROR_FAILURE;
3311 return LoadURI(loadState, true);
3314 NS_IMETHODIMP
3315 nsDocShell::FixupAndLoadURIStringFromScript(
3316 const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions,
3317 JSContext* aCx) {
3318 // generate dictionary for aLoadURIOptions and forward call
3319 LoadURIOptions loadURIOptions;
3320 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3321 return NS_ERROR_INVALID_ARG;
3323 return FixupAndLoadURIString(aURIString, loadURIOptions);
3326 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3327 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3328 // such as the content-chrome boundary, don't fire the error event.
3329 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3330 return;
3333 // If embedder is same-process, then unblocking the load event is already
3334 // handled by nsDocLoader. Fire the error event on our embedder element if
3335 // requested.
3337 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3338 // more like the remote case when in-process.
3339 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3340 if (element) {
3341 if (aFireFrameErrorEvent) {
3342 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3343 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3344 fl->FireErrorEvent();
3348 return;
3351 // If we have a cross-process parent document, we must notify it that we no
3352 // longer block its load event. This is necessary for OOP sub-documents
3353 // because error documents do not result in a call to
3354 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3355 // (Obviously, we must do this before any of the returns below.)
3356 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3357 if (browserChild &&
3358 !mBrowsingContext->GetParentWindowContext()->IsInProcess()) {
3359 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3360 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3361 : EmbedderElementEventType::NoEvent);
3365 NS_IMETHODIMP
3366 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3367 const char16_t* aURL, nsIChannel* aFailedChannel,
3368 bool* aDisplayedErrorPage) {
3369 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3370 ("DOCSHELL %p DisplayLoadError %s\n", this,
3371 aURI ? aURI->GetSpecOrDefault().get() : ""));
3373 *aDisplayedErrorPage = false;
3374 // Get prompt and string bundle services
3375 nsCOMPtr<nsIPrompt> prompter;
3376 nsCOMPtr<nsIStringBundle> stringBundle;
3377 GetPromptAndStringBundle(getter_AddRefs(prompter),
3378 getter_AddRefs(stringBundle));
3380 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3381 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3383 const char* error = nullptr;
3384 // The key used to select the appropriate error message from the properties
3385 // file.
3386 const char* errorDescriptionID = nullptr;
3387 AutoTArray<nsString, 3> formatStrs;
3388 bool addHostPort = false;
3389 bool isBadStsCertError = false;
3390 nsresult rv = NS_OK;
3391 nsAutoString messageStr;
3392 nsAutoCString cssClass;
3393 nsAutoCString errorPage;
3395 errorPage.AssignLiteral("neterror");
3397 // Turn the error code into a human readable error message.
3398 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3399 NS_ENSURE_ARG_POINTER(aURI);
3401 // Extract the schemes into a comma delimited list.
3402 nsAutoCString scheme;
3403 aURI->GetScheme(scheme);
3404 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3405 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3406 while (nestedURI) {
3407 nsCOMPtr<nsIURI> tempURI;
3408 nsresult rv2;
3409 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3410 if (NS_SUCCEEDED(rv2) && tempURI) {
3411 tempURI->GetScheme(scheme);
3412 formatStrs[0].AppendLiteral(", ");
3413 AppendASCIItoUTF16(scheme, formatStrs[0]);
3415 nestedURI = do_QueryInterface(tempURI);
3417 error = "unknownProtocolFound";
3418 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3419 NS_ENSURE_ARG_POINTER(aURI);
3420 error = "fileNotFound";
3421 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3422 NS_ENSURE_ARG_POINTER(aURI);
3423 error = "fileAccessDenied";
3424 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3425 NS_ENSURE_ARG_POINTER(aURI);
3426 // Get the host
3427 nsAutoCString host;
3428 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3429 innermostURI->GetHost(host);
3430 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3431 errorDescriptionID = "dnsNotFound2";
3432 error = "dnsNotFound";
3433 } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3434 NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3435 NS_ENSURE_ARG_POINTER(aURI);
3436 addHostPort = true;
3437 error = "connectionFailure";
3438 } else if (NS_ERROR_NET_INTERRUPT == aError) {
3439 NS_ENSURE_ARG_POINTER(aURI);
3440 addHostPort = true;
3441 error = "netInterrupt";
3442 } else if (NS_ERROR_NET_TIMEOUT == aError ||
3443 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
3444 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
3445 NS_ENSURE_ARG_POINTER(aURI);
3446 // Get the host
3447 nsAutoCString host;
3448 aURI->GetHost(host);
3449 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3450 error = "netTimeout";
3451 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3452 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
3453 // CSP error
3454 cssClass.AssignLiteral("neterror");
3455 error = "cspBlocked";
3456 } else if (NS_ERROR_XFO_VIOLATION == aError) {
3457 // XFO error
3458 cssClass.AssignLiteral("neterror");
3459 error = "xfoBlocked";
3460 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3461 nsCOMPtr<nsINSSErrorsService> nsserr =
3462 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3464 uint32_t errorClass;
3465 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3466 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3469 nsCOMPtr<nsITransportSecurityInfo> tsi;
3470 if (aFailedChannel) {
3471 aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
3473 if (tsi) {
3474 uint32_t securityState;
3475 tsi->GetSecurityState(&securityState);
3476 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3477 error = "sslv3Used";
3478 addHostPort = true;
3479 } else if (securityState &
3480 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3481 error = "weakCryptoUsed";
3482 addHostPort = true;
3484 } else {
3485 // No channel, let's obtain the generic error message
3486 if (nsserr) {
3487 nsserr->GetErrorMessage(aError, messageStr);
3490 // We don't have a message string here anymore but DisplayLoadError
3491 // requires a non-empty messageStr.
3492 messageStr.Truncate();
3493 messageStr.AssignLiteral(u" ");
3494 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3495 error = "nssBadCert";
3497 // If this is an HTTP Strict Transport Security host or a pinned host
3498 // and the certificate is bad, don't allow overrides (RFC 6797 section
3499 // 12.1).
3500 bool isStsHost = false;
3501 bool isPinnedHost = false;
3502 OriginAttributes attrsForHSTS;
3503 if (aFailedChannel) {
3504 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
3505 attrsForHSTS);
3506 } else {
3507 attrsForHSTS = GetOriginAttributes();
3510 if (XRE_IsParentProcess()) {
3511 nsCOMPtr<nsISiteSecurityService> sss =
3512 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3513 NS_ENSURE_SUCCESS(rv, rv);
3514 rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost);
3515 NS_ENSURE_SUCCESS(rv, rv);
3516 } else {
3517 mozilla::dom::ContentChild* cc =
3518 mozilla::dom::ContentChild::GetSingleton();
3519 cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost);
3521 nsCOMPtr<nsIPublicKeyPinningService> pkps =
3522 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
3523 NS_ENSURE_SUCCESS(rv, rv);
3524 rv = pkps->HostHasPins(aURI, &isPinnedHost);
3526 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3527 false)) {
3528 cssClass.AssignLiteral("expertBadCert");
3531 // HSTS/pinning takes precedence over the expert bad cert pref. We
3532 // never want to show the "Add Exception" button for these sites.
3533 // In the future we should differentiate between an HSTS host and a
3534 // pinned host and display a more informative message to the user.
3535 if (isStsHost || isPinnedHost) {
3536 isBadStsCertError = true;
3537 cssClass.AssignLiteral("badStsCert");
3540 errorPage.Assign("certerror");
3541 } else {
3542 error = "nssFailure2";
3544 } else if (NS_ERROR_PHISHING_URI == aError ||
3545 NS_ERROR_MALWARE_URI == aError ||
3546 NS_ERROR_UNWANTED_URI == aError ||
3547 NS_ERROR_HARMFUL_URI == aError) {
3548 nsAutoCString host;
3549 aURI->GetHost(host);
3550 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3552 // Malware and phishing detectors may want to use an alternate error
3553 // page, but if the pref's not set, we'll fall back on the standard page
3554 nsAutoCString alternateErrorPage;
3555 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3556 alternateErrorPage);
3557 if (NS_SUCCEEDED(rv)) {
3558 errorPage.Assign(alternateErrorPage);
3561 if (NS_ERROR_PHISHING_URI == aError) {
3562 error = "deceptiveBlocked";
3563 } else if (NS_ERROR_MALWARE_URI == aError) {
3564 error = "malwareBlocked";
3565 } else if (NS_ERROR_UNWANTED_URI == aError) {
3566 error = "unwantedBlocked";
3567 } else if (NS_ERROR_HARMFUL_URI == aError) {
3568 error = "harmfulBlocked";
3571 cssClass.AssignLiteral("blacklist");
3572 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3573 errorPage.AssignLiteral("tabcrashed");
3574 error = "tabcrashed";
3576 RefPtr<EventTarget> handler = mChromeEventHandler;
3577 if (handler) {
3578 nsCOMPtr<Element> element = do_QueryInterface(handler);
3579 element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
3582 // DisplayLoadError requires a non-empty messageStr to proceed and call
3583 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3584 // space which will be trimmed and thus treated as empty by the front-end.
3585 if (messageStr.IsEmpty()) {
3586 messageStr.AssignLiteral(u" ");
3588 } else if (NS_ERROR_FRAME_CRASHED == aError) {
3589 errorPage.AssignLiteral("framecrashed");
3590 error = "framecrashed";
3591 messageStr.AssignLiteral(u" ");
3592 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3593 errorPage.AssignLiteral("restartrequired");
3594 error = "restartrequired";
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 {
3603 // Errors requiring simple formatting
3604 switch (aError) {
3605 case NS_ERROR_MALFORMED_URI:
3606 // URI is malformed
3607 error = "malformedURI";
3608 errorDescriptionID = "malformedURI2";
3609 break;
3610 case NS_ERROR_REDIRECT_LOOP:
3611 // Doc failed to load because the server generated too many redirects
3612 error = "redirectLoop";
3613 break;
3614 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3615 // Doc failed to load because PSM is not installed
3616 error = "unknownSocketType";
3617 break;
3618 case NS_ERROR_NET_RESET:
3619 // Doc failed to load because the server kept reseting the connection
3620 // before we could read any data from it
3621 error = "netReset";
3622 break;
3623 case NS_ERROR_DOCUMENT_NOT_CACHED:
3624 // Doc failed to load because the cache does not contain a copy of
3625 // the document.
3626 error = "notCached";
3627 break;
3628 case NS_ERROR_OFFLINE:
3629 // Doc failed to load because we are offline.
3630 error = "netOffline";
3631 break;
3632 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3633 // Doc navigation attempted while Printing or Print Preview
3634 error = "isprinting";
3635 break;
3636 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3637 // Port blocked for security reasons
3638 addHostPort = true;
3639 error = "deniedPortAccess";
3640 break;
3641 case NS_ERROR_UNKNOWN_PROXY_HOST:
3642 // Proxy hostname could not be resolved.
3643 error = "proxyResolveFailure";
3644 break;
3645 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3646 case NS_ERROR_PROXY_FORBIDDEN:
3647 case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3648 case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3649 case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3650 // Proxy connection was refused.
3651 error = "proxyConnectFailure";
3652 break;
3653 case NS_ERROR_INVALID_CONTENT_ENCODING:
3654 // Bad Content Encoding.
3655 error = "contentEncodingError";
3656 break;
3657 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3658 // Channel refused to load from an unrecognized content type.
3659 error = "unsafeContentType";
3660 break;
3661 case NS_ERROR_CORRUPTED_CONTENT:
3662 // Broken Content Detected. e.g. Content-MD5 check failure.
3663 error = "corruptedContentErrorv2";
3664 break;
3665 case NS_ERROR_INTERCEPTION_FAILED:
3666 // ServiceWorker intercepted request, but something went wrong.
3667 error = "corruptedContentErrorv2";
3668 break;
3669 case NS_ERROR_NET_INADEQUATE_SECURITY:
3670 // Server negotiated bad TLS for HTTP/2.
3671 error = "inadequateSecurityError";
3672 addHostPort = true;
3673 break;
3674 case NS_ERROR_BLOCKED_BY_POLICY:
3675 case NS_ERROR_DOM_COOP_FAILED:
3676 case NS_ERROR_DOM_COEP_FAILED:
3677 // Page blocked by policy
3678 error = "blockedByPolicy";
3679 break;
3680 case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3681 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3682 // HTTP/2 or HTTP/3 stack detected a protocol error
3683 error = "networkProtocolError";
3684 break;
3686 default:
3687 break;
3691 nsresult delegateErrorCode = aError;
3692 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3693 // caused this error, we replace the error-page with about:httpsonlyerror
3694 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
3695 errorPage.AssignLiteral("httpsonlyerror");
3696 delegateErrorCode = NS_ERROR_HTTPS_ONLY;
3697 } else if (isBadStsCertError) {
3698 delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
3701 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3702 nsCOMPtr<nsIURI> errorPageURI;
3703 rv = loadURIDelegate->HandleLoadError(
3704 aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
3705 getter_AddRefs(errorPageURI));
3706 // If the docshell is going away there's no point in showing an error page.
3707 if (NS_FAILED(rv) || mIsBeingDestroyed) {
3708 *aDisplayedErrorPage = false;
3709 return NS_OK;
3712 if (errorPageURI) {
3713 *aDisplayedErrorPage =
3714 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3715 return NS_OK;
3719 // Test if the error should be displayed
3720 if (!error) {
3721 return NS_OK;
3724 if (!errorDescriptionID) {
3725 errorDescriptionID = error;
3728 Telemetry::AccumulateCategoricalKeyed(
3729 IsSubframe() ? "frame"_ns : "top"_ns,
3730 mozilla::dom::LoadErrorToTelemetryLabel(aError));
3732 // Test if the error needs to be formatted
3733 if (!messageStr.IsEmpty()) {
3734 // already obtained message
3735 } else {
3736 if (addHostPort) {
3737 // Build up the host:port string.
3738 nsAutoCString hostport;
3739 if (aURI) {
3740 aURI->GetHostPort(hostport);
3741 } else {
3742 hostport.Assign('?');
3744 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3747 nsAutoCString spec;
3748 rv = NS_ERROR_NOT_AVAILABLE;
3749 auto& nextFormatStr = *formatStrs.AppendElement();
3750 if (aURI) {
3751 // displaying "file://" is aesthetically unpleasing and could even be
3752 // confusing to the user
3753 if (SchemeIsFile(aURI)) {
3754 aURI->GetPathQueryRef(spec);
3755 } else {
3756 aURI->GetSpec(spec);
3759 nsCOMPtr<nsITextToSubURI> textToSubURI(
3760 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3761 if (NS_SUCCEEDED(rv)) {
3762 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3764 } else {
3765 spec.Assign('?');
3767 if (NS_FAILED(rv)) {
3768 CopyUTF8toUTF16(spec, nextFormatStr);
3770 rv = NS_OK;
3772 nsAutoString str;
3773 rv =
3774 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3775 NS_ENSURE_SUCCESS(rv, rv);
3776 messageStr.Assign(str);
3779 // Display the error as a page or an alert prompt
3780 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3782 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3783 SchemeIsHTTPS(aURI)) {
3784 // Maybe TLS intolerant. Treat this as an SSL error.
3785 error = "nssFailure2";
3788 if (mBrowsingContext->GetUseErrorPages()) {
3789 // Display an error page
3790 nsresult loadedPage =
3791 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3792 cssClass.get(), aFailedChannel);
3793 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3794 } else {
3795 // The prompter reqires that our private window has a document (or it
3796 // asserts). Satisfy that assertion now since GetDoc will force
3797 // creation of one if it hasn't already been created.
3798 if (mScriptGlobal) {
3799 Unused << mScriptGlobal->GetDoc();
3802 // Display a message box
3803 prompter->Alert(nullptr, messageStr.get());
3806 return NS_OK;
3809 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3811 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3812 const char* aErrorPage,
3813 const char* aErrorType,
3814 const char16_t* aDescription,
3815 const char* aCSSClass,
3816 nsIChannel* aFailedChannel) {
3817 if (mIsBeingDestroyed) {
3818 return NS_ERROR_NOT_AVAILABLE;
3821 #if defined(DEBUG)
3822 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3823 nsAutoCString chanName;
3824 if (aFailedChannel) {
3825 aFailedChannel->GetName(chanName);
3826 } else {
3827 chanName.AssignLiteral("<no channel>");
3830 MOZ_LOG(gDocShellLog, LogLevel::Debug,
3831 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3832 this, aURI ? aURI->GetSpecOrDefault().get() : "",
3833 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3835 #endif
3837 nsAutoCString url;
3838 if (aURI) {
3839 nsresult rv = aURI->GetSpec(url);
3840 NS_ENSURE_SUCCESS(rv, rv);
3841 } else if (aURL) {
3842 CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3843 } else {
3844 return NS_ERROR_INVALID_POINTER;
3847 // Create a URL to pass all the error information through to the page.
3849 #undef SAFE_ESCAPE
3850 #define SAFE_ESCAPE(output, input, params) \
3851 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3852 return NS_ERROR_OUT_OF_MEMORY; \
3855 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3856 SAFE_ESCAPE(escapedUrl, url, url_Path);
3857 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3858 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3859 url_Path);
3860 if (aCSSClass) {
3861 nsCString cssClass(aCSSClass);
3862 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3864 nsCString errorPageUrl("about:");
3865 errorPageUrl.AppendASCII(aErrorPage);
3866 errorPageUrl.AppendLiteral("?e=");
3868 errorPageUrl.AppendASCII(escapedError.get());
3869 errorPageUrl.AppendLiteral("&u=");
3870 errorPageUrl.AppendASCII(escapedUrl.get());
3871 if ((strcmp(aErrorPage, "blocked") == 0) &&
3872 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
3873 errorPageUrl.AppendLiteral("&o=1");
3875 if (!escapedCSSClass.IsEmpty()) {
3876 errorPageUrl.AppendLiteral("&s=");
3877 errorPageUrl.AppendASCII(escapedCSSClass.get());
3879 errorPageUrl.AppendLiteral("&c=UTF-8");
3881 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
3882 int32_t cpsState;
3883 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
3884 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
3885 errorPageUrl.AppendLiteral("&captive=true");
3888 errorPageUrl.AppendLiteral("&d=");
3889 errorPageUrl.AppendASCII(escapedDescription.get());
3891 nsCOMPtr<nsIURI> errorPageURI;
3892 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3893 NS_ENSURE_SUCCESS(rv, rv);
3895 return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
3898 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
3899 nsIChannel* aFailedChannel) {
3900 mFailedChannel = aFailedChannel;
3901 mFailedURI = aFailedURI;
3902 mFailedLoadType = mLoadType;
3904 if (mLSHE) {
3905 // Abandon mLSHE's BFCache entry and create a new one. This way, if
3906 // we go back or forward to another SHEntry with the same doc
3907 // identifier, the error page won't persist.
3908 mLSHE->AbandonBFCacheEntry();
3911 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
3912 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
3913 if (mBrowsingContext) {
3914 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
3915 loadState->SetTriggeringWindowId(
3916 mBrowsingContext->GetCurrentInnerWindowId());
3917 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
3918 if (innerWin) {
3919 loadState->SetTriggeringStorageAccess(innerWin->UsingStorageAccess());
3922 loadState->SetLoadType(LOAD_ERROR_PAGE);
3923 loadState->SetFirstParty(true);
3924 loadState->SetSourceBrowsingContext(mBrowsingContext);
3925 if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
3926 // We keep the loading entry for the load that failed here. If the user
3927 // reloads we want to try to reload the original load, not the error page.
3928 loadState->SetLoadingSessionHistoryInfo(
3929 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
3931 return InternalLoad(loadState);
3934 NS_IMETHODIMP
3935 nsDocShell::Reload(uint32_t aReloadFlags) {
3936 if (!IsNavigationAllowed()) {
3937 return NS_OK; // JS may not handle returning of an error code
3940 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
3941 "Reload command not updated to use load flags!");
3942 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
3943 "Don't pass these flags to Reload");
3945 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3946 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3948 // Send notifications to the HistoryListener if any, about the impending
3949 // reload
3950 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3951 if (mozilla::SessionHistoryInParent()) {
3952 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
3953 bool forceReload = IsForceReloadType(loadType);
3954 if (!XRE_IsParentProcess()) {
3955 ++mPendingReloadCount;
3956 RefPtr<nsDocShell> docShell(this);
3957 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
3958 NS_ENSURE_STATE(viewer);
3960 bool okToUnload = true;
3961 MOZ_TRY(viewer->PermitUnload(&okToUnload));
3962 if (!okToUnload) {
3963 return NS_OK;
3966 RefPtr<Document> doc(GetDocument());
3967 RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
3968 nsCOMPtr<nsIURI> currentURI(mCurrentURI);
3969 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
3970 RefPtr<StopDetector> stopDetector = new StopDetector();
3971 nsCOMPtr<nsILoadGroup> loadGroup;
3972 GetLoadGroup(getter_AddRefs(loadGroup));
3973 if (loadGroup) {
3974 // loadGroup may be null in theory. In that case stopDetector just
3975 // doesn't do anything.
3976 loadGroup->AddRequest(stopDetector, nullptr);
3979 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
3980 mBrowsingContext, forceReload,
3981 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo,
3982 loadGroup, stopDetector](
3983 std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>,
3984 Maybe<bool>>&& aResult) {
3985 auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() {
3986 if (loadGroup) {
3987 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
3991 // Decrease mPendingReloadCount before any other early returns!
3992 if (--(docShell->mPendingReloadCount) > 0) {
3993 return;
3996 if (stopDetector->Canceled()) {
3997 return;
3999 bool canReload;
4000 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4001 Maybe<bool> reloadingActiveEntry;
4003 std::tie(canReload, loadState, reloadingActiveEntry) = aResult;
4005 if (!canReload) {
4006 return;
4009 if (loadState.isSome()) {
4010 MOZ_LOG(
4011 gSHLog, LogLevel::Debug,
4012 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
4013 loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
4014 docShell->LoadHistoryEntry(loadState.ref(), loadType,
4015 reloadingActiveEntry.ref());
4016 } else {
4017 MOZ_LOG(gSHLog, LogLevel::Debug,
4018 ("nsDocShell %p ReloadDocument", docShell.get()));
4019 ReloadDocument(docShell, doc, loadType, browsingContext,
4020 currentURI, referrerInfo,
4021 /* aNotifiedBeforeUnloadListeners */ true);
4024 [](mozilla::ipc::ResponseRejectReason) {});
4025 } else {
4026 // Parent process
4027 bool canReload = false;
4028 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4029 Maybe<bool> reloadingActiveEntry;
4030 if (!mBrowsingContext->IsDiscarded()) {
4031 mBrowsingContext->Canonical()->NotifyOnHistoryReload(
4032 forceReload, canReload, loadState, reloadingActiveEntry);
4034 if (canReload) {
4035 if (loadState.isSome()) {
4036 MOZ_LOG(gSHLog, LogLevel::Debug,
4037 ("nsDocShell %p Reload - LoadHistoryEntry", this));
4038 LoadHistoryEntry(loadState.ref(), loadType,
4039 reloadingActiveEntry.ref());
4040 } else {
4041 MOZ_LOG(gSHLog, LogLevel::Debug,
4042 ("nsDocShell %p ReloadDocument", this));
4043 RefPtr<Document> doc = GetDocument();
4044 RefPtr<BrowsingContext> bc = mBrowsingContext;
4045 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4046 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4047 ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4051 return NS_OK;
4054 bool canReload = true;
4055 if (rootSH) {
4056 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
4059 if (!canReload) {
4060 return NS_OK;
4063 /* If you change this part of code, make sure bug 45297 does not re-occur */
4064 if (mOSHE) {
4065 nsCOMPtr<nsISHEntry> oshe = mOSHE;
4066 return LoadHistoryEntry(
4067 oshe, loadType,
4068 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4071 if (mLSHE) { // In case a reload happened before the current load is done
4072 nsCOMPtr<nsISHEntry> lshe = mLSHE;
4073 return LoadHistoryEntry(
4074 lshe, loadType,
4075 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4078 RefPtr<Document> doc = GetDocument();
4079 RefPtr<BrowsingContext> bc = mBrowsingContext;
4080 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4081 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4082 return ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4085 /* static */
4086 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
4087 uint32_t aLoadType,
4088 BrowsingContext* aBrowsingContext,
4089 nsIURI* aCurrentURI,
4090 nsIReferrerInfo* aReferrerInfo,
4091 bool aNotifiedBeforeUnloadListeners) {
4092 if (!aDocument) {
4093 return NS_OK;
4096 // Do not inherit owner from document
4097 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4098 nsAutoString srcdoc;
4099 nsIURI* baseURI = nullptr;
4100 nsCOMPtr<nsIURI> originalURI;
4101 nsCOMPtr<nsIURI> resultPrincipalURI;
4102 bool loadReplace = false;
4104 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
4105 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
4106 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
4107 uint64_t triggeringWindowId = aDocument->InnerWindowID();
4108 bool triggeringStorageAccess = aDocument->UsingStorageAccess();
4110 nsAutoString contentTypeHint;
4111 aDocument->GetContentType(contentTypeHint);
4113 if (aDocument->IsSrcdocDocument()) {
4114 aDocument->GetSrcdocData(srcdoc);
4115 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4116 baseURI = aDocument->GetBaseURI();
4117 } else {
4118 srcdoc = VoidString();
4120 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
4121 if (chan) {
4122 uint32_t loadFlags;
4123 chan->GetLoadFlags(&loadFlags);
4124 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4125 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4126 if (httpChan) {
4127 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4130 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
4131 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4134 if (!triggeringPrincipal) {
4135 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4136 return NS_ERROR_FAILURE;
4139 // Stack variables to ensure changes to the member variables don't affect to
4140 // the call.
4141 nsCOMPtr<nsIURI> currentURI = aCurrentURI;
4143 // Reload always rewrites result principal URI.
4144 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4145 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4147 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
4148 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
4149 loadState->SetReferrerInfo(aReferrerInfo);
4150 loadState->SetOriginalURI(originalURI);
4151 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
4152 loadState->SetLoadReplace(loadReplace);
4153 loadState->SetTriggeringPrincipal(triggeringPrincipal);
4154 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
4155 loadState->SetTriggeringWindowId(triggeringWindowId);
4156 loadState->SetTriggeringStorageAccess(triggeringStorageAccess);
4157 loadState->SetPrincipalToInherit(triggeringPrincipal);
4158 loadState->SetCsp(csp);
4159 loadState->SetInternalLoadFlags(flags);
4160 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
4161 loadState->SetLoadType(aLoadType);
4162 loadState->SetFirstParty(true);
4163 loadState->SetSrcdocData(srcdoc);
4164 loadState->SetSourceBrowsingContext(aBrowsingContext);
4165 loadState->SetBaseURI(baseURI);
4166 loadState->SetHasValidUserGestureActivation(
4167 context && context->HasValidTransientUserGestureActivation());
4168 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
4169 return aDocShell->InternalLoad(loadState);
4172 NS_IMETHODIMP
4173 nsDocShell::Stop(uint32_t aStopFlags) {
4174 // Revoke any pending event related to content viewer restoration
4175 mRestorePresentationEvent.Revoke();
4177 if (mLoadType == LOAD_ERROR_PAGE) {
4178 if (mLSHE) {
4179 // Since error page loads never unset mLSHE, do so now
4180 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
4182 mActiveEntryIsLoadingFromSessionHistory = false;
4184 mFailedChannel = nullptr;
4185 mFailedURI = nullptr;
4188 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4189 // Stop the document loading and animations
4190 if (mDocumentViewer) {
4191 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4192 viewer->Stop();
4194 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4195 // Stop the document loading only
4196 if (mDocumentViewer) {
4197 RefPtr<Document> doc = mDocumentViewer->GetDocument();
4198 if (doc) {
4199 doc->StopDocumentLoad();
4204 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4205 // Suspend any timers that were set for this loader. We'll clear
4206 // them out for good in CreateDocumentViewer.
4207 if (mRefreshURIList) {
4208 SuspendRefreshURIs();
4209 mSavedRefreshURIList.swap(mRefreshURIList);
4210 mRefreshURIList = nullptr;
4213 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4214 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4215 // redundant apis!
4216 Stop();
4218 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4219 // BFCache now, and the Stop above will have removed the DocumentChannel
4220 // from the loadgroup.
4221 mChannelToDisconnectOnPageHide = 0;
4224 for (auto* child : mChildList.ForwardRange()) {
4225 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
4226 if (shellAsNav) {
4227 shellAsNav->Stop(aStopFlags);
4231 return NS_OK;
4234 NS_IMETHODIMP
4235 nsDocShell::GetDocument(Document** aDocument) {
4236 NS_ENSURE_ARG_POINTER(aDocument);
4237 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), NS_ERROR_FAILURE);
4239 RefPtr<Document> doc = mDocumentViewer->GetDocument();
4240 if (!doc) {
4241 return NS_ERROR_NOT_AVAILABLE;
4244 doc.forget(aDocument);
4245 return NS_OK;
4248 NS_IMETHODIMP
4249 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4250 NS_ENSURE_ARG_POINTER(aURI);
4252 nsCOMPtr<nsIURI> uri = mCurrentURI;
4253 uri.forget(aURI);
4254 return NS_OK;
4257 NS_IMETHODIMP
4258 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4259 NS_ENSURE_ARG_POINTER(aSessionHistory);
4260 RefPtr<ChildSHistory> shistory = GetSessionHistory();
4261 shistory.forget(aSessionHistory);
4262 return NS_OK;
4265 //*****************************************************************************
4266 // nsDocShell::nsIWebPageDescriptor
4267 //*****************************************************************************
4269 NS_IMETHODIMP
4270 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
4271 const nsAString& aURI) {
4272 if (!aOtherDocShell) {
4273 return NS_ERROR_INVALID_POINTER;
4275 nsCOMPtr<nsIURI> newURI;
4276 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
4277 if (NS_FAILED(rv)) {
4278 return rv;
4281 RefPtr<nsDocShellLoadState> loadState;
4282 uint32_t cacheKey;
4283 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
4284 if (mozilla::SessionHistoryInParent()) {
4285 loadState = new nsDocShellLoadState(newURI);
4286 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
4287 return NS_ERROR_INVALID_POINTER;
4289 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
4290 } else {
4291 nsCOMPtr<nsISHEntry> entry;
4292 bool isOriginalSHE;
4293 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
4294 if (!entry) {
4295 return NS_ERROR_INVALID_POINTER;
4297 rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
4298 NS_ENSURE_SUCCESS(rv, rv);
4299 entry->GetCacheKey(&cacheKey);
4300 loadState->SetURI(newURI);
4301 loadState->SetSHEntry(nullptr);
4304 // We're doing a load of the page, via an API that
4305 // is only exposed to system code. The triggering principal for this load
4306 // should be the system principal.
4307 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4308 loadState->SetOriginalURI(nullptr);
4309 loadState->SetResultPrincipalURI(nullptr);
4311 return InternalLoad(loadState, Some(cacheKey));
4314 NS_IMETHODIMP
4315 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4316 MOZ_ASSERT(aPageDescriptor, "Null out param?");
4318 *aPageDescriptor = nullptr;
4320 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4321 if (src) {
4322 nsCOMPtr<nsISHEntry> dest;
4324 nsresult rv = src->Clone(getter_AddRefs(dest));
4325 if (NS_FAILED(rv)) {
4326 return rv;
4329 // null out inappropriate cloned attributes...
4330 dest->SetParent(nullptr);
4331 dest->SetIsSubFrame(false);
4333 return CallQueryInterface(dest, aPageDescriptor);
4336 return NS_ERROR_NOT_AVAILABLE;
4339 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
4340 const {
4341 nsCOMPtr<nsIInputStream> postData;
4342 if (mozilla::SessionHistoryInParent()) {
4343 if (mActiveEntry) {
4344 postData = mActiveEntry->GetPostData();
4345 } else if (mLoadingEntry) {
4346 postData = mLoadingEntry->mInfo.GetPostData();
4348 } else {
4349 if (mOSHE) {
4350 postData = mOSHE->GetPostData();
4351 } else if (mLSHE) {
4352 postData = mLSHE->GetPostData();
4356 return postData.forget();
4359 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4360 if (mozilla::SessionHistoryInParent()) {
4361 if (mActiveEntry) {
4362 return Some(mActiveEntry->GetCacheKey());
4365 if (mLoadingEntry) {
4366 return Some(mLoadingEntry->mInfo.GetCacheKey());
4368 } else {
4369 if (mOSHE) {
4370 return Some(mOSHE->GetCacheKey());
4373 if (mLSHE) {
4374 return Some(mLSHE->GetCacheKey());
4378 return Nothing();
4381 bool nsDocShell::FillLoadStateFromCurrentEntry(
4382 nsDocShellLoadState& aLoadState) {
4383 if (mLoadingEntry) {
4384 mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
4385 return true;
4387 if (mActiveEntry) {
4388 mActiveEntry->FillLoadInfo(aLoadState);
4389 return true;
4391 return false;
4394 //*****************************************************************************
4395 // nsDocShell::nsIBaseWindow
4396 //*****************************************************************************
4398 NS_IMETHODIMP
4399 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4400 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4401 int32_t aWidth, int32_t aHeight) {
4402 SetParentWidget(aParentWidget);
4403 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4404 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE);
4406 return NS_OK;
4409 NS_IMETHODIMP
4410 nsDocShell::Destroy() {
4411 // XXX: We allow this function to be called just once. If you are going to
4412 // reset new variables in this function, please make sure the variables will
4413 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4414 // in the setter functions for the variables would be enough.
4415 if (mIsBeingDestroyed) {
4416 return NS_ERROR_DOCSHELL_DYING;
4419 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4420 "Unexpected item type in docshell");
4422 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4423 if (serv) {
4424 const char* msg = mItemType == typeContent
4425 ? NS_WEBNAVIGATION_DESTROY
4426 : NS_CHROME_WEBNAVIGATION_DESTROY;
4427 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4430 mIsBeingDestroyed = true;
4432 // Brak the cycle with the initial client, if present.
4433 mInitialClientSource.reset();
4435 // Make sure to blow away our mLoadingURI just in case. No loads
4436 // from inside this pagehide.
4437 mLoadingURI = nullptr;
4439 // Fire unload event before we blow anything away.
4440 (void)FirePageHideNotification(true);
4442 // Clear pointers to any detached nsEditorData that's lying
4443 // around in shistory entries. Breaks cycle. See bug 430921.
4444 if (mOSHE) {
4445 mOSHE->SetEditorData(nullptr);
4447 if (mLSHE) {
4448 mLSHE->SetEditorData(nullptr);
4451 // Note: mContentListener can be null if Init() failed and we're being
4452 // called from the destructor.
4453 if (mContentListener) {
4454 mContentListener->DropDocShellReference();
4455 mContentListener->SetParentContentListener(nullptr);
4456 // Note that we do NOT set mContentListener to null here; that
4457 // way if someone tries to do a load in us after this point
4458 // the nsDSURIContentListener will block it. All of which
4459 // means that we should do this before calling Stop(), of
4460 // course.
4463 // Stop any URLs that are currently being loaded...
4464 Stop(nsIWebNavigation::STOP_ALL);
4466 mEditorData = nullptr;
4468 // Save the state of the current document, before destroying the window.
4469 // This is needed to capture the state of a frameset when the new document
4470 // causes the frameset to be destroyed...
4471 PersistLayoutHistoryState();
4473 // Remove this docshell from its parent's child list
4474 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4475 do_QueryInterface(GetAsSupports(mParent));
4476 if (docShellParentAsItem) {
4477 docShellParentAsItem->RemoveChild(this);
4480 if (mDocumentViewer) {
4481 mDocumentViewer->Close(nullptr);
4482 mDocumentViewer->Destroy();
4483 mDocumentViewer = nullptr;
4486 nsDocLoader::Destroy();
4488 mParentWidget = nullptr;
4489 SetCurrentURIInternal(nullptr);
4491 if (mScriptGlobal) {
4492 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4493 mScriptGlobal = nullptr;
4496 if (GetSessionHistory()) {
4497 // We want to destroy these content viewers now rather than
4498 // letting their destruction wait for the session history
4499 // entries to get garbage collected. (Bug 488394)
4500 GetSessionHistory()->EvictLocalDocumentViewers();
4503 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
4504 mBrowsingContext->PrepareForProcessChange();
4507 SetTreeOwner(nullptr);
4509 mBrowserChild = nullptr;
4511 mChromeEventHandler = nullptr;
4513 // Cancel any timers that were set for this docshell; this is needed
4514 // to break the cycle between us and the timers.
4515 CancelRefreshURITimers();
4517 return NS_OK;
4520 double nsDocShell::GetWidgetCSSToDeviceScale() {
4521 if (mParentWidget) {
4522 return mParentWidget->GetDefaultScale().scale;
4524 if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) {
4525 return ownerWindow->GetWidgetCSSToDeviceScale();
4527 return 1.0;
4530 NS_IMETHODIMP
4531 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4532 if (mParentWidget) {
4533 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4534 return NS_OK;
4537 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4538 if (ownerWindow) {
4539 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4542 *aScale = 1.0;
4543 return NS_OK;
4546 NS_IMETHODIMP
4547 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4548 mBounds.MoveTo(aX, aY);
4550 if (mDocumentViewer) {
4551 NS_ENSURE_SUCCESS(mDocumentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4554 return NS_OK;
4557 NS_IMETHODIMP
4558 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4559 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4560 if (ownerWindow) {
4561 return ownerWindow->SetPositionDesktopPix(aX, aY);
4564 double scale = 1.0;
4565 GetDevicePixelsPerDesktopPixel(&scale);
4566 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4569 NS_IMETHODIMP
4570 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4571 return GetPositionAndSize(aX, aY, nullptr, nullptr);
4574 NS_IMETHODIMP
4575 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4576 int32_t x = 0, y = 0;
4577 GetPosition(&x, &y);
4578 return SetPositionAndSize(x, y, aWidth, aHeight,
4579 aRepaint ? nsIBaseWindow::eRepaint : 0);
4582 NS_IMETHODIMP
4583 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4584 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4587 NS_IMETHODIMP
4588 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4589 int32_t aHeight, uint32_t aFlags) {
4590 mBounds.SetRect(aX, aY, aWidth, aHeight);
4592 // Hold strong ref, since SetBounds can make us null out mDocumentViewer
4593 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4594 if (viewer) {
4595 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4596 ? nsIDocumentViewer::eDelayResize
4597 : 0;
4598 // XXX Border figured in here or is that handled elsewhere?
4599 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4600 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4603 return NS_OK;
4606 NS_IMETHODIMP
4607 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4608 int32_t* aHeight) {
4609 if (mParentWidget) {
4610 // ensure size is up-to-date if window has changed resolution
4611 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4612 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4615 // We should really consider just getting this information from
4616 // our window instead of duplicating the storage and code...
4617 if (aWidth || aHeight) {
4618 // Caller wants to know our size; make sure to give them up to
4619 // date information.
4620 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4621 if (doc) {
4622 doc->FlushPendingNotifications(FlushType::Layout);
4626 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4627 return NS_OK;
4630 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4631 int32_t* aHeight) {
4632 if (aX) {
4633 *aX = mBounds.X();
4635 if (aY) {
4636 *aY = mBounds.Y();
4638 if (aWidth) {
4639 *aWidth = mBounds.Width();
4641 if (aHeight) {
4642 *aHeight = mBounds.Height();
4646 NS_IMETHODIMP
4647 nsDocShell::SetDimensions(DimensionRequest&& aRequest) {
4648 return NS_ERROR_NOT_IMPLEMENTED;
4651 NS_IMETHODIMP
4652 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
4653 int32_t* aY, int32_t* aCX, int32_t* aCY) {
4654 return NS_ERROR_NOT_IMPLEMENTED;
4657 NS_IMETHODIMP
4658 nsDocShell::Repaint(bool aForce) {
4659 PresShell* presShell = GetPresShell();
4660 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4662 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4663 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4665 viewManager->InvalidateAllViews();
4666 return NS_OK;
4669 NS_IMETHODIMP
4670 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4671 NS_ENSURE_ARG_POINTER(aParentWidget);
4673 *aParentWidget = mParentWidget;
4674 NS_IF_ADDREF(*aParentWidget);
4676 return NS_OK;
4679 NS_IMETHODIMP
4680 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4681 MOZ_ASSERT(!mIsBeingDestroyed);
4682 mParentWidget = aParentWidget;
4684 return NS_OK;
4687 NS_IMETHODIMP
4688 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4689 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4691 if (mParentWidget) {
4692 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4693 } else {
4694 *aParentNativeWindow = nullptr;
4697 return NS_OK;
4700 NS_IMETHODIMP
4701 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4702 return NS_ERROR_NOT_IMPLEMENTED;
4705 NS_IMETHODIMP
4706 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4707 // the nativeHandle should be accessed from nsIAppWindow
4708 return NS_ERROR_NOT_IMPLEMENTED;
4711 NS_IMETHODIMP
4712 nsDocShell::GetVisibility(bool* aVisibility) {
4713 NS_ENSURE_ARG_POINTER(aVisibility);
4715 *aVisibility = false;
4717 if (!mDocumentViewer) {
4718 return NS_OK;
4721 PresShell* presShell = GetPresShell();
4722 if (!presShell) {
4723 return NS_OK;
4726 // get the view manager
4727 nsViewManager* vm = presShell->GetViewManager();
4728 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4730 // get the root view
4731 nsView* view = vm->GetRootView(); // views are not ref counted
4732 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4734 // if our root view is hidden, we are not visible
4735 if (view->GetVisibility() == ViewVisibility::Hide) {
4736 return NS_OK;
4739 // otherwise, we must walk up the document and view trees checking
4740 // for a hidden view, unless we're an off screen browser, which
4741 // would make this test meaningless.
4743 RefPtr<nsDocShell> docShell = this;
4744 RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4745 while (parentItem) {
4746 // Null-check for crash in bug 267804
4747 if (!parentItem->GetPresShell()) {
4748 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4749 return NS_OK;
4752 vm = docShell->GetPresShell()->GetViewManager();
4753 if (vm) {
4754 view = vm->GetRootView();
4757 if (view) {
4758 view = view->GetParent(); // anonymous inner view
4759 if (view) {
4760 view = view->GetParent(); // subdocumentframe's view
4764 nsIFrame* frame = view ? view->GetFrame() : nullptr;
4765 if (frame && !frame->IsVisibleConsideringAncestors(
4766 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
4767 return NS_OK;
4770 docShell = parentItem;
4771 parentItem = docShell->GetInProcessParentDocshell();
4774 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4775 if (!treeOwnerAsWin) {
4776 *aVisibility = true;
4777 return NS_OK;
4780 // Check with the tree owner as well to give embedders a chance to
4781 // expose visibility as well.
4782 nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility);
4783 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
4784 // The tree owner had no opinion on our visibility.
4785 *aVisibility = true;
4786 return NS_OK;
4788 return rv;
4791 void nsDocShell::ActivenessMaybeChanged() {
4792 const bool isActive = mBrowsingContext->IsActive();
4793 if (RefPtr<PresShell> presShell = GetPresShell()) {
4794 presShell->ActivenessMaybeChanged();
4797 // Tell the window about it
4798 if (mScriptGlobal) {
4799 mScriptGlobal->SetIsBackground(!isActive);
4800 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4801 // Update orientation when the top-level browsing context becomes active.
4802 if (isActive && mBrowsingContext->IsTop()) {
4803 // We only care about the top-level browsing context.
4804 auto orientation = mBrowsingContext->GetOrientationLock();
4805 ScreenOrientation::UpdateActiveOrientationLock(orientation);
4808 doc->PostVisibilityUpdateEvent();
4812 // Tell the nsDOMNavigationTiming about it
4813 RefPtr<nsDOMNavigationTiming> timing = mTiming;
4814 if (!timing && mDocumentViewer) {
4815 if (Document* doc = mDocumentViewer->GetDocument()) {
4816 timing = doc->GetNavigationTiming();
4819 if (timing) {
4820 timing->NotifyDocShellStateChanged(
4821 isActive ? nsDOMNavigationTiming::DocShellState::eActive
4822 : nsDOMNavigationTiming::DocShellState::eInactive);
4825 // Restart or stop meta refresh timers if necessary
4826 if (mDisableMetaRefreshWhenInactive) {
4827 if (isActive) {
4828 ResumeRefreshURIs();
4829 } else {
4830 SuspendRefreshURIs();
4834 if (InputTaskManager::CanSuspendInputEvent()) {
4835 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
4839 NS_IMETHODIMP
4840 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4841 if (!mWillChangeProcess) {
4842 // Intentionally ignoring handling discarded browsing contexts.
4843 Unused << mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4844 } else {
4845 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4846 // shutdown. Sorry DevTools, your DocShell is in another process.
4847 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4849 return NS_OK;
4852 NS_IMETHODIMP
4853 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4854 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4855 return NS_OK;
4858 NS_IMETHODIMP
4859 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4860 NS_ENSURE_ARG_POINTER(aFailedChannel);
4861 Document* doc = GetDocument();
4862 if (!doc) {
4863 *aFailedChannel = nullptr;
4864 return NS_OK;
4866 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4867 return NS_OK;
4870 NS_IMETHODIMP
4871 nsDocShell::SetVisibility(bool aVisibility) {
4872 // Show()/Hide() may change mDocumentViewer.
4873 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4874 if (!viewer) {
4875 return NS_OK;
4877 if (aVisibility) {
4878 viewer->Show();
4879 } else {
4880 viewer->Hide();
4883 return NS_OK;
4886 NS_IMETHODIMP
4887 nsDocShell::GetEnabled(bool* aEnabled) {
4888 NS_ENSURE_ARG_POINTER(aEnabled);
4889 *aEnabled = true;
4890 return NS_ERROR_NOT_IMPLEMENTED;
4893 NS_IMETHODIMP
4894 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4896 NS_IMETHODIMP
4897 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4898 // We don't create our own widget, so simply return the parent one.
4899 return GetParentWidget(aMainWidget);
4902 NS_IMETHODIMP
4903 nsDocShell::GetTitle(nsAString& aTitle) {
4904 aTitle = mTitle;
4905 return NS_OK;
4908 NS_IMETHODIMP
4909 nsDocShell::SetTitle(const nsAString& aTitle) {
4910 // Avoid unnecessary updates of the title if the URI and the title haven't
4911 // changed.
4912 if (mTitleValidForCurrentURI && mTitle == aTitle) {
4913 return NS_OK;
4916 // Store local title
4917 mTitle = aTitle;
4918 mTitleValidForCurrentURI = true;
4920 // When title is set on the top object it should then be passed to the
4921 // tree owner.
4922 if (mBrowsingContext->IsTop()) {
4923 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4924 if (treeOwnerAsWin) {
4925 treeOwnerAsWin->SetTitle(aTitle);
4929 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4930 UpdateGlobalHistoryTitle(mCurrentURI);
4933 // Update SessionHistory with the document's title.
4934 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
4935 SetTitleOnHistoryEntry(true);
4938 return NS_OK;
4941 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
4942 if (mOSHE) {
4943 mOSHE->SetTitle(mTitle);
4946 if (mActiveEntry && mBrowsingContext) {
4947 mActiveEntry->SetTitle(mTitle);
4948 if (aUpdateEntryInSessionHistory) {
4949 if (XRE_IsParentProcess()) {
4950 SessionHistoryEntry* entry =
4951 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
4952 if (entry) {
4953 entry->SetTitle(mTitle);
4955 } else {
4956 mozilla::Unused
4957 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
4958 mBrowsingContext, mTitle);
4964 nsPoint nsDocShell::GetCurScrollPos() {
4965 nsPoint scrollPos;
4966 if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
4967 scrollPos = sf->GetVisualViewportOffset();
4969 return scrollPos;
4972 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
4973 int32_t aCurVerticalPos) {
4974 nsIScrollableFrame* sf = GetRootScrollFrame();
4975 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4977 ScrollMode scrollMode =
4978 sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
4980 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
4981 sf->ScrollTo(targetPos, scrollMode);
4983 // Set the visual viewport offset as well.
4985 RefPtr<PresShell> presShell = GetPresShell();
4986 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4988 nsPresContext* presContext = presShell->GetPresContext();
4989 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
4991 // Only the root content document can have a distinct visual viewport offset.
4992 if (!presContext->IsRootContentDocumentCrossProcess()) {
4993 return NS_OK;
4996 // Not on a platform with a distinct visual viewport - don't bother setting
4997 // the visual viewport offset.
4998 if (!presShell->IsVisualViewportSizeSet()) {
4999 return NS_OK;
5002 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
5003 scrollMode);
5005 return NS_OK;
5008 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
5009 if (mScrollbarPref == aPref) {
5010 return;
5012 mScrollbarPref = aPref;
5013 auto* ps = GetPresShell();
5014 if (!ps) {
5015 return;
5017 nsIFrame* scrollFrame = ps->GetRootScrollFrame();
5018 if (!scrollFrame) {
5019 return;
5021 ps->FrameNeedsReflow(scrollFrame,
5022 IntrinsicDirty::FrameAncestorsAndDescendants,
5023 NS_FRAME_IS_DIRTY);
5026 //*****************************************************************************
5027 // nsDocShell::nsIRefreshURI
5028 //*****************************************************************************
5030 NS_IMETHODIMP
5031 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5032 uint32_t aDelay) {
5033 MOZ_ASSERT(!mIsBeingDestroyed);
5035 NS_ENSURE_ARG(aURI);
5037 /* Check if Meta refresh/redirects are permitted. Some
5038 * embedded applications may not want to do this.
5039 * Must do this before sending out NOTIFY_REFRESH events
5040 * because listeners may have side effects (e.g. displaying a
5041 * button to manually trigger the refresh later).
5043 bool allowRedirects = true;
5044 GetAllowMetaRedirects(&allowRedirects);
5045 if (!allowRedirects) {
5046 return NS_OK;
5049 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5050 // give them a chance to block this refresh.
5051 bool sameURI;
5052 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5053 if (NS_FAILED(rv)) {
5054 sameURI = false;
5056 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
5057 return NS_OK;
5060 nsCOMPtr<nsITimerCallback> refreshTimer =
5061 new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
5063 BusyFlags busyFlags = GetBusyFlags();
5065 if (!mRefreshURIList) {
5066 mRefreshURIList = nsArray::Create();
5069 if (busyFlags & BUSY_FLAGS_BUSY ||
5070 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
5071 // We don't want to create the timer right now. Instead queue up the
5072 // request and trigger the timer in EndPageLoad() or whenever we become
5073 // active.
5074 mRefreshURIList->AppendElement(refreshTimer);
5075 } else {
5076 // There is no page loading going on right now. Create the
5077 // timer and fire it right away.
5078 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5079 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
5081 nsCOMPtr<nsITimer> timer;
5082 MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
5083 nsITimer::TYPE_ONE_SHOT));
5085 mRefreshURIList->AppendElement(timer); // owning timer ref
5087 return NS_OK;
5090 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
5091 nsIPrincipal* aPrincipal,
5092 uint32_t aDelay,
5093 nsITimer* aTimer) {
5094 MOZ_ASSERT(aTimer, "Must have a timer here");
5096 // Remove aTimer from mRefreshURIList if needed
5097 if (mRefreshURIList) {
5098 uint32_t n = 0;
5099 mRefreshURIList->GetLength(&n);
5101 for (uint32_t i = 0; i < n; ++i) {
5102 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5103 if (timer == aTimer) {
5104 mRefreshURIList->RemoveElementAt(i);
5105 break;
5110 return ForceRefreshURI(aURI, aPrincipal, aDelay);
5113 NS_IMETHODIMP
5114 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5115 uint32_t aDelay) {
5116 NS_ENSURE_ARG(aURI);
5118 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
5119 loadState->SetOriginalURI(mCurrentURI);
5120 loadState->SetResultPrincipalURI(aURI);
5121 loadState->SetResultPrincipalURIIsSome(true);
5122 loadState->SetKeepResultPrincipalURIIfSet(true);
5123 loadState->SetIsMetaRefresh(true);
5125 // Set the triggering pricipal to aPrincipal if available, or current
5126 // document's principal otherwise.
5127 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
5128 RefPtr<Document> doc = GetDocument();
5129 if (!principal) {
5130 if (!doc) {
5131 return NS_ERROR_FAILURE;
5133 principal = doc->NodePrincipal();
5135 loadState->SetTriggeringPrincipal(principal);
5136 if (doc) {
5137 loadState->SetCsp(doc->GetCsp());
5138 loadState->SetHasValidUserGestureActivation(
5139 doc->HasValidTransientUserGestureActivation());
5140 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
5141 loadState->SetTriggeringWindowId(doc->InnerWindowID());
5142 loadState->SetTriggeringStorageAccess(doc->UsingStorageAccess());
5145 loadState->SetPrincipalIsExplicit(true);
5147 /* Check if this META refresh causes a redirection
5148 * to another site.
5150 bool equalUri = false;
5151 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5153 nsCOMPtr<nsIReferrerInfo> referrerInfo;
5154 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
5155 /* It is a META refresh based redirection within the threshold time
5156 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5157 * Pass a REPLACE flag to LoadURI().
5159 loadState->SetLoadType(LOAD_REFRESH_REPLACE);
5161 /* For redirects we mimic HTTP, which passes the
5162 * original referrer.
5163 * We will pass in referrer but will not send to server
5165 if (mReferrerInfo) {
5166 referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5167 ->CloneWithNewSendReferrer(false);
5169 } else {
5170 loadState->SetLoadType(LOAD_REFRESH);
5171 /* We do need to pass in a referrer, but we don't want it to
5172 * be sent to the server.
5173 * For most refreshes the current URI is an appropriate
5174 * internal referrer.
5176 referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5179 loadState->SetReferrerInfo(referrerInfo);
5180 loadState->SetLoadFlags(
5181 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5182 loadState->SetFirstParty(true);
5185 * LoadURI(...) will cancel all refresh timers... This causes the
5186 * Timer and its refreshData instance to be released...
5188 LoadURI(loadState, false);
5190 return NS_OK;
5193 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
5194 const char16_t* aEnd) {
5195 const char16_t* iter = aStart;
5196 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
5197 ++iter;
5199 return iter;
5202 static std::tuple<const char16_t*, const char16_t*> ExtractURLString(
5203 const char16_t* aPosition, const char16_t* aEnd) {
5204 MOZ_ASSERT(aPosition != aEnd);
5206 // 1. Let urlString be the substring of input from the code point at
5207 // position to the end of the string.
5208 const char16_t* urlStart = aPosition;
5209 const char16_t* urlEnd = aEnd;
5211 // 2. If the code point in input pointed to by position is U+0055 (U) or
5212 // U+0075 (u), then advance position to the next code point.
5213 // Otherwise, jump to the step labeled skip quotes.
5214 if (*aPosition == 'U' || *aPosition == 'u') {
5215 ++aPosition;
5217 // 3. If the code point in input pointed to by position is U+0052 (R) or
5218 // U+0072 (r), then advance position to the next code point.
5219 // Otherwise, jump to the step labeled parse.
5220 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
5221 return std::make_tuple(urlStart, urlEnd);
5224 ++aPosition;
5226 // 4. If the code point in input pointed to by position is U+004C (L) or
5227 // U+006C (l), then advance position to the next code point.
5228 // Otherwise, jump to the step labeled parse.
5229 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
5230 return std::make_tuple(urlStart, urlEnd);
5233 ++aPosition;
5235 // 5. Skip ASCII whitespace within input given position.
5236 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5238 // 6. If the code point in input pointed to by position is U+003D (=),
5239 // then advance position to the next code point. Otherwise, jump to
5240 // the step labeled parse.
5241 if (aPosition == aEnd || *aPosition != '=') {
5242 return std::make_tuple(urlStart, urlEnd);
5245 ++aPosition;
5247 // 7. Skip ASCII whitespace within input given position.
5248 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5251 // 8. Skip quotes: If the code point in input pointed to by position is
5252 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5253 // advance position to the next code point. Otherwise, let quote be
5254 // the empty string.
5255 Maybe<char> quote;
5256 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
5257 quote.emplace(*aPosition);
5258 ++aPosition;
5261 // 9. Set urlString to the substring of input from the code point at
5262 // position to the end of the string.
5263 urlStart = aPosition;
5264 urlEnd = aEnd;
5266 // 10. If quote is not the empty string, and there is a code point in
5267 // urlString equal to quote, then truncate urlString at that code
5268 // point, so that it and all subsequent code points are removed.
5269 const char16_t* quotePos;
5270 if (quote.isSome() &&
5271 (quotePos = nsCharTraits<char16_t>::find(
5272 urlStart, std::distance(urlStart, aEnd), quote.value()))) {
5273 urlEnd = quotePos;
5276 return std::make_tuple(urlStart, urlEnd);
5279 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
5280 const nsAString& aHeader) {
5281 if (mIsBeingDestroyed) {
5282 return;
5285 const char16_t* position = aHeader.BeginReading();
5286 const char16_t* end = aHeader.EndReading();
5288 // See
5289 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5291 // 3. Skip ASCII whitespace
5292 position = SkipASCIIWhitespace(position, end);
5294 // 4. Let time be 0.
5295 CheckedInt<uint32_t> milliSeconds;
5297 // 5. Collect a sequence of code points that are ASCII digits
5298 const char16_t* digitsStart = position;
5299 while (position != end && mozilla::IsAsciiDigit(*position)) {
5300 ++position;
5303 if (position == digitsStart) {
5304 // 6. If timeString is the empty string, then:
5305 // 1. If the code point in input pointed to by position is not U+002E
5306 // (.), then return.
5307 if (position == end || *position != '.') {
5308 return;
5310 } else {
5311 // 7. Otherwise, set time to the result of parsing timeString using the
5312 // rules for parsing non-negative integers.
5313 nsContentUtils::ParseHTMLIntegerResultFlags result;
5314 uint32_t seconds =
5315 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
5316 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
5317 if (result & nsContentUtils::eParseHTMLInteger_Error) {
5318 // The spec assumes no errors here (since we only pass ASCII digits in),
5319 // but we can still overflow, so this block should deal with that (and
5320 // only that).
5321 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
5322 return;
5324 MOZ_ASSERT(
5325 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
5327 milliSeconds = seconds;
5328 milliSeconds *= 1000;
5329 if (!milliSeconds.isValid()) {
5330 return;
5334 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5335 // STOP characters (.) from input given position. Ignore any collected
5336 // characters.
5337 while (position != end &&
5338 (mozilla::IsAsciiDigit(*position) || *position == '.')) {
5339 ++position;
5342 // 9. Let urlRecord be document's URL.
5343 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
5345 // 10. If position is not past the end of input
5346 if (position != end) {
5347 // 1. If the code point in input pointed to by position is not U+003B (;),
5348 // U+002C (,), or ASCII whitespace, then return.
5349 if (*position != ';' && *position != ',' &&
5350 !mozilla::IsAsciiWhitespace(*position)) {
5351 return;
5354 // 2. Skip ASCII whitespace within input given position.
5355 position = SkipASCIIWhitespace(position, end);
5357 // 3. If the code point in input pointed to by position is U+003B (;) or
5358 // U+002C (,), then advance position to the next code point.
5359 if (position != end && (*position == ';' || *position == ',')) {
5360 ++position;
5362 // 4. Skip ASCII whitespace within input given position.
5363 position = SkipASCIIWhitespace(position, end);
5366 // 11. If position is not past the end of input, then:
5367 if (position != end) {
5368 const char16_t* urlStart;
5369 const char16_t* urlEnd;
5371 // 1-10. See ExtractURLString.
5372 std::tie(urlStart, urlEnd) = ExtractURLString(position, end);
5374 // 11. Parse: Parse urlString relative to document. If that fails, return.
5375 // Otherwise, set urlRecord to the resulting URL record.
5376 nsresult rv =
5377 NS_NewURI(getter_AddRefs(urlRecord),
5378 Substring(urlStart, std::distance(urlStart, urlEnd)),
5379 /* charset = */ nullptr, aDocument->GetDocBaseURI());
5380 NS_ENSURE_SUCCESS_VOID(rv);
5384 nsIPrincipal* principal = aDocument->NodePrincipal();
5385 nsCOMPtr<nsIScriptSecurityManager> securityManager =
5386 nsContentUtils::GetSecurityManager();
5387 nsresult rv = securityManager->CheckLoadURIWithPrincipal(
5388 principal, urlRecord,
5389 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5390 aDocument->InnerWindowID());
5391 NS_ENSURE_SUCCESS_VOID(rv);
5393 bool isjs = true;
5394 rv = NS_URIChainHasFlags(
5395 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5396 NS_ENSURE_SUCCESS_VOID(rv);
5398 if (isjs) {
5399 return;
5402 RefreshURI(urlRecord, principal, milliSeconds.value());
5405 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5406 if (!aTimerList) {
5407 return;
5410 uint32_t n = 0;
5411 aTimerList->GetLength(&n);
5413 while (n) {
5414 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5416 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5418 if (timer) {
5419 timer->Cancel();
5424 NS_IMETHODIMP
5425 nsDocShell::CancelRefreshURITimers() {
5426 DoCancelRefreshURITimers(mRefreshURIList);
5427 DoCancelRefreshURITimers(mSavedRefreshURIList);
5428 DoCancelRefreshURITimers(mBFCachedRefreshURIList);
5429 mRefreshURIList = nullptr;
5430 mSavedRefreshURIList = nullptr;
5431 mBFCachedRefreshURIList = nullptr;
5433 return NS_OK;
5436 NS_IMETHODIMP
5437 nsDocShell::GetRefreshPending(bool* aResult) {
5438 if (!mRefreshURIList) {
5439 *aResult = false;
5440 return NS_OK;
5443 uint32_t count;
5444 nsresult rv = mRefreshURIList->GetLength(&count);
5445 if (NS_SUCCEEDED(rv)) {
5446 *aResult = (count != 0);
5448 return rv;
5451 void nsDocShell::RefreshURIToQueue() {
5452 if (mRefreshURIList) {
5453 uint32_t n = 0;
5454 mRefreshURIList->GetLength(&n);
5456 for (uint32_t i = 0; i < n; ++i) {
5457 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5458 if (!timer) {
5459 continue; // this must be a nsRefreshURI already
5462 // Replace this timer object with a nsRefreshTimer object.
5463 nsCOMPtr<nsITimerCallback> callback;
5464 timer->GetCallback(getter_AddRefs(callback));
5466 timer->Cancel();
5468 mRefreshURIList->ReplaceElementAt(callback, i);
5473 NS_IMETHODIMP
5474 nsDocShell::SuspendRefreshURIs() {
5475 RefreshURIToQueue();
5477 // Suspend refresh URIs for our child shells as well.
5478 for (auto* child : mChildList.ForwardRange()) {
5479 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5480 if (shell) {
5481 shell->SuspendRefreshURIs();
5485 return NS_OK;
5488 NS_IMETHODIMP
5489 nsDocShell::ResumeRefreshURIs() {
5490 RefreshURIFromQueue();
5492 // Resume refresh URIs for our child shells as well.
5493 for (auto* child : mChildList.ForwardRange()) {
5494 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5495 if (shell) {
5496 shell->ResumeRefreshURIs();
5500 return NS_OK;
5503 nsresult nsDocShell::RefreshURIFromQueue() {
5504 if (!mRefreshURIList) {
5505 return NS_OK;
5507 uint32_t n = 0;
5508 mRefreshURIList->GetLength(&n);
5510 while (n) {
5511 nsCOMPtr<nsITimerCallback> refreshInfo =
5512 do_QueryElementAt(mRefreshURIList, --n);
5514 if (refreshInfo) {
5515 // This is the nsRefreshTimer object, waiting to be
5516 // setup in a timer object and fired.
5517 // Create the timer and trigger it.
5518 uint32_t delay = static_cast<nsRefreshTimer*>(
5519 static_cast<nsITimerCallback*>(refreshInfo))
5520 ->GetDelay();
5521 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5522 if (win) {
5523 nsCOMPtr<nsITimer> timer;
5524 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5525 nsITimer::TYPE_ONE_SHOT);
5527 if (timer) {
5528 // Replace the nsRefreshTimer element in the queue with
5529 // its corresponding timer object, so that in case another
5530 // load comes through before the timer can go off, the timer will
5531 // get cancelled in CancelRefreshURITimer()
5532 mRefreshURIList->ReplaceElementAt(timer, n);
5538 return NS_OK;
5541 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) {
5542 nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
5543 bool firstPart = false;
5544 return multiPartChannel &&
5545 NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) &&
5546 !firstPart;
5549 nsresult nsDocShell::Embed(nsIDocumentViewer* aDocumentViewer,
5550 WindowGlobalChild* aWindowActor,
5551 bool aIsTransientAboutBlank, bool aPersist,
5552 nsIRequest* aRequest, nsIURI* aPreviousURI) {
5553 // Save the LayoutHistoryState of the previous document, before
5554 // setting up new document
5555 PersistLayoutHistoryState();
5557 nsresult rv = SetupNewViewer(aDocumentViewer, aWindowActor);
5558 NS_ENSURE_SUCCESS(rv, rv);
5560 // XXX What if SetupNewViewer fails?
5561 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
5562 // Set history.state
5563 SetDocCurrentStateObj(mLSHE,
5564 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
5567 if (mLSHE) {
5568 // Restore the editing state, if it's stored in session history.
5569 if (mLSHE->HasDetachedEditor()) {
5570 ReattachEditorToWindow(mLSHE);
5573 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5576 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() &&
5577 !IsFollowupPartOfMultipart(aRequest)) {
5578 bool expired = false;
5579 uint32_t cacheKey = 0;
5580 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
5581 if (cacheChannel) {
5582 // Check if the page has expired from cache
5583 uint32_t expTime = 0;
5584 cacheChannel->GetCacheTokenExpirationTime(&expTime);
5585 uint32_t now = PRTimeToSeconds(PR_Now());
5586 if (expTime <= now) {
5587 expired = true;
5590 // The checks for updating cache key are similar to the old session
5591 // history in OnNewURI. Try to update the cache key if
5592 // - we should update session history and aren't doing a session
5593 // history load.
5594 // - we're doing a forced reload.
5595 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
5596 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
5597 IsForceReloadType(mLoadType)) {
5598 cacheChannel->GetCacheKey(&cacheKey);
5602 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
5603 MoveLoadingToActiveEntry(aPersist, expired, cacheKey, aPreviousURI);
5606 bool updateHistory = true;
5608 // Determine if this type of load should update history
5609 switch (mLoadType) {
5610 case LOAD_NORMAL_REPLACE:
5611 case LOAD_REFRESH_REPLACE:
5612 case LOAD_STOP_CONTENT_AND_REPLACE:
5613 case LOAD_RELOAD_BYPASS_CACHE:
5614 case LOAD_RELOAD_BYPASS_PROXY:
5615 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5616 case LOAD_REPLACE_BYPASS_CACHE:
5617 updateHistory = false;
5618 break;
5619 default:
5620 break;
5623 if (!updateHistory) {
5624 SetLayoutHistoryState(nullptr);
5627 return NS_OK;
5630 //*****************************************************************************
5631 // nsDocShell::nsIWebProgressListener
5632 //*****************************************************************************
5634 NS_IMETHODIMP
5635 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5636 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5637 int32_t aCurTotalProgress,
5638 int32_t aMaxTotalProgress) {
5639 return NS_OK;
5642 NS_IMETHODIMP
5643 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5644 uint32_t aStateFlags, nsresult aStatus) {
5645 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5646 // Save timing statistics.
5647 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5648 nsCOMPtr<nsIURI> uri;
5649 channel->GetURI(getter_AddRefs(uri));
5650 nsAutoCString aURI;
5651 uri->GetAsciiSpec(aURI);
5653 if (this == aProgress) {
5654 mozilla::Unused << MaybeInitTiming();
5655 mTiming->NotifyFetchStart(uri,
5656 ConvertLoadTypeToNavigationType(mLoadType));
5657 // If we are starting a DocumentChannel, we need to pass the timing
5658 // statistics so that should a process switch occur, the starting type can
5659 // be passed to the new DocShell running in the other content process.
5660 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5661 docChannel->SetNavigationTiming(mTiming);
5665 // Page has begun to load
5666 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5668 if ((aStateFlags & STATE_RESTORING) == 0) {
5669 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
5670 if (IsForceReloadType(mLoadType)) {
5671 if (WindowContext* windowContext =
5672 mBrowsingContext->GetCurrentWindowContext()) {
5673 SessionStoreChild::From(windowContext->GetWindowGlobalChild())
5674 ->ResetSessionStore(mBrowsingContext,
5675 mBrowsingContext->GetSessionStoreEpoch());
5680 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5681 // Page is loading
5682 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5683 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5684 // Page has finished loading
5685 mBusyFlags = BUSY_FLAGS_NONE;
5688 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5689 nsCOMPtr<nsIWebProgress> webProgress =
5690 do_QueryInterface(GetAsSupports(this));
5691 // Is the document stop notification for this document?
5692 if (aProgress == webProgress.get()) {
5693 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5694 EndPageLoad(aProgress, channel, aStatus);
5697 // note that redirect state changes will go through here as well, but it
5698 // is better to handle those in OnRedirectStateChange where more
5699 // information is available.
5700 return NS_OK;
5703 NS_IMETHODIMP
5704 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5705 nsIURI* aURI, uint32_t aFlags) {
5706 // Since we've now changed Documents, notify the BrowsingContext that we've
5707 // changed. Ideally we'd just let the BrowsingContext do this when it
5708 // changes the current window global, but that happens before this and we
5709 // have a lot of tests that depend on the specific ordering of messages.
5710 bool isTopLevel = false;
5711 if (XRE_IsParentProcess() &&
5712 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
5713 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
5714 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5716 return NS_OK;
5719 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5720 nsIChannel* aNewChannel,
5721 uint32_t aRedirectFlags,
5722 uint32_t aStateFlags) {
5723 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5724 "Calling OnRedirectStateChange when there is no redirect");
5726 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5727 return; // not a toplevel document
5730 nsCOMPtr<nsIURI> oldURI, newURI;
5731 aOldChannel->GetURI(getter_AddRefs(oldURI));
5732 aNewChannel->GetURI(getter_AddRefs(newURI));
5733 if (!oldURI || !newURI) {
5734 return;
5737 // DocumentChannel adds redirect chain to global history in the parent
5738 // process. The redirect chain can't be queried from the content process, so
5739 // there's no need to update global history here.
5740 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5741 if (!docChannel) {
5742 // Below a URI visit is saved (see AddURIVisit method doc).
5743 // The visit chain looks something like:
5744 // ...
5745 // Site N - 1
5746 // => Site N
5747 // (redirect to =>) Site N + 1 (we are here!)
5749 // Get N - 1 and transition type
5750 nsCOMPtr<nsIURI> previousURI;
5751 uint32_t previousFlags = 0;
5752 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5754 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5755 net::ChannelIsPost(aOldChannel)) {
5756 // 1. Internal redirects are ignored because they are specific to the
5757 // channel implementation.
5758 // 2. POSTs are not saved by global history.
5760 // Regardless, we need to propagate the previous visit to the new
5761 // channel.
5762 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5763 } else {
5764 // Get the HTTP response code, if available.
5765 uint32_t responseStatus = 0;
5766 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5767 if (httpChannel) {
5768 Unused << httpChannel->GetResponseStatus(&responseStatus);
5771 // Add visit N -1 => N
5772 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5774 // Since N + 1 could be the final destination, we will not save N => N + 1
5775 // here. OnNewURI will do that, so we will cache it.
5776 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5780 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5781 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5782 mLoadType = LOAD_NORMAL_REPLACE;
5783 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5787 NS_IMETHODIMP
5788 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5789 nsresult aStatus, const char16_t* aMessage) {
5790 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5791 return NS_OK;
5794 NS_IMETHODIMP
5795 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5796 uint32_t aState) {
5797 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5798 return NS_OK;
5801 NS_IMETHODIMP
5802 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5803 nsIRequest* aRequest, uint32_t aEvent) {
5804 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5805 return NS_OK;
5808 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5809 const nsACString& aKeyword, bool aIsPrivateContext) {
5810 nsCOMPtr<nsIURIFixupInfo> info;
5811 if (!XRE_IsContentProcess()) {
5812 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5813 if (uriFixup) {
5814 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
5817 return info.forget();
5820 /* static */
5821 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
5822 nsIChannel* aChannel, nsIURI* aUrl) {
5823 if (!aChannel) {
5824 return nullptr;
5827 nsresult rv = NS_OK;
5828 nsAutoCString host;
5829 rv = aUrl->GetAsciiHost(host);
5830 if (NS_WARN_IF(NS_FAILED(rv))) {
5831 return nullptr;
5834 // No point in going further if "www." is included in the hostname
5835 // already. That is the only hueristic we're applying in this function.
5836 if (StringBeginsWith(host, "www."_ns)) {
5837 return nullptr;
5840 // Return if fixup enable pref is turned off.
5841 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5842 return nullptr;
5845 // Return if scheme is not HTTPS.
5846 if (!SchemeIsHTTPS(aUrl)) {
5847 return nullptr;
5850 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
5851 if (!info) {
5852 return nullptr;
5855 // Skip doing the fixup if our channel was redirected, because we
5856 // shouldn't be guessing things about the post-redirect URI.
5857 if (!info->RedirectChain().IsEmpty()) {
5858 return nullptr;
5861 int32_t port = 0;
5862 rv = aUrl->GetPort(&port);
5863 if (NS_WARN_IF(NS_FAILED(rv))) {
5864 return nullptr;
5867 // Don't fix up hosts with ports.
5868 if (port != -1) {
5869 return nullptr;
5872 // Don't fix up localhost url.
5873 if (host == "localhost") {
5874 return nullptr;
5877 // Don't fix up hostnames with IP address.
5878 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
5879 return nullptr;
5882 nsAutoCString userPass;
5883 rv = aUrl->GetUserPass(userPass);
5884 if (NS_WARN_IF(NS_FAILED(rv))) {
5885 return nullptr;
5888 // Security - URLs with user / password info should NOT be modified.
5889 if (!userPass.IsEmpty()) {
5890 return nullptr;
5893 nsCOMPtr<nsITransportSecurityInfo> tsi;
5894 rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi));
5895 if (NS_WARN_IF(NS_FAILED(rv))) {
5896 return nullptr;
5899 if (NS_WARN_IF(!tsi)) {
5900 return nullptr;
5903 nsCOMPtr<nsIX509Cert> cert;
5904 rv = tsi->GetServerCert(getter_AddRefs(cert));
5905 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
5906 return nullptr;
5909 nsTArray<uint8_t> certBytes;
5910 rv = cert->GetRawDER(certBytes);
5911 if (NS_FAILED(rv)) {
5912 return nullptr;
5915 mozilla::pkix::Input serverCertInput;
5916 mozilla::pkix::Result rv1 =
5917 serverCertInput.Init(certBytes.Elements(), certBytes.Length());
5918 if (rv1 != mozilla::pkix::Success) {
5919 return nullptr;
5922 nsAutoCString newHost("www."_ns);
5923 newHost.Append(host);
5925 mozilla::pkix::Input newHostInput;
5926 rv1 = newHostInput.Init(
5927 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
5928 newHost.Length());
5929 if (rv1 != mozilla::pkix::Success) {
5930 return nullptr;
5933 // Check if adding a "www." prefix to the request's hostname will
5934 // cause the response's certificate to match.
5935 rv1 = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput);
5936 if (rv1 != mozilla::pkix::Success) {
5937 return nullptr;
5940 nsCOMPtr<nsIURI> newURI;
5941 Unused << NS_MutateURI(aUrl).SetHost(newHost).Finalize(
5942 getter_AddRefs(newURI));
5944 return newURI.forget();
5947 /* static */
5948 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
5949 nsIChannel* aChannel, nsresult aStatus,
5950 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
5951 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
5952 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData,
5953 bool* outWasSchemelessInput) {
5954 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
5955 aStatus != NS_ERROR_CONNECTION_REFUSED &&
5956 aStatus !=
5957 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
5958 return nullptr;
5961 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
5962 return nullptr;
5965 nsCOMPtr<nsIURI> url;
5966 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
5967 if (NS_FAILED(rv)) {
5968 return nullptr;
5972 // Try and make an alternative URI from the old one
5974 nsCOMPtr<nsIURI> newURI;
5975 nsCOMPtr<nsIInputStream> newPostData;
5977 nsAutoCString oldSpec;
5978 url->GetSpec(oldSpec);
5981 // First try keyword fixup
5983 nsAutoString keywordProviderName, keywordAsSent;
5984 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
5985 // we should only perform a keyword search under the following
5986 // conditions:
5987 // (0) Pref keyword.enabled is true
5988 // (1) the url scheme is http (or https)
5989 // (2) the url does not have a protocol scheme
5990 // If we don't enforce such a policy, then we end up doing
5991 // keyword searchs on urls we don't intend like imap, file,
5992 // mailbox, etc. This could lead to a security problem where we
5993 // send data to the keyword server that we shouldn't be.
5994 // Someone needs to clean up keywords in general so we can
5995 // determine on a per url basis if we want keywords
5996 // enabled...this is just a bandaid...
5997 nsAutoCString scheme;
5998 Unused << url->GetScheme(scheme);
5999 if (Preferences::GetBool("keyword.enabled", false) &&
6000 StringBeginsWith(scheme, "http"_ns)) {
6001 bool attemptFixup = false;
6002 nsAutoCString host;
6003 Unused << url->GetHost(host);
6004 if (host.FindChar('.') == kNotFound) {
6005 attemptFixup = true;
6006 } else {
6007 // For domains with dots, we check the public suffix validity.
6008 nsCOMPtr<nsIEffectiveTLDService> tldService =
6009 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
6010 if (tldService) {
6011 nsAutoCString suffix;
6012 attemptFixup =
6013 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
6014 suffix.IsEmpty();
6017 if (attemptFixup) {
6018 nsCOMPtr<nsIURIFixupInfo> info;
6019 // only send non-qualified hosts to the keyword server
6020 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
6021 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
6022 } else {
6024 // If this string was passed through nsStandardURL by
6025 // chance, then it may have been converted from UTF-8 to
6026 // ACE, which would result in a completely bogus keyword
6027 // query. Here we try to recover the original Unicode
6028 // value, but this is not 100% correct since the value may
6029 // have been normalized per the IDN normalization rules.
6031 // Since we don't have access to the exact original string
6032 // that was entered by the user, this will just have to do.
6033 bool isACE;
6034 nsAutoCString utf8Host;
6035 nsCOMPtr<nsIIDNService> idnSrv =
6036 do_GetService(NS_IDNSERVICE_CONTRACTID);
6037 if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6038 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6039 info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
6041 } else {
6042 info = KeywordToURI(host, aUsePrivateBrowsing);
6045 if (info) {
6046 info->GetPreferredURI(getter_AddRefs(newURI));
6047 info->GetWasSchemelessInput(outWasSchemelessInput);
6048 if (newURI) {
6049 info->GetKeywordAsSent(keywordAsSent);
6050 info->GetKeywordProviderName(keywordProviderName);
6051 info->GetPostData(getter_AddRefs(newPostData));
6059 // Now try change the address, e.g. turn http://foo into
6060 // http://www.foo.com, and if that doesn't work try https with
6061 // https://foo and https://www.foo.com.
6063 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6064 // Skip fixup for anything except a normal document load
6065 // operation on the topframe.
6066 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
6068 if (doCreateAlternate) {
6069 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6070 nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
6071 // Only do this if our channel was loaded directly by the user from the
6072 // URL bar or similar (system principal) and not redirected, because we
6073 // shouldn't be guessing things about links from other sites, or a
6074 // post-redirect URI.
6075 doCreateAlternate = principal && principal->IsSystemPrincipal() &&
6076 loadInfo->RedirectChain().IsEmpty();
6078 // Test if keyword lookup produced a new URI or not
6079 if (doCreateAlternate && newURI) {
6080 bool sameURI = false;
6081 url->Equals(newURI, &sameURI);
6082 if (!sameURI) {
6083 // Keyword lookup made a new URI so no need to try
6084 // an alternate one.
6085 doCreateAlternate = false;
6088 if (doCreateAlternate) {
6089 newURI = nullptr;
6090 newPostData = nullptr;
6091 keywordProviderName.Truncate();
6092 keywordAsSent.Truncate();
6093 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6094 if (uriFixup) {
6095 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
6096 uriFixup->GetFixupURIInfo(oldSpec, nsIURIFixup::FIXUP_FLAG_NONE,
6097 getter_AddRefs(fixupInfo));
6098 if (fixupInfo) {
6099 fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
6103 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6104 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6105 // Try HTTPS, since http didn't work
6106 if (SchemeIsHTTP(url)) {
6107 int32_t port = 0;
6108 url->GetPort(&port);
6110 // Fall back to HTTPS only if port is default
6111 if (port == -1) {
6112 newURI = nullptr;
6113 newPostData = nullptr;
6114 Unused << NS_MutateURI(url)
6115 .SetScheme("https"_ns)
6116 .Finalize(getter_AddRefs(newURI));
6121 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try prefixing the domain name
6122 // with www. to see if we can avoid showing the cert error page. For example,
6123 // https://example.com -> https://www.example.com.
6124 if (aStatus ==
6125 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6126 newPostData = nullptr;
6127 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
6130 // Did we make a new URI that is different to the old one? If so
6131 // load it.
6133 if (newURI) {
6134 // Make sure the new URI is different from the old one,
6135 // otherwise there's little point trying to load it again.
6136 bool sameURI = false;
6137 url->Equals(newURI, &sameURI);
6138 if (!sameURI) {
6139 if (aNewPostData) {
6140 newPostData.forget(aNewPostData);
6142 if (aNotifyKeywordSearchLoading) {
6143 // This notification is meant for Firefox Health Report so it
6144 // can increment counts from the search engine
6145 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6147 return newURI.forget();
6151 return nullptr;
6154 nsresult nsDocShell::FilterStatusForErrorPage(
6155 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
6156 bool aIsTopFrame, bool aUseErrorPages, bool aIsInitialDocument,
6157 bool* aSkippedUnknownProtocolNavigation) {
6158 // Errors to be shown only on top-level frames
6159 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6160 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6161 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6162 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6163 aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6164 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6165 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6166 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6167 aStatus == NS_ERROR_MALFORMED_URI ||
6168 aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
6169 aStatus == NS_ERROR_DOM_COOP_FAILED ||
6170 aStatus == NS_ERROR_DOM_COEP_FAILED) &&
6171 (aIsTopFrame || aUseErrorPages)) {
6172 return aStatus;
6175 if (aStatus == NS_ERROR_NET_TIMEOUT ||
6176 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
6177 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6178 aStatus == NS_ERROR_REDIRECT_LOOP ||
6179 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6180 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
6181 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
6182 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
6183 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
6184 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6185 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6186 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6187 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6188 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6189 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
6190 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
6191 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
6192 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
6193 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6194 // Errors to be shown for any frame
6195 return aStatus;
6198 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6199 // For unknown protocols we only display an error if the load is triggered
6200 // by the browser itself, or we're replacing the initial document (and
6201 // nothing else). Showing the error for page-triggered navigations causes
6202 // annoying behavior for users, see bug 1528305.
6204 // We could, maybe, try to detect if this is in response to some user
6205 // interaction (like clicking a link, or something else) and maybe show
6206 // the error page in that case. But this allows for ctrl+clicking and such
6207 // to see the error page.
6208 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6209 if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6210 StaticPrefs::dom_no_unknown_protocol_error_enabled() &&
6211 !aIsInitialDocument) {
6212 if (aSkippedUnknownProtocolNavigation) {
6213 *aSkippedUnknownProtocolNavigation = true;
6215 return NS_OK;
6217 return aStatus;
6220 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6221 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6222 // Caching channels would have to look at their flags to work
6223 // out which error to return. Or we can fix up the error here.
6224 if (!(aLoadType & LOAD_CMD_HISTORY)) {
6225 return NS_ERROR_OFFLINE;
6227 return aStatus;
6230 return NS_OK;
6233 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6234 nsIChannel* aChannel, nsresult aStatus) {
6235 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
6236 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
6237 static_cast<uint32_t>(aStatus)));
6238 if (!aChannel) {
6239 return NS_ERROR_NULL_POINTER;
6242 // Make sure to discard the initial client if we never created the initial
6243 // about:blank document. Do this before possibly returning from the method
6244 // due to an error.
6245 mInitialClientSource.reset();
6247 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6248 if (reporter) {
6249 nsCOMPtr<nsILoadGroup> loadGroup;
6250 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6251 if (loadGroup) {
6252 reporter->FlushConsoleReports(loadGroup);
6253 } else {
6254 reporter->FlushConsoleReports(GetDocument());
6258 nsCOMPtr<nsIURI> url;
6259 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6260 if (NS_FAILED(rv)) {
6261 return rv;
6264 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6265 if (timingChannel) {
6266 TimeStamp channelCreationTime;
6267 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6268 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6269 glean::performance_page::total_content_page_load.AccumulateRawDuration(
6270 TimeStamp::Now() - channelCreationTime);
6274 // Timing is picked up by the window, we don't need it anymore
6275 mTiming = nullptr;
6277 // clean up reload state for meta charset
6278 if (eCharsetReloadRequested == mCharsetReloadState) {
6279 mCharsetReloadState = eCharsetReloadStopOrigional;
6280 } else {
6281 mCharsetReloadState = eCharsetReloadInit;
6284 // Save a pointer to the currently-loading history entry.
6285 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6286 // entry further down in this method.
6287 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6288 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
6291 // one of many safeguards that prevent death and destruction if
6292 // someone is so very very rude as to bring this window down
6293 // during this load handler.
6295 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6297 // Notify the DocumentViewer that the Document has finished loading. This
6298 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6299 if (!mEODForCurrentDocument && mDocumentViewer) {
6300 mIsExecutingOnLoadHandler = true;
6301 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
6302 viewer->LoadComplete(aStatus);
6303 mIsExecutingOnLoadHandler = false;
6305 mEODForCurrentDocument = true;
6307 /* Check if the httpChannel has any cache-control related response headers,
6308 * like no-store, no-cache. If so, update SHEntry so that
6309 * when a user goes back/forward to this page, we appropriately do
6310 * form value restoration or load from server.
6312 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6313 if (!httpChannel) {
6314 // HttpChannel could be hiding underneath a Multipart channel.
6315 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6318 if (httpChannel) {
6319 // figure out if SH should be saving layout state.
6320 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6321 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6322 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
6323 mLSHE->SetSaveLayoutStateFlag(false);
6327 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6328 // onLoadHandler tries to load something different in
6329 // itself or one of its children, we can deal with it appropriately.
6330 if (mLSHE) {
6331 mLSHE->SetLoadType(LOAD_HISTORY);
6333 // Clear the mLSHE reference to indicate document loading is done one
6334 // way or another.
6335 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6337 mActiveEntryIsLoadingFromSessionHistory = false;
6339 // if there's a refresh header in the channel, this method
6340 // will set it up for us.
6341 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
6342 RefreshURIFromQueue();
6344 // Test whether this is the top frame or a subframe
6345 bool isTopFrame = mBrowsingContext->IsTop();
6347 bool hadErrorStatus = false;
6348 // If status code indicates an error it means that DocumentChannel already
6349 // tried to fixup the uri and failed. Throw an error dialog box here.
6350 if (NS_FAILED(aStatus)) {
6351 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6352 // the error event to our embedder, since tests are relying on this.
6353 // The error event is usually fired by the caller of InternalLoad, but
6354 // this particular error can happen asynchronously.
6355 // Bug 1629201 is filed for having much clearer decision making around
6356 // which cases need error events.
6357 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6358 aStatus == NS_ERROR_CONTENT_BLOCKED);
6359 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6361 bool isInitialDocument =
6362 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6363 bool skippedUnknownProtocolNavigation = false;
6364 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
6365 mBrowsingContext->GetUseErrorPages(),
6366 isInitialDocument,
6367 &skippedUnknownProtocolNavigation);
6368 hadErrorStatus = true;
6369 if (NS_FAILED(aStatus)) {
6370 if (!mIsBeingDestroyed) {
6371 DisplayLoadError(aStatus, url, nullptr, aChannel);
6373 } else if (skippedUnknownProtocolNavigation) {
6374 nsTArray<nsString> params;
6375 if (NS_FAILED(
6376 NS_GetSanitizedURIStringFromURI(url, *params.AppendElement()))) {
6377 params.LastElement().AssignLiteral(u"(unknown uri)");
6379 nsContentUtils::ReportToConsole(
6380 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
6381 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
6382 params);
6384 } else {
6385 // If we have a host
6386 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6387 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6390 if (hadErrorStatus) {
6391 // Don't send session store updates if the reason EndPageLoad was called is
6392 // because we are process switching. Sometimes the update takes too long and
6393 // incorrectly overrides session store data from the following load.
6394 return NS_OK;
6396 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
6397 if (WindowContext* windowContext =
6398 mBrowsingContext->GetCurrentWindowContext()) {
6399 using Change = SessionStoreChangeListener::Change;
6401 // We've finished loading the page and now we want to collect all the
6402 // session store state that the page is initialized with.
6403 SessionStoreChangeListener::CollectSessionStoreData(
6404 windowContext,
6405 EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory,
6406 Change::WireFrame));
6410 return NS_OK;
6413 //*****************************************************************************
6414 // nsDocShell: Content Viewer Management
6415 //*****************************************************************************
6417 nsresult nsDocShell::EnsureDocumentViewer() {
6418 if (mDocumentViewer) {
6419 return NS_OK;
6421 if (mIsBeingDestroyed) {
6422 return NS_ERROR_FAILURE;
6425 nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6426 nsCOMPtr<nsIURI> baseURI;
6427 nsIPrincipal* principal = GetInheritedPrincipal(false);
6428 nsIPrincipal* partitionedPrincipal = GetInheritedPrincipal(false, true);
6430 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6431 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6432 if (parentItem) {
6433 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6434 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6435 if (parentElement) {
6436 baseURI = parentElement->GetBaseURI();
6437 cspToInheritForAboutBlank = parentElement->GetCsp();
6442 nsresult rv = CreateAboutBlankDocumentViewer(
6443 principal, partitionedPrincipal, cspToInheritForAboutBlank, baseURI,
6444 /* aIsInitialDocument */ true);
6446 NS_ENSURE_STATE(mDocumentViewer);
6448 if (NS_SUCCEEDED(rv)) {
6449 RefPtr<Document> doc(GetDocument());
6450 MOZ_ASSERT(doc,
6451 "Should have doc if CreateAboutBlankDocumentViewer "
6452 "succeeded!");
6453 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
6455 // Documents created using EnsureDocumentViewer may be transient
6456 // placeholders created by framescripts before content has a
6457 // chance to load. In some cases, window.open(..., "noopener")
6458 // will create such a document and then synchronously tear it
6459 // down, firing a "pagehide" event. Doing so violates our
6460 // assertions about DocGroups. It's easier to silence the
6461 // assertion here than to avoid creating the extra document.
6462 doc->IgnoreDocGroupMismatches();
6465 return rv;
6468 nsresult nsDocShell::CreateAboutBlankDocumentViewer(
6469 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
6470 nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI, bool aIsInitialDocument,
6471 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
6472 bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6473 WindowGlobalChild* aActor) {
6474 RefPtr<Document> blankDoc;
6475 nsCOMPtr<nsIDocumentViewer> viewer;
6476 nsresult rv = NS_ERROR_FAILURE;
6478 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6480 /* mCreatingDocument should never be true at this point. However, it's
6481 a theoretical possibility. We want to know about it and make it stop,
6482 and this sounds like a job for an assertion. */
6483 NS_ASSERTION(!mCreatingDocument,
6484 "infinite(?) loop creating document averted");
6485 if (mCreatingDocument) {
6486 return NS_ERROR_FAILURE;
6489 if (!mBrowsingContext->AncestorsAreCurrent() ||
6490 mBrowsingContext->IsInBFCache()) {
6491 mBrowsingContext->RemoveRootFromBFCacheSync();
6492 return NS_ERROR_NOT_AVAILABLE;
6495 // mDocumentViewer->PermitUnload may release |this| docshell.
6496 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6498 AutoRestore<bool> creatingDocument(mCreatingDocument);
6499 mCreatingDocument = true;
6501 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6502 mItemType != typeChrome) {
6503 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6504 mBrowsingContext->OriginAttributesRef());
6507 // Make sure timing is created. But first record whether we had it
6508 // already, so we don't clobber the timing for an in-progress load.
6509 bool hadTiming = mTiming;
6510 bool toBeReset = MaybeInitTiming();
6511 if (mDocumentViewer) {
6512 if (aCheckPermitUnload) {
6513 // We've got a content viewer already. Make sure the user
6514 // permits us to discard the current document and replace it
6515 // with about:blank. And also ensure we fire the unload events
6516 // in the current document.
6518 // Unload gets fired first for
6519 // document loaded from the session history.
6520 mTiming->NotifyBeforeUnload();
6522 bool okToUnload;
6523 rv = mDocumentViewer->PermitUnload(&okToUnload);
6525 if (NS_SUCCEEDED(rv) && !okToUnload) {
6526 // The user chose not to unload the page, interrupt the load.
6527 MaybeResetInitTiming(toBeReset);
6528 return NS_ERROR_FAILURE;
6530 if (mTiming) {
6531 mTiming->NotifyUnloadAccepted(mCurrentURI);
6535 mSavingOldViewer =
6536 aTryToSaveOldPresentation &&
6537 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
6538 /* aReportBFCacheComboTelemetry */ true);
6540 // Make sure to blow away our mLoadingURI just in case. No loads
6541 // from inside this pagehide.
6542 mLoadingURI = nullptr;
6544 // Stop any in-progress loading, so that we don't accidentally trigger any
6545 // PageShow notifications from Embed() interrupting our loading below.
6546 Stop();
6548 // Notify the current document that it is about to be unloaded!!
6550 // It is important to fire the unload() notification *before* any state
6551 // is changed within the DocShell - otherwise, javascript will get the
6552 // wrong information :-(
6554 (void)FirePageHideNotification(!mSavingOldViewer);
6555 // pagehide notification might destroy this docshell.
6556 if (mIsBeingDestroyed) {
6557 return NS_ERROR_DOCSHELL_DYING;
6561 // Now make sure we don't think we're in the middle of firing unload after
6562 // this point. This will make us fire unload when the about:blank document
6563 // unloads... but that's ok, more or less. Would be nice if it fired load
6564 // too, of course.
6565 mFiredUnloadEvent = false;
6567 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6568 nsContentUtils::FindInternalDocumentViewer("text/html"_ns);
6570 if (docFactory) {
6571 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6572 const uint32_t sandboxFlags =
6573 mBrowsingContext->GetHasLoadedNonInitialDocument()
6574 ? mBrowsingContext->GetSandboxFlags()
6575 : mBrowsingContext->GetInitialSandboxFlags();
6576 // If we're sandboxed, then create a new null principal. We skip
6577 // this if we're being created from WindowGlobalChild, since in
6578 // that case we already have a null principal if required.
6579 // We can't compare againt the BrowsingContext sandbox flag, since
6580 // the value was taken when the load initiated and may have since
6581 // changed.
6582 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6583 if (aPrincipal) {
6584 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6585 } else {
6586 principal = NullPrincipal::Create(GetOriginAttributes());
6588 partitionedPrincipal = principal;
6589 } else {
6590 principal = aPrincipal;
6591 partitionedPrincipal = aPartitionedPrincipal;
6594 // We cannot get the foreign partitioned prinicpal for the initial
6595 // about:blank page. So, we change to check if we need to use the
6596 // partitioned principal for the service worker here.
6597 MaybeCreateInitialClientSource(
6598 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6599 this)
6600 ? partitionedPrincipal
6601 : principal);
6603 // generate (about:blank) document to load
6604 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6605 partitionedPrincipal, this);
6606 if (blankDoc) {
6607 // Hack: manually set the CSP for the new document
6608 // Please create an actual copy of the CSP (do not share the same
6609 // reference) otherwise appending a new policy within the new
6610 // document will be incorrectly propagated to the opening doc.
6611 if (aCSP) {
6612 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6613 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6614 blankDoc->SetCsp(cspToInherit);
6617 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6619 blankDoc->SetEmbedderPolicy(aCOEP);
6621 // Hack: set the base URI manually, since this document never
6622 // got Reset() with a channel.
6623 blankDoc->SetBaseURI(aBaseURI);
6625 // Copy our sandbox flags to the document. These are immutable
6626 // after being set here.
6627 blankDoc->SetSandboxFlags(sandboxFlags);
6629 blankDoc->InitFeaturePolicy();
6631 // create a content viewer for us and the new document
6632 docFactory->CreateInstanceForDocument(
6633 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6634 getter_AddRefs(viewer));
6636 // hook 'em up
6637 if (viewer) {
6638 viewer->SetContainer(this);
6639 rv = Embed(viewer, aActor, true, false, nullptr, mCurrentURI);
6640 NS_ENSURE_SUCCESS(rv, rv);
6642 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6643 /* aFireLocationChange */ true,
6644 /* aIsInitialAboutBlank */ true,
6645 /* aLocationFlags */ 0);
6646 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6651 // The transient about:blank viewer doesn't have a session history entry.
6652 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6654 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6655 // have one before entering this function.
6656 if (!hadTiming) {
6657 mTiming = nullptr;
6658 mBlankTiming = true;
6661 return rv;
6664 NS_IMETHODIMP
6665 nsDocShell::CreateAboutBlankDocumentViewer(nsIPrincipal* aPrincipal,
6666 nsIPrincipal* aPartitionedPrincipal,
6667 nsIContentSecurityPolicy* aCSP) {
6668 return CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6669 nullptr,
6670 /* aIsInitialDocument */ false);
6673 nsresult nsDocShell::CreateDocumentViewerForActor(
6674 WindowGlobalChild* aWindowActor) {
6675 MOZ_ASSERT(aWindowActor);
6677 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6678 // FIXME: We may want to support non-initial documents here.
6679 nsresult rv = CreateAboutBlankDocumentViewer(
6680 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6681 /* aCsp */ nullptr,
6682 /* aBaseURI */ nullptr,
6683 /* aIsInitialDocument */ true,
6684 /* aCOEP */ Nothing(),
6685 /* aTryToSaveOldPresentation */ true,
6686 /* aCheckPermitUnload */ true, aWindowActor);
6687 #ifdef DEBUG
6688 if (NS_SUCCEEDED(rv)) {
6689 RefPtr<Document> doc(GetDocument());
6690 MOZ_ASSERT(
6691 doc,
6692 "Should have a document if CreateAboutBlankDocumentViewer succeeded");
6693 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6694 "New document should be in the same global as our actor");
6695 MOZ_ASSERT(doc->IsInitialDocument(),
6696 "New document should be an initial document");
6698 #endif
6700 return rv;
6703 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6704 nsIRequest* aNewRequest,
6705 Document* aNewDocument,
6706 bool aReportBFCacheComboTelemetry) {
6707 if (!mOSHE) {
6708 return false; // no entry to save into
6711 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6712 "mOSHE cannot be non-null with SHIP");
6713 nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
6714 if (viewer) {
6715 NS_WARNING("mOSHE already has a content viewer!");
6716 return false;
6719 // Only save presentation for "normal" loads and link loads. Anything else
6720 // probably wants to refetch the page, so caching the old presentation
6721 // would be incorrect.
6722 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6723 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6724 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6725 aLoadType != LOAD_ERROR_PAGE) {
6726 return false;
6729 // If the session history entry has the saveLayoutState flag set to false,
6730 // then we should not cache the presentation.
6731 if (!mOSHE->GetSaveLayoutStateFlag()) {
6732 return false;
6735 // If the document is not done loading, don't cache it.
6736 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6737 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6738 ("Blocked due to document still loading"));
6739 return false;
6742 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6743 return false;
6746 // Avoid doing the work of saving the presentation state in the case where
6747 // the content viewer cache is disabled.
6748 if (nsSHistory::GetMaxTotalViewers() == 0) {
6749 return false;
6752 // Don't cache the content viewer if we're in a subframe.
6753 if (mBrowsingContext->GetParent()) {
6754 return false; // this is a subframe load
6757 // If the document does not want its presentation cached, then don't.
6758 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6760 uint32_t bfCacheCombo = 0;
6761 bool canSavePresentation =
6762 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6763 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6764 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6765 auto* browsingContextGroup = mBrowsingContext->Group();
6766 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6767 browsingContextGroup->Toplevels();
6769 for (const auto& browsingContext : topLevelContext) {
6770 if (browsingContext != mBrowsingContext) {
6771 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6772 canSavePresentation = false;
6774 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6775 break;
6780 if (aReportBFCacheComboTelemetry) {
6781 ReportBFCacheComboTelemetry(bfCacheCombo);
6783 return doc && canSavePresentation;
6786 /* static */
6787 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6788 // There are 11 possible reasons to make a request fails to use BFCache
6789 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6790 // the common combinations for reasons which make requests fail to use
6791 // BFCache. These combinations are generated based on some local browsings,
6792 // we need to adjust them when necessary.
6793 enum BFCacheStatusCombo : uint32_t {
6794 BFCACHE_SUCCESS,
6795 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6796 // If both unload and beforeunload listeners are presented, it'll be
6797 // recorded as unload
6798 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6799 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6800 mozilla::dom::BFCacheStatus::REQUEST,
6801 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6802 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6803 mozilla::dom::BFCacheStatus::REQUEST |
6804 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6805 UNLOAD_REQUEST_PEER_MSE =
6806 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6807 mozilla::dom::BFCacheStatus::REQUEST |
6808 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6809 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6810 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6811 mozilla::dom::BFCacheStatus::REQUEST |
6812 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6813 SUSPENDED_UNLOAD_REQUEST_PEER =
6814 mozilla::dom::BFCacheStatus::SUSPENDED |
6815 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6816 mozilla::dom::BFCacheStatus::REQUEST |
6817 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6818 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6819 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6822 // Beforeunload is recorded as a blocker only if it is the only one to block
6823 // bfcache.
6824 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6825 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6827 switch (aCombo) {
6828 case BFCACHE_SUCCESS:
6829 Telemetry::AccumulateCategorical(
6830 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6831 break;
6832 case NOT_ONLY_TOPLEVEL:
6833 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6834 Telemetry::AccumulateCategorical(
6835 Telemetry::LABELS_BFCACHE_COMBO::Other);
6836 break;
6838 Telemetry::AccumulateCategorical(
6839 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6840 Telemetry::AccumulateCategorical(
6841 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6842 break;
6843 case UNLOAD:
6844 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6845 break;
6846 case BEFOREUNLOAD:
6847 Telemetry::AccumulateCategorical(
6848 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6849 break;
6850 case UNLOAD_REQUEST:
6851 Telemetry::AccumulateCategorical(
6852 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6853 break;
6854 case REQUEST:
6855 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6856 break;
6857 case UNLOAD_REQUEST_PEER:
6858 Telemetry::AccumulateCategorical(
6859 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6860 break;
6861 case UNLOAD_REQUEST_PEER_MSE:
6862 Telemetry::AccumulateCategorical(
6863 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6864 break;
6865 case UNLOAD_REQUEST_MSE:
6866 Telemetry::AccumulateCategorical(
6867 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6868 break;
6869 case SUSPENDED_UNLOAD_REQUEST_PEER:
6870 Telemetry::AccumulateCategorical(
6871 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6872 break;
6873 case REMOTE_SUBFRAMES:
6874 Telemetry::AccumulateCategorical(
6875 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6876 break;
6877 default:
6878 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6879 break;
6883 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6884 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6885 MOZ_ASSERT(!mIsBeingDestroyed);
6887 NS_ASSERTION(!mEditorData,
6888 "Why reattach an editor when we already have one?");
6889 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6890 "Reattaching when there's not a detached editor.");
6892 if (mEditorData || !aSHEntry) {
6893 return;
6896 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6897 if (mEditorData) {
6898 #ifdef DEBUG
6899 nsresult rv =
6900 #endif
6901 mEditorData->ReattachToWindow(this);
6902 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
6906 void nsDocShell::DetachEditorFromWindow() {
6907 if (!mEditorData || mEditorData->WaitingForLoad()) {
6908 // If there's nothing to detach, or if the editor data is actually set
6909 // up for the _new_ page that's coming in, don't detach.
6910 return;
6913 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6914 "Detaching editor when it's already detached.");
6916 nsresult res = mEditorData->DetachFromWindow();
6917 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6919 if (NS_SUCCEEDED(res)) {
6920 // Make mOSHE hold the owning ref to the editor data.
6921 if (mOSHE) {
6922 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
6923 "We should not set the editor data again once after we "
6924 "detached the editor data during destroying this docshell");
6925 mOSHE->SetEditorData(mEditorData.release());
6926 } else {
6927 mEditorData = nullptr;
6931 #ifdef DEBUG
6933 bool isEditable;
6934 GetEditable(&isEditable);
6935 NS_ASSERTION(!isEditable,
6936 "Window is still editable after detaching editor.");
6938 #endif // DEBUG
6941 nsresult nsDocShell::CaptureState() {
6942 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6944 if (!mOSHE || mOSHE == mLSHE) {
6945 // No entry to save into, or we're replacing the existing entry.
6946 return NS_ERROR_FAILURE;
6949 if (!mScriptGlobal) {
6950 return NS_ERROR_FAILURE;
6953 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
6954 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
6956 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
6957 nsAutoCString spec;
6958 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
6959 if (uri) {
6960 uri->GetSpec(spec);
6962 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6963 ("Saving presentation into session history, URI: %s", spec.get()));
6966 mOSHE->SetWindowState(windowState);
6968 // Suspend refresh URIs and save off the timer queue
6969 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6971 // Capture the current content viewer bounds.
6972 if (mDocumentViewer) {
6973 nsIntRect bounds;
6974 mDocumentViewer->GetBounds(bounds);
6975 mOSHE->SetViewerBounds(bounds);
6978 // Capture the docshell hierarchy.
6979 mOSHE->ClearChildShells();
6981 uint32_t childCount = mChildList.Length();
6982 for (uint32_t i = 0; i < childCount; ++i) {
6983 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6984 NS_ASSERTION(childShell, "null child shell");
6986 mOSHE->AddChildShell(childShell);
6989 return NS_OK;
6992 NS_IMETHODIMP
6993 nsDocShell::RestorePresentationEvent::Run() {
6994 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6996 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
6997 NS_WARNING("RestoreFromHistory failed");
6999 return NS_OK;
7002 NS_IMETHODIMP
7003 nsDocShell::BeginRestore(nsIDocumentViewer* aDocumentViewer, bool aTop) {
7004 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7006 nsresult rv;
7007 if (!aDocumentViewer) {
7008 rv = EnsureDocumentViewer();
7009 NS_ENSURE_SUCCESS(rv, rv);
7011 aDocumentViewer = mDocumentViewer;
7014 // Dispatch events for restoring the presentation. We try to simulate
7015 // the progress notifications loading the document would cause, so we add
7016 // the document's channel to the loadgroup to initiate stateChange
7017 // notifications.
7019 RefPtr<Document> doc = aDocumentViewer->GetDocument();
7020 if (doc) {
7021 nsIChannel* channel = doc->GetChannel();
7022 if (channel) {
7023 mEODForCurrentDocument = false;
7024 mIsRestoringDocument = true;
7025 mLoadGroup->AddRequest(channel, nullptr);
7026 mIsRestoringDocument = false;
7030 if (!aTop) {
7031 // This point corresponds to us having gotten OnStartRequest or
7032 // STATE_START, so do the same thing that CreateDocumentViewer does at
7033 // this point to ensure that unload/pagehide events for this document
7034 // will fire when it's unloaded again.
7035 mFiredUnloadEvent = false;
7037 // For non-top frames, there is no notion of making sure that the
7038 // previous document is in the domwindow when STATE_START notifications
7039 // happen. We can just call BeginRestore for all of the child shells
7040 // now.
7041 rv = BeginRestoreChildren();
7042 NS_ENSURE_SUCCESS(rv, rv);
7045 return NS_OK;
7048 nsresult nsDocShell::BeginRestoreChildren() {
7049 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7051 for (auto* childDocLoader : mChildList.ForwardRange()) {
7052 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7053 if (child) {
7054 nsresult rv = child->BeginRestore(nullptr, false);
7055 NS_ENSURE_SUCCESS(rv, rv);
7058 return NS_OK;
7061 NS_IMETHODIMP
7062 nsDocShell::FinishRestore() {
7063 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7065 // First we call finishRestore() on our children. In the simulated load,
7066 // all of the child frames finish loading before the main document.
7068 for (auto* childDocLoader : mChildList.ForwardRange()) {
7069 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7070 if (child) {
7071 child->FinishRestore();
7075 if (mOSHE && mOSHE->HasDetachedEditor()) {
7076 ReattachEditorToWindow(mOSHE);
7079 RefPtr<Document> doc = GetDocument();
7080 if (doc) {
7081 // Finally, we remove the request from the loadgroup. This will
7082 // cause onStateChange(STATE_STOP) to fire, which will fire the
7083 // pageshow event to the chrome.
7085 nsIChannel* channel = doc->GetChannel();
7086 if (channel) {
7087 mIsRestoringDocument = true;
7088 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7089 mIsRestoringDocument = false;
7093 return NS_OK;
7096 NS_IMETHODIMP
7097 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7098 *aRestoring = mIsRestoringDocument;
7099 return NS_OK;
7102 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7103 bool* aRestoring) {
7104 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7105 MOZ_ASSERT(!mIsBeingDestroyed);
7107 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7108 "RestorePresentation should only be called for history loads");
7110 nsCOMPtr<nsIDocumentViewer> viewer = aSHEntry->GetDocumentViewer();
7112 nsAutoCString spec;
7113 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7114 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7115 if (uri) {
7116 uri->GetSpec(spec);
7120 *aRestoring = false;
7122 if (!viewer) {
7123 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7124 ("no saved presentation for uri: %s", spec.get()));
7125 return NS_OK;
7128 // We need to make sure the content viewer's container is this docshell.
7129 // In subframe navigation, it's possible for the docshell that the
7130 // content viewer was originally loaded into to be replaced with a
7131 // different one. We don't currently support restoring the presentation
7132 // in that case.
7134 nsCOMPtr<nsIDocShell> container;
7135 viewer->GetContainer(getter_AddRefs(container));
7136 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7137 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7138 ("No valid container, clearing presentation"));
7139 aSHEntry->SetDocumentViewer(nullptr);
7140 return NS_ERROR_FAILURE;
7143 NS_ASSERTION(mDocumentViewer != viewer, "Restoring existing presentation");
7145 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7146 ("restoring presentation from session history: %s", spec.get()));
7148 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7150 // Post an event that will remove the request after we've returned
7151 // to the event loop. This mimics the way it is called by nsIChannel
7152 // implementations.
7154 // Revoke any pending restore (just in case).
7155 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7156 "should only have one RestorePresentationEvent");
7157 mRestorePresentationEvent.Revoke();
7159 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7160 nsresult rv = Dispatch(do_AddRef(evt));
7161 if (NS_SUCCEEDED(rv)) {
7162 mRestorePresentationEvent = evt.get();
7163 // The rest of the restore processing will happen on our event
7164 // callback.
7165 *aRestoring = true;
7168 return rv;
7171 namespace {
7172 class MOZ_STACK_CLASS PresentationEventForgetter {
7173 public:
7174 explicit PresentationEventForgetter(
7175 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7176 aRestorePresentationEvent)
7177 : mRestorePresentationEvent(aRestorePresentationEvent),
7178 mEvent(aRestorePresentationEvent.get()) {}
7180 ~PresentationEventForgetter() { Forget(); }
7182 void Forget() {
7183 if (mRestorePresentationEvent.get() == mEvent) {
7184 mRestorePresentationEvent.Forget();
7185 mEvent = nullptr;
7189 private:
7190 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7191 mRestorePresentationEvent;
7192 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7195 } // namespace
7197 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7198 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7201 nsresult nsDocShell::RestoreFromHistory() {
7202 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7203 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7204 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7206 // This section of code follows the same ordering as CreateDocumentViewer.
7207 if (!mLSHE) {
7208 return NS_ERROR_FAILURE;
7211 nsCOMPtr<nsIDocumentViewer> viewer = mLSHE->GetDocumentViewer();
7212 if (!viewer) {
7213 return NS_ERROR_FAILURE;
7216 if (mSavingOldViewer) {
7217 // We determined that it was safe to cache the document presentation
7218 // at the time we initiated the new load. We need to check whether
7219 // it's still safe to do so, since there may have been DOM mutations
7220 // or new requests initiated.
7221 RefPtr<Document> doc = viewer->GetDocument();
7222 nsIRequest* request = nullptr;
7223 if (doc) {
7224 request = doc->GetChannel();
7226 mSavingOldViewer = CanSavePresentation(
7227 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
7230 // Protect against mLSHE going away via a load triggered from
7231 // pagehide or unload.
7232 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7234 // Make sure to blow away our mLoadingURI just in case. No loads
7235 // from inside this pagehide.
7236 mLoadingURI = nullptr;
7238 // Notify the old content viewer that it's being hidden.
7239 FirePageHideNotification(!mSavingOldViewer);
7240 // pagehide notification might destroy this docshell.
7241 if (mIsBeingDestroyed) {
7242 return NS_ERROR_DOCSHELL_DYING;
7245 // If mLSHE was changed as a result of the pagehide event, then
7246 // something else was loaded. Don't finish restoring.
7247 if (mLSHE != origLSHE) {
7248 return NS_OK;
7251 // Add the request to our load group. We do this before swapping out
7252 // the content viewers so that consumers of STATE_START can access
7253 // the old document. We only deal with the toplevel load at this time --
7254 // to be consistent with normal document loading, subframes cannot start
7255 // loading until after data arrives, which is after STATE_START completes.
7257 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7258 mRestorePresentationEvent.get();
7259 Stop();
7260 // Make sure we're still restoring the same presentation.
7261 // If we aren't, docshell is in process doing another load already.
7262 NS_ENSURE_STATE(currentPresentationRestoration ==
7263 mRestorePresentationEvent.get());
7264 BeginRestore(viewer, true);
7265 NS_ENSURE_STATE(currentPresentationRestoration ==
7266 mRestorePresentationEvent.get());
7267 forgetter.Forget();
7269 // Set mFiredUnloadEvent = false so that the unload handler for the
7270 // *new* document will fire.
7271 mFiredUnloadEvent = false;
7273 mURIResultedInDocument = true;
7274 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7275 if (rootSH) {
7276 mPreviousEntryIndex = rootSH->Index();
7277 rootSH->LegacySHistory()->UpdateIndex();
7278 mLoadedEntryIndex = rootSH->Index();
7279 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7280 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7281 mLoadedEntryIndex));
7284 // Rather than call Embed(), we will retrieve the viewer from the session
7285 // history entry and swap it in.
7286 // XXX can we refactor this so that we can just call Embed()?
7287 PersistLayoutHistoryState();
7288 nsresult rv;
7289 if (mDocumentViewer) {
7290 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7291 if (mOSHE) {
7292 mOSHE->SyncPresentationState();
7294 mSavingOldViewer = false;
7298 mSavedRefreshURIList = nullptr;
7300 // In cases where we use a transient about:blank viewer between loads,
7301 // we never show the transient viewer, so _its_ previous viewer is never
7302 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7303 // before we grab the root view sibling, so that we don't grab a view
7304 // that's about to go away.
7306 if (mDocumentViewer) {
7307 // Make sure to hold a strong ref to previousViewer here while we
7308 // drop the reference to it from mDocumentViewer.
7309 nsCOMPtr<nsIDocumentViewer> previousViewer =
7310 mDocumentViewer->GetPreviousViewer();
7311 if (previousViewer) {
7312 mDocumentViewer->SetPreviousViewer(nullptr);
7313 previousViewer->Destroy();
7317 // Save off the root view's parent and sibling so that we can insert the
7318 // new content viewer's root view at the same position. Also save the
7319 // bounds of the root view's widget.
7321 nsView* rootViewSibling = nullptr;
7322 nsView* rootViewParent = nullptr;
7323 nsIntRect newBounds(0, 0, 0, 0);
7325 PresShell* oldPresShell = GetPresShell();
7326 if (oldPresShell) {
7327 nsViewManager* vm = oldPresShell->GetViewManager();
7328 if (vm) {
7329 nsView* oldRootView = vm->GetRootView();
7331 if (oldRootView) {
7332 rootViewSibling = oldRootView->GetNextSibling();
7333 rootViewParent = oldRootView->GetParent();
7335 mDocumentViewer->GetBounds(newBounds);
7340 nsCOMPtr<nsIContent> container;
7341 RefPtr<Document> sibling;
7342 if (rootViewParent && rootViewParent->GetParent()) {
7343 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7344 container = frame ? frame->GetContent() : nullptr;
7346 if (rootViewSibling) {
7347 nsIFrame* frame = rootViewSibling->GetFrame();
7348 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7351 // Transfer ownership to mDocumentViewer. By ensuring that either the
7352 // docshell or the session history, but not both, have references to the
7353 // content viewer, we prevent the viewer from being torn down after
7354 // Destroy() is called.
7356 if (mDocumentViewer) {
7357 mDocumentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7358 viewer->SetPreviousViewer(mDocumentViewer);
7360 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
7361 // We don't plan to save a viewer in mOSHE; tell it to drop
7362 // any other state it's holding.
7363 mOSHE->SyncPresentationState();
7366 // Order the mDocumentViewer setup just like Embed does.
7367 mDocumentViewer = nullptr;
7369 // Now that we're about to switch documents, forget all of our children.
7370 // Note that we cached them as needed up in CaptureState above.
7371 DestroyChildren();
7373 mDocumentViewer.swap(viewer);
7375 // Grab all of the related presentation from the SHEntry now.
7376 // Clearing the viewer from the SHEntry will clear all of this state.
7377 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7378 mLSHE->SetWindowState(nullptr);
7380 bool sticky = mLSHE->GetSticky();
7382 RefPtr<Document> document = mDocumentViewer->GetDocument();
7384 nsCOMArray<nsIDocShellTreeItem> childShells;
7385 int32_t i = 0;
7386 nsCOMPtr<nsIDocShellTreeItem> child;
7387 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7388 child) {
7389 childShells.AppendObject(child);
7392 // get the previous content viewer size
7393 nsIntRect oldBounds(0, 0, 0, 0);
7394 mLSHE->GetViewerBounds(oldBounds);
7396 // Restore the refresh URI list. The refresh timers will be restarted
7397 // when EndPageLoad() is called.
7398 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7400 // Reattach to the window object.
7401 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7402 rv = mDocumentViewer->Open(windowState, mLSHE);
7403 mIsRestoringDocument = false;
7405 // Hack to keep nsDocShellEditorData alive across the
7406 // SetContentViewer(nullptr) call below.
7407 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7409 // Now remove it from the cached presentation.
7410 mLSHE->SetDocumentViewer(nullptr);
7411 mEODForCurrentDocument = false;
7413 mLSHE->SetEditorData(data.release());
7415 #ifdef DEBUG
7417 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7418 nsCOMPtr<nsIDocShellTreeItem> childShell;
7419 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7420 NS_ASSERTION(!refreshURIs && !childShell,
7421 "SHEntry should have cleared presentation state");
7423 #endif
7425 // Restore the sticky state of the viewer. The viewer has set this state
7426 // on the history entry in Destroy() just before marking itself non-sticky,
7427 // to avoid teardown of the presentation.
7428 mDocumentViewer->SetSticky(sticky);
7430 NS_ENSURE_SUCCESS(rv, rv);
7432 // mLSHE is now our currently-loaded document.
7433 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7435 // We aren't going to restore any items from the LayoutHistoryState,
7436 // but we don't want them to stay around in case the page is reloaded.
7437 SetLayoutHistoryState(nullptr);
7439 // This is the end of our Embed() replacement
7441 mSavingOldViewer = false;
7442 mEODForCurrentDocument = false;
7444 if (document) {
7445 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7446 if (parent) {
7447 RefPtr<Document> d = parent->GetDocument();
7448 if (d) {
7449 if (d->EventHandlingSuppressed()) {
7450 document->SuppressEventHandling(d->EventHandlingSuppressed());
7455 // Use the uri from the mLSHE we had when we entered this function
7456 // (which need not match the document's URI if anchors are involved),
7457 // since that's the history entry we're loading. Note that if we use
7458 // origLSHE we don't have to worry about whether the entry in question
7459 // is still mLSHE or whether it's now mOSHE.
7460 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7461 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7462 /* aIsInitialAboutBlank */ false,
7463 /* aLocationFlags */ 0);
7466 // This is the end of our CreateDocumentViewer() replacement.
7467 // Now we simulate a load. First, we restore the state of the javascript
7468 // window object.
7469 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7470 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7472 // Now, dispatch a title change event which would happen as the
7473 // <head> is parsed.
7474 document->NotifyPossibleTitleChange(false);
7476 // Now we simulate appending child docshells for subframes.
7477 for (i = 0; i < childShells.Count(); ++i) {
7478 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7479 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7481 // Make sure to not clobber the state of the child. Since AddChild
7482 // always clobbers it, save it off first.
7483 bool allowRedirects;
7484 childShell->GetAllowMetaRedirects(&allowRedirects);
7486 bool allowSubframes;
7487 childShell->GetAllowSubframes(&allowSubframes);
7489 bool allowImages;
7490 childShell->GetAllowImages(&allowImages);
7492 bool allowMedia = childShell->GetAllowMedia();
7494 bool allowDNSPrefetch;
7495 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7497 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7498 bool allowContentRetargetingOnChildren =
7499 childShell->GetAllowContentRetargetingOnChildren();
7501 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7502 // the child inherits our state. Among other things, this means that the
7503 // child inherits our mPrivateBrowsingId, which is what we want.
7504 AddChild(childItem);
7506 childShell->SetAllowMetaRedirects(allowRedirects);
7507 childShell->SetAllowSubframes(allowSubframes);
7508 childShell->SetAllowImages(allowImages);
7509 childShell->SetAllowMedia(allowMedia);
7510 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7511 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7512 childShell->SetAllowContentRetargetingOnChildren(
7513 allowContentRetargetingOnChildren);
7515 rv = childShell->BeginRestore(nullptr, false);
7516 NS_ENSURE_SUCCESS(rv, rv);
7519 // Make sure to restore the window state after adding the child shells back
7520 // to the tree. This is necessary for Thaw() and Resume() to propagate
7521 // properly.
7522 rv = privWin->RestoreWindowState(windowState);
7523 NS_ENSURE_SUCCESS(rv, rv);
7525 RefPtr<PresShell> presShell = GetPresShell();
7527 // We may be displayed on a different monitor (or in a different
7528 // HiDPI mode) than when we got into the history list. So we need
7529 // to check if this has happened. See bug 838239.
7531 // Because the prescontext normally handles resolution changes via
7532 // a runnable (see nsPresContext::UIResolutionChanged), its device
7533 // context won't be -immediately- updated as a result of calling
7534 // presShell->BackingScaleFactorChanged().
7536 // But we depend on that device context when adjusting the view size
7537 // via mDocumentViewer->SetBounds(newBounds) below. So we need to
7538 // explicitly tell it to check for changed resolution here.
7539 if (presShell) {
7540 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7541 if (pc->DeviceContext()->CheckDPIChange()) {
7542 presShell->BackingScaleFactorChanged();
7544 // Recompute zoom and text-zoom and such.
7545 pc->RecomputeBrowsingContextDependentData();
7548 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7549 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7551 // Insert the new root view at the correct location in the view tree.
7552 if (container) {
7553 nsSubDocumentFrame* subDocFrame =
7554 do_QueryFrame(container->GetPrimaryFrame());
7555 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7556 } else {
7557 rootViewParent = nullptr;
7559 if (sibling && sibling->GetPresShell() &&
7560 sibling->GetPresShell()->GetViewManager()) {
7561 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7562 } else {
7563 rootViewSibling = nullptr;
7565 if (rootViewParent && newRootView &&
7566 newRootView->GetParent() != rootViewParent) {
7567 nsViewManager* parentVM = rootViewParent->GetViewManager();
7568 if (parentVM) {
7569 // InsertChild(parent, child, sib, true) inserts the child after
7570 // sib in content order, which is before sib in view order. BUT
7571 // when sib is null it inserts at the end of the the document
7572 // order, i.e., first in view order. But when oldRootSibling is
7573 // null, the old root as at the end of the view list --- last in
7574 // content order --- and we want to call InsertChild(parent, child,
7575 // nullptr, false) in that case.
7576 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7577 rootViewSibling ? true : false);
7579 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7580 "error in InsertChild");
7584 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7586 // If parent is suspended, increase suspension count.
7587 // This can't be done as early as event suppression since this
7588 // depends on docshell tree.
7589 privWinInner->SyncStateFromParentWindow();
7591 // Now that all of the child docshells have been put into place, we can
7592 // restart the timers for the window and all of the child frames.
7593 privWinInner->Resume();
7595 // Now that we have found the inner window of the page restored
7596 // from the history, we have to make sure that
7597 // performance.navigation.type is 2.
7598 Performance* performance = privWinInner->GetPerformance();
7599 if (performance) {
7600 performance->GetDOMTiming()->NotifyRestoreStart();
7603 // Restore the refresh URI list. The refresh timers will be restarted
7604 // when EndPageLoad() is called.
7605 mRefreshURIList = refreshURIList;
7607 // Meta-refresh timers have been restarted for this shell, but not
7608 // for our children. Walk the child shells and restart their timers.
7609 for (auto* childDocLoader : mChildList.ForwardRange()) {
7610 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7611 if (child) {
7612 child->ResumeRefreshURIs();
7616 // Make sure this presentation is the same size as the previous
7617 // presentation. If this is not the same size we showed it at last time,
7618 // then we need to resize the widget.
7620 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7621 // presentation had the infobar visible, then we will resize the new
7622 // presentation to that smaller size. However, firing the locationchanged
7623 // event will hide the infobar, which will immediately resize the window
7624 // back to the larger size. A future optimization might be to restore
7625 // the presentation at the "wrong" size, then fire the locationchanged
7626 // event and check whether the docshell's new size is the same as the
7627 // cached viewer size (skipping the resize if they are equal).
7629 if (newRootView) {
7630 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7631 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7632 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7633 newBounds.width, newBounds.height));
7634 mDocumentViewer->SetBounds(newBounds);
7635 } else {
7636 nsIScrollableFrame* rootScrollFrame =
7637 presShell->GetRootScrollFrameAsScrollable();
7638 if (rootScrollFrame) {
7639 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7644 // The FinishRestore call below can kill these, null them out so we don't
7645 // have invalid pointer lying around.
7646 newRootView = rootViewSibling = rootViewParent = nullptr;
7647 newVM = nullptr;
7649 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7650 // update it.
7651 if (oldPresShell && presShell &&
7652 presShell->IsUnderHiddenEmbedderElement() !=
7653 oldPresShell->IsUnderHiddenEmbedderElement()) {
7654 presShell->SetIsUnderHiddenEmbedderElement(
7655 oldPresShell->IsUnderHiddenEmbedderElement());
7658 // Simulate the completion of the load.
7659 nsDocShell::FinishRestore();
7661 // Restart plugins, and paint the content.
7662 if (presShell) {
7663 presShell->Thaw();
7666 return privWin->FireDelayedDOMEvents(true);
7669 nsresult nsDocShell::CreateDocumentViewer(const nsACString& aContentType,
7670 nsIRequest* aRequest,
7671 nsIStreamListener** aContentHandler) {
7672 *aContentHandler = nullptr;
7674 if (!mTreeOwner || mIsBeingDestroyed) {
7675 // If we don't have a tree owner, then we're in the process of being
7676 // destroyed. Rather than continue trying to load something, just give up.
7677 return NS_ERROR_DOCSHELL_DYING;
7680 if (!mBrowsingContext->AncestorsAreCurrent() ||
7681 mBrowsingContext->IsInBFCache()) {
7682 mBrowsingContext->RemoveRootFromBFCacheSync();
7683 return NS_ERROR_NOT_AVAILABLE;
7686 // Can we check the content type of the current content viewer
7687 // and reuse it without destroying it and re-creating it?
7689 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7691 // Instantiate the content viewer object
7692 nsCOMPtr<nsIDocumentViewer> viewer;
7693 nsresult rv = NewDocumentViewerObj(aContentType, aRequest, mLoadGroup,
7694 aContentHandler, getter_AddRefs(viewer));
7696 if (NS_FAILED(rv)) {
7697 return rv;
7700 // Notify the current document that it is about to be unloaded!!
7702 // It is important to fire the unload() notification *before* any state
7703 // is changed within the DocShell - otherwise, javascript will get the
7704 // wrong information :-(
7707 if (mSavingOldViewer) {
7708 // We determined that it was safe to cache the document presentation
7709 // at the time we initiated the new load. We need to check whether
7710 // it's still safe to do so, since there may have been DOM mutations
7711 // or new requests initiated.
7712 RefPtr<Document> doc = viewer->GetDocument();
7713 mSavingOldViewer = CanSavePresentation(
7714 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
7717 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7719 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7720 if (aOpenedChannel) {
7721 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7724 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7725 // it before we do call Embed.
7726 nsCOMPtr<nsIURI> previousURI = mCurrentURI;
7728 FirePageHideNotification(!mSavingOldViewer);
7729 if (mIsBeingDestroyed) {
7730 // Force to stop the newly created orphaned viewer.
7731 viewer->Stop();
7732 return NS_ERROR_DOCSHELL_DYING;
7734 mLoadingURI = nullptr;
7736 // Set mFiredUnloadEvent = false so that the unload handler for the
7737 // *new* document will fire.
7738 mFiredUnloadEvent = false;
7740 // we've created a new document so go ahead and call
7741 // OnNewURI(), but don't fire OnLocationChange()
7742 // notifications before we've called Embed(). See bug 284993.
7743 mURIResultedInDocument = true;
7744 bool errorOnLocationChangeNeeded = false;
7745 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7746 nsCOMPtr<nsIURI> failedURI;
7748 if (mLoadType == LOAD_ERROR_PAGE) {
7749 // We need to set the SH entry and our current URI here and not
7750 // at the moment we load the page. We want the same behavior
7751 // of Stop() as for a normal page load. See bug 514232 for details.
7753 // Revert mLoadType to load type to state the page load failed,
7754 // following function calls need it.
7755 mLoadType = mFailedLoadType;
7757 Document* doc = viewer->GetDocument();
7758 if (doc) {
7759 doc->SetFailedChannel(failedChannel);
7762 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7763 if (failedChannel) {
7764 // Make sure we have a URI to set currentURI.
7765 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7766 } else {
7767 // if there is no failed channel we have to explicitly provide
7768 // a triggeringPrincipal for the history entry.
7769 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7772 if (!failedURI) {
7773 failedURI = mFailedURI;
7775 if (!failedURI) {
7776 // We need a URI object to store a session history entry, so make up a URI
7777 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7780 // When we don't have failedURI, something wrong will happen. See
7781 // bug 291876.
7782 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7784 mFailedChannel = nullptr;
7785 mFailedURI = nullptr;
7787 // Create an shistory entry for the old load.
7788 if (failedURI) {
7789 errorOnLocationChangeNeeded =
7790 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7791 nullptr, nullptr, false, false);
7794 // Be sure to have a correct mLSHE, it may have been cleared by
7795 // EndPageLoad. See bug 302115.
7796 ChildSHistory* shistory = GetSessionHistory();
7797 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7798 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7799 if (idx == -1) {
7800 idx = shistory->Index();
7802 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7805 mLoadType = LOAD_ERROR_PAGE;
7808 nsCOMPtr<nsIURI> finalURI;
7809 // If this a redirect, use the final url (uri)
7810 // else use the original url
7812 // Note that this should match what documents do (see Document::Reset).
7813 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7815 bool onLocationChangeNeeded = false;
7816 if (finalURI) {
7817 // Pass false for aCloneSHChildren, since we're loading a new page here.
7818 onLocationChangeNeeded = OnNewURI(finalURI, aOpenedChannel, nullptr,
7819 nullptr, nullptr, nullptr, true, false);
7822 // let's try resetting the load group if we need to...
7823 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7824 NS_ENSURE_SUCCESS(
7825 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7826 NS_ERROR_FAILURE);
7828 if (currentLoadGroup != mLoadGroup) {
7829 nsLoadFlags loadFlags = 0;
7831 // Cancel any URIs that are currently loading...
7832 // XXX: Need to do this eventually Stop();
7834 // Retarget the document to this loadgroup...
7836 /* First attach the channel to the right loadgroup
7837 * and then remove from the old loadgroup. This
7838 * puts the notifications in the right order and
7839 * we don't null-out mLSHE in OnStateChange() for
7840 * all redirected urls
7842 aOpenedChannel->SetLoadGroup(mLoadGroup);
7844 // Mark the channel as being a document URI...
7845 aOpenedChannel->GetLoadFlags(&loadFlags);
7846 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7847 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7848 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7849 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7852 aOpenedChannel->SetLoadFlags(loadFlags);
7854 mLoadGroup->AddRequest(aRequest, nullptr);
7855 if (currentLoadGroup) {
7856 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7859 // Update the notification callbacks, so that progress and
7860 // status information are sent to the right docshell...
7861 aOpenedChannel->SetNotificationCallbacks(this);
7864 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
7865 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
7866 aOpenedChannel, previousURI),
7867 NS_ERROR_FAILURE);
7869 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
7870 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
7873 mSavedRefreshURIList = nullptr;
7874 mSavingOldViewer = false;
7875 mEODForCurrentDocument = false;
7877 // if this document is part of a multipart document,
7878 // the ID can be used to distinguish it from the other parts.
7879 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
7880 if (multiPartChannel) {
7881 if (PresShell* presShell = GetPresShell()) {
7882 if (Document* doc = presShell->GetDocument()) {
7883 uint32_t partID;
7884 multiPartChannel->GetPartID(&partID);
7885 doc->SetPartID(partID);
7890 if (errorOnLocationChangeNeeded) {
7891 FireOnLocationChange(this, failedChannel, failedURI,
7892 LOCATION_CHANGE_ERROR_PAGE);
7893 } else if (onLocationChangeNeeded) {
7894 uint32_t locationFlags =
7895 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
7896 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
7899 return NS_OK;
7902 nsresult nsDocShell::NewDocumentViewerObj(const nsACString& aContentType,
7903 nsIRequest* aRequest,
7904 nsILoadGroup* aLoadGroup,
7905 nsIStreamListener** aContentHandler,
7906 nsIDocumentViewer** aViewer) {
7907 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7909 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
7910 nsContentUtils::FindInternalDocumentViewer(aContentType);
7911 if (!docLoaderFactory) {
7912 return NS_ERROR_FAILURE;
7915 // Now create an instance of the content viewer nsLayoutDLF makes the
7916 // determination if it should be a "view-source" instead of "view"
7917 nsresult rv = docLoaderFactory->CreateInstance(
7918 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
7919 aContentHandler, aViewer);
7920 NS_ENSURE_SUCCESS(rv, rv);
7922 (*aViewer)->SetContainer(this);
7923 return NS_OK;
7926 nsresult nsDocShell::SetupNewViewer(nsIDocumentViewer* aNewViewer,
7927 WindowGlobalChild* aWindowActor) {
7928 MOZ_ASSERT(!mIsBeingDestroyed);
7931 // Copy content viewer state from previous or parent content viewer.
7933 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7935 // Do NOT to maintain a reference to the old content viewer outside
7936 // of this "copying" block, or it will not be destroyed until the end of
7937 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7939 // In this block of code, if we get an error result, we return it
7940 // but if we get a null pointer, that's perfectly legal for parent
7941 // and parentContentViewer.
7944 int32_t x = 0;
7945 int32_t y = 0;
7946 int32_t cx = 0;
7947 int32_t cy = 0;
7949 // This will get the size from the current content viewer or from the
7950 // Init settings
7951 DoGetPositionAndSize(&x, &y, &cx, &cy);
7953 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7954 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
7955 NS_ERROR_FAILURE);
7956 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7958 const Encoding* reloadEncoding = nullptr;
7959 int32_t reloadEncodingSource = kCharsetUninitialized;
7960 // |newMUDV| also serves as a flag to set the data from the above vars
7961 nsCOMPtr<nsIDocumentViewer> newViewer;
7963 if (mDocumentViewer || parent) {
7964 nsCOMPtr<nsIDocumentViewer> oldViewer;
7965 if (mDocumentViewer) {
7966 // Get any interesting state from old content viewer
7967 // XXX: it would be far better to just reuse the document viewer ,
7968 // since we know we're just displaying the same document as before
7969 oldViewer = mDocumentViewer;
7971 // Tell the old content viewer to hibernate in session history when
7972 // it is destroyed.
7974 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7975 if (mOSHE) {
7976 mOSHE->SyncPresentationState();
7978 mSavingOldViewer = false;
7980 } else {
7981 // No old content viewer, so get state from parent's content viewer
7982 parent->GetDocViewer(getter_AddRefs(oldViewer));
7985 if (oldViewer) {
7986 newViewer = aNewViewer;
7987 if (newViewer) {
7988 reloadEncoding =
7989 oldViewer->GetReloadEncodingAndSource(&reloadEncodingSource);
7994 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
7995 bool isUnderHiddenEmbedderElement = false;
7996 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
7997 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
7998 if (viewer) {
7999 // Stop any activity that may be happening in the old document before
8000 // releasing it...
8001 viewer->Stop();
8003 // Try to extract the canvas background color from the old
8004 // presentation shell, so we can use it for the next document.
8005 if (PresShell* presShell = viewer->GetPresShell()) {
8006 bgcolor = presShell->GetCanvasBackground();
8007 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
8010 viewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8011 aNewViewer->SetPreviousViewer(viewer);
8013 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
8014 // We don't plan to save a viewer in mOSHE; tell it to drop
8015 // any other state it's holding.
8016 mOSHE->SyncPresentationState();
8019 mDocumentViewer = nullptr;
8021 // Now that we're about to switch documents, forget all of our children.
8022 // Note that we cached them as needed up in CaptureState above.
8023 DestroyChildren();
8025 mDocumentViewer = aNewViewer;
8027 nsCOMPtr<nsIWidget> widget;
8028 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8030 nsIntRect bounds(x, y, cx, cy);
8032 mDocumentViewer->SetNavigationTiming(mTiming);
8034 if (NS_FAILED(mDocumentViewer->Init(widget, bounds, aWindowActor))) {
8035 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
8036 viewer->Close(nullptr);
8037 viewer->Destroy();
8038 mDocumentViewer = nullptr;
8039 SetCurrentURIInternal(nullptr);
8040 NS_WARNING("ContentViewer Initialization failed");
8041 return NS_ERROR_FAILURE;
8044 // If we have old state to copy, set the old state onto the new content
8045 // viewer
8046 if (newViewer) {
8047 newViewer->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8050 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8052 // Stuff the bgcolor from the old pres shell into the new
8053 // pres shell. This improves page load continuity.
8054 if (RefPtr<PresShell> presShell = mDocumentViewer->GetPresShell()) {
8055 presShell->SetCanvasBackground(bgcolor);
8056 presShell->ActivenessMaybeChanged();
8057 if (isUnderHiddenEmbedderElement) {
8058 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8062 // XXX: It looks like the LayoutState gets restored again in Embed()
8063 // right after the call to SetupNewViewer(...)
8065 // We don't show the mDocumentViewer yet, since we want to draw the old page
8066 // until we have enough of the new page to show. Just return with the new
8067 // viewer still set to hidden.
8069 return NS_OK;
8072 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8073 SessionHistoryInfo* aInfo) {
8074 NS_ENSURE_TRUE_VOID(mDocumentViewer);
8076 RefPtr<Document> document = GetDocument();
8077 NS_ENSURE_TRUE_VOID(document);
8079 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8080 if (mozilla::SessionHistoryInParent()) {
8081 // If aInfo is null, just set the document's state object to null.
8082 if (aInfo) {
8083 scContainer = aInfo->GetStateData();
8085 MOZ_LOG(gSHLog, LogLevel::Debug,
8086 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8087 } else {
8088 if (aShEntry) {
8089 scContainer = aShEntry->GetStateData();
8091 // If aShEntry is null, just set the document's state object to null.
8095 // It's OK for scContainer too be null here; that just means there's no
8096 // state data associated with this history entry.
8097 document->SetStateObject(scContainer);
8100 nsresult nsDocShell::CheckLoadingPermissions() {
8101 // This method checks whether the caller may load content into
8102 // this docshell. Even though we've done our best to hide windows
8103 // from code that doesn't have the right to access them, it's
8104 // still possible for an evil site to open a window and access
8105 // frames in the new window through window.frames[] (which is
8106 // allAccess for historic reasons), so we still need to do this
8107 // check on load.
8108 nsresult rv = NS_OK;
8110 if (!IsSubframe()) {
8111 // We're not a frame. Permit all loads.
8112 return rv;
8115 // Note - The check for a current JSContext here isn't necessarily sensical.
8116 // It's just designed to preserve the old semantics during a mass-conversion
8117 // patch.
8118 if (!nsContentUtils::GetCurrentJSContext()) {
8119 return NS_OK;
8122 // Check if the caller is from the same origin as this docshell,
8123 // or any of its ancestors.
8124 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8125 bc = bc->GetParent()) {
8126 // If the BrowsingContext is not in process, then it
8127 // is true by construction that its principal will not
8128 // subsume the current docshell principal.
8129 if (!bc->IsInProcess()) {
8130 continue;
8133 nsCOMPtr<nsIScriptGlobalObject> sgo =
8134 bc->GetDocShell()->GetScriptGlobalObject();
8135 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8137 nsIPrincipal* p;
8138 if (!sop || !(p = sop->GetPrincipal())) {
8139 return NS_ERROR_UNEXPECTED;
8142 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8143 // Same origin, permit load
8144 return NS_OK;
8148 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8151 //*****************************************************************************
8152 // nsDocShell: Site Loading
8153 //*****************************************************************************
8155 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8156 bool aInPrivateBrowsing) {
8157 if (XRE_IsContentProcess()) {
8158 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8159 if (contentChild) {
8160 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8162 return;
8165 #ifdef MOZ_PLACES
8166 nsCOMPtr<nsIFaviconService> favSvc =
8167 do_GetService("@mozilla.org/browser/favicon-service;1");
8168 if (favSvc) {
8169 favSvc->CopyFavicons(aOldURI, aNewURI,
8170 aInPrivateBrowsing
8171 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8172 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8173 nullptr);
8175 #endif
8178 class InternalLoadEvent : public Runnable {
8179 public:
8180 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8181 : mozilla::Runnable("InternalLoadEvent"),
8182 mDocShell(aDocShell),
8183 mLoadState(aLoadState) {
8184 // For events, both target and filename should be the version of "null" they
8185 // expect. By the time the event is fired, both window targeting and file
8186 // downloading have been handled, so we should never have an internal load
8187 // event that retargets or had a download.
8188 mLoadState->SetTarget(u""_ns);
8189 mLoadState->SetFileName(VoidString());
8192 NS_IMETHOD
8193 Run() override {
8194 #ifndef ANDROID
8195 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8196 "InternalLoadEvent: Should always have a principal here");
8197 #endif
8198 return mDocShell->InternalLoad(mLoadState);
8201 private:
8202 RefPtr<nsDocShell> mDocShell;
8203 RefPtr<nsDocShellLoadState> mLoadState;
8207 * Returns true if we started an asynchronous load (i.e., from the network), but
8208 * the document we're loading there hasn't yet become this docshell's active
8209 * document.
8211 * When JustStartedNetworkLoad is true, you should be careful about modifying
8212 * mLoadType and mLSHE. These are both set when the asynchronous load first
8213 * starts, and the load expects that, when it eventually runs InternalLoad,
8214 * mLoadType and mLSHE will have their original values.
8216 bool nsDocShell::JustStartedNetworkLoad() {
8217 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8220 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8221 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8222 // <embed>, etc)
8224 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8225 // later when we call DoURILoad.
8226 nsContentPolicyType nsDocShell::DetermineContentType() {
8227 if (!IsSubframe()) {
8228 return nsIContentPolicy::TYPE_DOCUMENT;
8231 const auto& maybeEmbedderElementType =
8232 GetBrowsingContext()->GetEmbedderElementType();
8233 if (!maybeEmbedderElementType) {
8234 // If the EmbedderElementType hasn't been set yet, just assume we're
8235 // an iframe since that's more common.
8236 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8239 return maybeEmbedderElementType->EqualsLiteral("iframe")
8240 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8241 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8244 bool nsDocShell::NoopenerForceEnabled() {
8245 // If current's top-level browsing context's active document's
8246 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8247 // if currentDoc's origin is not same origin with currentDoc's top-level
8248 // origin, noopener is force enabled, and name is cleared to "_blank".
8249 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8250 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8251 topPolicy ==
8252 nsILoadInfo::
8253 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8254 !mBrowsingContext->SameOriginWithTop();
8257 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8258 MOZ_ASSERT(aLoadState, "need a load state!");
8259 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8260 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8261 "should not have picked target yet");
8263 nsresult rv = NS_OK;
8264 RefPtr<BrowsingContext> targetContext;
8266 // Only _self, _parent, and _top are supported in noopener case. But we
8267 // have to be careful to not apply that to the noreferrer case. See bug
8268 // 1358469.
8269 bool allowNamedTarget =
8270 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8271 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8272 if (allowNamedTarget ||
8273 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8274 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8275 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8276 Document* document = GetDocument();
8277 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8278 WindowGlobalChild* wgc = document->GetWindowGlobalChild();
8279 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
8280 targetContext = wgc->FindBrowsingContextWithName(
8281 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8284 if (!targetContext) {
8285 // If the targetContext doesn't exist, then this is a new docShell and we
8286 // should consider this a TYPE_DOCUMENT load
8288 // For example, when target="_blank"
8290 // If there's no targetContext, that means we are about to create a new
8291 // window. Perform a content policy check before creating the window. Please
8292 // note for all other docshell loads content policy checks are performed
8293 // within the contentSecurityManager when the channel is about to be
8294 // openend.
8295 nsISupports* requestingContext = nullptr;
8296 if (XRE_IsContentProcess()) {
8297 // In e10s the child process doesn't have access to the element that
8298 // contains the browsing context (because that element is in the chrome
8299 // process). So we just pass mScriptGlobal.
8300 requestingContext = ToSupports(mScriptGlobal);
8301 } else {
8302 // This is for loading non-e10s tabs and toplevel windows of various
8303 // sorts.
8304 // For the toplevel window cases, requestingElement will be null.
8305 nsCOMPtr<Element> requestingElement =
8306 mScriptGlobal->GetFrameElementInternal();
8307 requestingContext = requestingElement;
8310 // Ideally we should use the same loadinfo as within DoURILoad which
8311 // should match this one when both are applicable.
8312 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
8313 new LoadInfo(mScriptGlobal, aLoadState->URI(),
8314 aLoadState->TriggeringPrincipal(), requestingContext,
8315 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8317 // Since Content Policy checks are performed within docShell as well as
8318 // the ContentSecurityManager we need a reliable way to let certain
8319 // nsIContentPolicy consumers ignore duplicate calls.
8320 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8322 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8323 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8324 &shouldLoad);
8326 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8327 if (NS_SUCCEEDED(rv)) {
8328 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8329 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8331 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8332 return NS_ERROR_BLOCKED_BY_POLICY;
8336 return NS_ERROR_CONTENT_BLOCKED;
8341 // Resolve the window target before going any further...
8342 // If the load has been targeted to another DocShell, then transfer the
8343 // load to it...
8346 // We've already done our owner-inheriting. Mask out that bit, so we
8347 // don't try inheriting an owner from the target window if we came up
8348 // with a null owner above.
8349 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8351 if (!targetContext) {
8352 // If the docshell's document is sandboxed, only open a new window
8353 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8354 // (i.e. if allow-popups is specified)
8355 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8356 Document* doc = mDocumentViewer->GetDocument();
8358 const bool isDocumentAuxSandboxed =
8359 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8361 if (isDocumentAuxSandboxed) {
8362 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8365 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8366 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8368 RefPtr<BrowsingContext> newBC;
8369 nsAutoCString spec;
8370 aLoadState->URI()->GetSpec(spec);
8372 // If we are a noopener load, we just hand the whole thing over to our
8373 // window.
8374 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8375 NoopenerForceEnabled()) {
8376 // Various asserts that we know to hold because NO_OPENER loads can only
8377 // happen for links.
8378 MOZ_ASSERT(!aLoadState->LoadReplace());
8379 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8380 aLoadState->TriggeringPrincipal());
8381 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8382 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8383 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8384 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8385 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8386 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8387 aLoadState->IsFormSubmission());
8388 MOZ_ASSERT(!aLoadState->HeadersStream());
8389 // If OnLinkClickSync was invoked inside the onload handler, the load
8390 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8391 // LOAD_LINK.
8392 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8393 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8394 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8395 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8397 RefPtr<nsDocShellLoadState> loadState =
8398 new nsDocShellLoadState(aLoadState->URI());
8400 // Set up our loadinfo so it will do the load as much like we would have
8401 // as possible.
8402 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8403 loadState->SetOriginalURI(aLoadState->OriginalURI());
8405 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8406 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8408 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8409 loadState->SetKeepResultPrincipalURIIfSet(
8410 aLoadState->KeepResultPrincipalURIIfSet());
8411 // LoadReplace will always be false due to asserts above, skip setting
8412 // it.
8413 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8414 loadState->SetTriggeringSandboxFlags(
8415 aLoadState->TriggeringSandboxFlags());
8416 loadState->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
8417 loadState->SetTriggeringStorageAccess(
8418 aLoadState->TriggeringStorageAccess());
8419 loadState->SetCsp(aLoadState->Csp());
8420 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8421 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8422 // Explicit principal because we do not want any guesses as to what the
8423 // principal to inherit is: it should be aTriggeringPrincipal.
8424 loadState->SetPrincipalIsExplicit(true);
8425 loadState->SetLoadType(aLoadState->LoadType());
8426 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8427 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8429 loadState->SetHasValidUserGestureActivation(
8430 aLoadState->HasValidUserGestureActivation());
8432 // Propagate POST data to the new load.
8433 loadState->SetPostDataStream(aLoadState->PostDataStream());
8434 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8436 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8437 aLoadState->Target(), // window name
8438 u""_ns, // Features
8439 loadState,
8440 true, // aForceNoOpener
8441 getter_AddRefs(newBC));
8442 MOZ_ASSERT(!newBC);
8443 return rv;
8446 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8447 aLoadState->Target(), // window name
8448 u""_ns, // Features
8449 getter_AddRefs(newBC));
8451 // In some cases the Open call doesn't actually result in a new
8452 // window being opened. We can detect these cases by examining the
8453 // document in |newBC|, if any.
8454 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8455 newBC ? newBC->GetDOMWindow() : nullptr;
8456 if (piNewWin) {
8457 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8458 if (!newDoc || newDoc->IsInitialDocument()) {
8459 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8463 if (newBC) {
8464 targetContext = newBC;
8467 NS_ENSURE_SUCCESS(rv, rv);
8468 NS_ENSURE_TRUE(targetContext, rv);
8470 // If our target BrowsingContext is still pending initialization, ignore the
8471 // navigation request targeting it.
8472 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8473 return NS_OK;
8476 aLoadState->SetTargetBrowsingContext(targetContext);
8477 if (aLoadState->IsFormSubmission()) {
8478 aLoadState->SetLoadType(
8479 GetLoadTypeForFormSubmission(targetContext, aLoadState));
8483 // Transfer the load to the target BrowsingContext... Clear the window target
8484 // name to the empty string to prevent recursive retargeting!
8486 // No window target
8487 aLoadState->SetTarget(u""_ns);
8488 // No forced download
8489 aLoadState->SetFileName(VoidString());
8490 return targetContext->InternalLoad(aLoadState);
8493 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8494 nsAutoCString result;
8495 if (NS_FAILED(aURI->GetRef(result))) {
8496 result.SetIsVoid(true);
8498 return result;
8501 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8502 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8504 bool equal = false;
8505 if (mCurrentURI &&
8506 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8507 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8508 flags |= LOCATION_CHANGE_HASHCHANGE;
8511 return flags;
8514 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8515 SameDocumentNavigationState& aState) {
8516 MOZ_ASSERT(aLoadState);
8517 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8518 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8519 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8520 LOAD_FLAGS_REPLACE_HISTORY) ||
8521 aLoadState->LoadType() == LOAD_HISTORY ||
8522 aLoadState->LoadType() == LOAD_LINK)) {
8523 return false;
8526 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8528 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8529 if (NS_SUCCEEDED(rvURINew)) {
8530 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8533 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8534 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8535 if (NS_SUCCEEDED(rvURIOld)) {
8536 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8538 if (NS_SUCCEEDED(rvURIOld)) {
8539 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8540 &aState.mSameExceptHashes))) {
8541 aState.mSameExceptHashes = false;
8546 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8547 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8548 nsCOMPtr<nsIURI> currentExposableURI =
8549 nsIOService::CreateExposableURI(currentURI);
8550 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8551 if (NS_SUCCEEDED(rvURIOld)) {
8552 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8554 if (NS_SUCCEEDED(rvURIOld)) {
8555 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8556 aLoadState->URI(), &aState.mSameExceptHashes))) {
8557 aState.mSameExceptHashes = false;
8559 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8560 // have to perform a special check here to avoid an actual navigation. If
8561 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8562 // fact that the new URI is currently http), then set mSameExceptHashes to
8563 // true and only perform a fragment navigation.
8564 if (!aState.mSameExceptHashes) {
8565 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
8566 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8567 if (!docLoadInfo->GetLoadErrorPage() &&
8568 nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8569 currentExposableURI, aLoadState->URI(), docLoadInfo)) {
8570 uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
8571 if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
8572 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
8573 // At this point the requested URI is for sure a fragment
8574 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8575 // enabled. Also it is not interfering the upgrade order of
8576 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8577 // Since we are on an HTTPS site the fragment
8578 // navigation should also be an HTTPS.
8579 // For that reason we should upgrade the URI to HTTPS.
8580 aState.mSecureUpgradeURI = true;
8581 aState.mSameExceptHashes = true;
8589 if (mozilla::SessionHistoryInParent()) {
8590 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8591 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8592 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8594 MOZ_LOG(gSHLog, LogLevel::Debug,
8595 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8596 this, aState.mHistoryNavBetweenSameDoc));
8597 } else {
8598 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8599 // We're doing a history load.
8601 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8602 &aState.mHistoryNavBetweenSameDoc);
8606 // A same document navigation happens when we navigate between two SHEntries
8607 // for the same document. We do a same document navigation under two
8608 // circumstances. Either
8610 // a) we're navigating between two different SHEntries which share a
8611 // document, or
8613 // b) we're navigating to a new shentry whose URI differs from the
8614 // current URI only in its hash, the new hash is non-empty, and
8615 // we're not doing a POST.
8617 // The restriction that the SHEntries in (a) must be different ensures
8618 // that history.go(0) and the like trigger full refreshes, rather than
8619 // same document navigations.
8620 if (!mozilla::SessionHistoryInParent()) {
8621 bool doSameDocumentNavigation =
8622 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8623 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8624 aState.mSameExceptHashes && aState.mNewURIHasRef);
8625 MOZ_LOG(gSHLog, LogLevel::Debug,
8626 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8627 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8628 return doSameDocumentNavigation;
8631 if (aState.mHistoryNavBetweenSameDoc &&
8632 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8633 return true;
8636 MOZ_LOG(
8637 gSHLog, LogLevel::Debug,
8638 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8639 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8640 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8641 !aLoadState->PostDataStream() ? "true" : "false",
8642 aState.mSameExceptHashes ? "true" : "false",
8643 aState.mNewURIHasRef ? "true" : "false"));
8644 return !aLoadState->LoadIsFromSessionHistory() &&
8645 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8646 aState.mNewURIHasRef;
8649 nsresult nsDocShell::HandleSameDocumentNavigation(
8650 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState,
8651 bool& aSameDocument) {
8652 aSameDocument = true;
8653 #ifdef DEBUG
8654 SameDocumentNavigationState state;
8655 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8656 #endif
8658 MOZ_LOG(gSHLog, LogLevel::Debug,
8659 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8660 mCurrentURI->GetSpecOrDefault().get(),
8661 aLoadState->URI()->GetSpecOrDefault().get()));
8663 RefPtr<Document> doc = GetDocument();
8664 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8665 doc->DoNotifyPossibleTitleChange();
8667 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8669 // We need to upgrade the new URI from http: to https:
8670 nsCOMPtr<nsIURI> newURI = aLoadState->URI();
8671 if (aState.mSecureUpgradeURI) {
8672 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
8673 MOZ_LOG(gSHLog, LogLevel::Debug,
8674 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
8677 if (StaticPrefs::dom_security_setdocumenturi()) {
8678 // check if aLoadState->URI(), principalURI, mCurrentURI are same origin
8679 // skip handling otherwise
8680 nsCOMPtr<nsIPrincipal> origPrincipal = doc->NodePrincipal();
8681 nsCOMPtr<nsIURI> principalURI = origPrincipal->GetURI();
8682 if (origPrincipal->GetIsNullPrincipal()) {
8683 nsCOMPtr<nsIPrincipal> precursor = origPrincipal->GetPrecursorPrincipal();
8684 if (precursor) {
8685 principalURI = precursor->GetURI();
8689 auto isLoadableViaInternet = [](nsIURI* uri) {
8690 return (uri && (net::SchemeIsHTTP(uri) || net::SchemeIsHTTPS(uri)));
8693 if (isLoadableViaInternet(principalURI) &&
8694 isLoadableViaInternet(mCurrentURI) && isLoadableViaInternet(newURI)) {
8695 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
8696 if (!NS_SUCCEEDED(
8697 ssm->CheckSameOriginURI(newURI, principalURI, false, false)) ||
8698 !NS_SUCCEEDED(ssm->CheckSameOriginURI(mCurrentURI, principalURI,
8699 false, false))) {
8700 MOZ_LOG(gSHLog, LogLevel::Debug,
8701 ("nsDocShell[%p]: possible violation of the same origin policy "
8702 "during same document navigation",
8703 this));
8704 aSameDocument = false;
8705 return NS_OK;
8710 #ifdef DEBUG
8711 if (aState.mSameExceptHashes) {
8712 bool sameExceptHashes = false;
8713 currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
8714 MOZ_ASSERT(sameExceptHashes);
8716 #endif
8718 // Save the position of the scrollers.
8719 nsPoint scrollPos = GetCurScrollPos();
8721 // Reset mLoadType to its original value once we exit this block, because this
8722 // same document navigation might have started after a normal, network load,
8723 // and we don't want to clobber its load type. See bug 737307.
8724 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8726 // If a non-same-document-navigation (i.e., a network load) is pending, make
8727 // this a replacement load, so that we don't add a SHEntry here and the
8728 // network load goes into the SHEntry it expects to.
8729 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8730 mLoadType = LOAD_NORMAL_REPLACE;
8731 } else {
8732 mLoadType = aLoadState->LoadType();
8735 mURIResultedInDocument = true;
8737 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8739 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8740 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8741 // onLocationChange() notifications to the browser to update back/forward
8742 // buttons.
8743 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8744 Nothing());
8745 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8746 mLoadingEntry.swap(oldLoadingEntry);
8747 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8748 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8749 *aLoadState->GetLoadingSessionHistoryInfo());
8750 mNeedToReportActiveAfterLoadingBecomesActive = false;
8753 // Set the doc's URI according to the new history entry's URI.
8754 doc->SetDocumentURI(newURI);
8756 /* This is a anchor traversal within the same page.
8757 * call OnNewURI() so that, this traversal will be
8758 * recorded in session and global history.
8760 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8761 newURIPartitionedPrincipalToInherit;
8762 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8763 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8764 if (mozilla::SessionHistoryInParent()) {
8765 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8766 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8767 newURIPartitionedPrincipalToInherit =
8768 mActiveEntry->GetPartitionedPrincipalToInherit();
8769 newCsp = mActiveEntry->GetCsp();
8770 } else {
8771 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8772 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8773 newURIPartitionedPrincipalToInherit =
8774 mOSHE->GetPartitionedPrincipalToInherit();
8775 newCsp = mOSHE->GetCsp();
8777 } else {
8778 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8779 newURIPrincipalToInherit = doc->NodePrincipal();
8780 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8781 newCsp = doc->GetCsp();
8784 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
8786 // Pass true for aCloneSHChildren, since we're not
8787 // changing documents here, so all of our subframes are
8788 // still relevant to the new session history entry.
8790 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8791 // flag on firing onLocationChange(...).
8792 // Anyway, aCloneSHChildren param is simply reflecting
8793 // doSameDocumentNavigation in this scope.
8795 // Note: we'll actually fire onLocationChange later, in order to preserve
8796 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8797 // 1668126)
8798 bool locationChangeNeeded = OnNewURI(
8799 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
8800 newURIPartitionedPrincipalToInherit, newCsp, true, true);
8802 nsCOMPtr<nsIInputStream> postData;
8803 nsCOMPtr<nsIReferrerInfo> referrerInfo;
8804 uint32_t cacheKey = 0;
8806 bool scrollRestorationIsManual = false;
8807 if (!mozilla::SessionHistoryInParent()) {
8808 if (mOSHE) {
8809 /* save current position of scroller(s) (bug 59774) */
8810 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8811 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8812 // Get the postdata, page ident and referrer info from the current page,
8813 // if the new load is being done via normal means. Note that "normal
8814 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
8815 // the loadType and allowScroll check above -- it filters out some
8816 // LOAD_CMD_NORMAL cases that we wouldn't want here.
8817 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8818 postData = mOSHE->GetPostData();
8819 cacheKey = mOSHE->GetCacheKey();
8820 referrerInfo = mOSHE->GetReferrerInfo();
8823 // Link our new SHEntry to the old SHEntry's back/forward
8824 // cache data, since the two SHEntries correspond to the
8825 // same document.
8826 if (mLSHE) {
8827 if (!aLoadState->LoadIsFromSessionHistory()) {
8828 // If we're not doing a history load, scroll restoration
8829 // should be inherited from the previous session history entry.
8830 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8831 scrollRestorationIsManual);
8833 mLSHE->AdoptBFCacheEntry(mOSHE);
8836 } else {
8837 if (mActiveEntry) {
8838 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8839 if (mBrowsingContext) {
8840 CollectWireframe();
8841 if (XRE_IsParentProcess()) {
8842 SessionHistoryEntry* entry =
8843 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8844 if (entry) {
8845 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8847 } else {
8848 mozilla::Unused << ContentChild::GetSingleton()
8849 ->SendSessionHistoryEntryScrollPosition(
8850 mBrowsingContext, scrollPos.x,
8851 scrollPos.y);
8855 if (mLoadingEntry) {
8856 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8857 // If we're not doing a history load, scroll restoration
8858 // should be inherited from the previous session history entry.
8859 // XXX This needs most probably tweaks once fragment navigation is
8860 // fixed to work with session-history-in-parent.
8861 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8862 scrollRestorationIsManual);
8867 // If we're doing a history load, use its scroll restoration state.
8868 if (aLoadState->LoadIsFromSessionHistory()) {
8869 if (mozilla::SessionHistoryInParent()) {
8870 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8871 ->mInfo.GetScrollRestorationIsManual();
8872 } else {
8873 scrollRestorationIsManual =
8874 aLoadState->SHEntry()->GetScrollRestorationIsManual();
8878 /* Assign mLSHE to mOSHE. This will either be a new entry created
8879 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8880 * loads.
8882 if (!mozilla::SessionHistoryInParent()) {
8883 if (mLSHE) {
8884 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8885 // Save the postData obtained from the previous page
8886 // in to the session history entry created for the
8887 // anchor page, so that any history load of the anchor
8888 // page will restore the appropriate postData.
8889 if (postData) {
8890 mOSHE->SetPostData(postData);
8893 // Make sure we won't just repost without hitting the
8894 // cache first
8895 if (cacheKey != 0) {
8896 mOSHE->SetCacheKey(cacheKey);
8899 // As the document has not changed, the referrer info hasn't changed too,
8900 // so we can just copy it over.
8901 if (referrerInfo) {
8902 mOSHE->SetReferrerInfo(referrerInfo);
8906 /* Set the title for the SH entry for this target url so that
8907 * SH menus in go/back/forward buttons won't be empty for this.
8908 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
8909 * the code above.
8910 * Note, when session history lives in the parent process, this does not
8911 * update the title there.
8913 SetTitleOnHistoryEntry(false);
8914 } else {
8915 if (aLoadState->LoadIsFromSessionHistory()) {
8916 MOZ_LOG(
8917 gSHLog, LogLevel::Debug,
8918 ("Moving the loading entry to the active entry on nsDocShell %p to "
8919 "%s",
8920 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
8922 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
8923 if (mActiveEntry) {
8924 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
8927 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
8928 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
8929 if (currentLayoutHistoryState) {
8930 // Restore the existing nsILayoutHistoryState object, since it is
8931 // possibly being used by the layout. When doing a new load, the
8932 // shared state is copied from the existing active entry, so this
8933 // special case is needed only with the history loads.
8934 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
8937 if (cacheKey != 0) {
8938 mActiveEntry->SetCacheKey(cacheKey);
8940 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
8941 // does require a non-null uri if this is for a refresh load of the same
8942 // URI, but in that case mCurrentURI won't be null here.
8943 mBrowsingContext->SessionHistoryCommit(
8944 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
8945 true, true,
8946 /* No expiration update on the same document loads*/
8947 false, cacheKey);
8948 // FIXME Need to set postdata.
8950 // Set the title for the SH entry for this target url so that
8951 // SH menus in go/back/forward buttons won't be empty for this.
8952 // Note, when session history lives in the parent process, this does not
8953 // update the title there.
8954 SetTitleOnHistoryEntry(false);
8955 } else {
8956 Maybe<bool> scrollRestorationIsManual;
8957 if (mActiveEntry) {
8958 scrollRestorationIsManual.emplace(
8959 mActiveEntry->GetScrollRestorationIsManual());
8961 // Get the postdata, page ident and referrer info from the current page,
8962 // if the new load is being done via normal means. Note that "normal
8963 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
8964 // the loadType and allowScroll check above -- it filters out some
8965 // LOAD_CMD_NORMAL cases that we wouldn't want here.
8966 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8967 postData = mActiveEntry->GetPostData();
8968 cacheKey = mActiveEntry->GetCacheKey();
8969 referrerInfo = mActiveEntry->GetReferrerInfo();
8973 MOZ_LOG(gSHLog, LogLevel::Debug,
8974 ("Creating an active entry on nsDocShell %p to %s", this,
8975 newURI->GetSpecOrDefault().get()));
8976 if (mActiveEntry) {
8977 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, newURI);
8978 } else {
8979 mActiveEntry = MakeUnique<SessionHistoryInfo>(
8980 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
8981 newURIPartitionedPrincipalToInherit, newCsp, mContentTypeHint);
8984 // Save the postData obtained from the previous page in to the session
8985 // history entry created for the anchor page, so that any history load of
8986 // the anchor page will restore the appropriate postData.
8987 if (postData) {
8988 mActiveEntry->SetPostData(postData);
8991 // Make sure we won't just repost without hitting the
8992 // cache first
8993 if (cacheKey != 0) {
8994 mActiveEntry->SetCacheKey(cacheKey);
8997 // As the document has not changed, the referrer info hasn't changed too,
8998 // so we can just copy it over.
8999 if (referrerInfo) {
9000 mActiveEntry->SetReferrerInfo(referrerInfo);
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());
10688 // If we did a process switch, then we should have an existing allocated
10689 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10690 CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget());
10692 RefPtr<nsDocumentOpenInfo> loader =
10693 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10694 channel->SetLoadGroup(mLoadGroup);
10696 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10698 nsresult rv = NS_OK;
10699 if (XRE_IsParentProcess()) {
10700 // If we're in the parent, the we don't have an nsIChildChannel, just
10701 // the original channel, which is already open in this process.
10703 // DocumentLoadListener expects to get an nsIParentChannel, so
10704 // we create a wrapper around the channel and nsIStreamListener
10705 // that forwards functionality as needed, and then we register
10706 // it under the provided identifier.
10707 RefPtr<ParentChannelWrapper> wrapper =
10708 new ParentChannelWrapper(channel, loader);
10709 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10711 mLoadGroup->AddRequest(channel, nullptr);
10712 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10713 do_QueryInterface(channel)) {
10714 // Our channel was redirected from another process, so doesn't need to
10715 // be opened again. However, it does need its listener hooked up
10716 // correctly.
10717 rv = childChannel->CompleteRedirectSetup(loader);
10718 } else {
10719 // It's possible for the redirected channel to not implement
10720 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10721 // can just open the local instance and it will work.
10722 rv = channel->AsyncOpen(loader);
10724 if (rv == NS_ERROR_NO_CONTENT) {
10725 return NS_OK;
10727 NS_ENSURE_SUCCESS(rv, rv);
10729 // Success. Keep the initial ClientSource if it exists.
10730 cleanupInitialClient.release();
10731 return NS_OK;
10734 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
10735 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10736 nsACString& aNewHash, uint32_t aLoadType) {
10737 if (!mCurrentURI) {
10738 return NS_OK;
10741 RefPtr<PresShell> presShell = GetPresShell();
10742 if (!presShell) {
10743 // If we failed to get the shell, or if there is no shell,
10744 // nothing left to do here.
10745 return NS_OK;
10748 nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
10749 if (rootScroll) {
10750 rootScroll->ClearDidHistoryRestore();
10753 // If we have no new anchor, we do not want to scroll, unless there is a
10754 // current anchor and we are doing a history load. So return if we have no
10755 // new anchor, and there is no current anchor or the load is not a history
10756 // load.
10757 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10758 return NS_OK;
10761 // Both the new and current URIs refer to the same page. We can now
10762 // browse to the hash stored in the new URI.
10764 // If it's a load from history, we don't have any anchor jumping to do.
10765 // Scrollbar position will be restored by the caller based on positions stored
10766 // in session history.
10767 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10769 if (aNewHash.IsEmpty()) {
10770 // 2. If fragment is the empty string, then return the special value top of
10771 // the document.
10773 // Tell the shell it's at an anchor without scrolling.
10774 presShell->GoToAnchor(u""_ns, false);
10776 if (scroll) {
10777 // Scroll to the top of the page. Ignore the return value; failure to
10778 // scroll here (e.g. if there is no root scrollframe) is not grounds for
10779 // canceling the load!
10780 SetCurScrollPosEx(0, 0);
10783 return NS_OK;
10786 // 3. Let potentialIndicatedElement be the result of finding a potential
10787 // indicated element given document and fragment.
10788 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10789 auto rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
10791 // 4. If potentialIndicatedElement is not null, then return
10792 // potentialIndicatedElement.
10793 if (NS_SUCCEEDED(rv)) {
10794 return NS_OK;
10797 // 5. Let fragmentBytes be the result of percent-decoding fragment.
10798 nsAutoCString fragmentBytes;
10799 const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(),
10800 /* aFlags = */ 0, fragmentBytes);
10802 if (!unescaped) {
10803 // Another attempt is only necessary if characters were unescaped.
10804 return NS_OK;
10807 if (fragmentBytes.IsEmpty()) {
10808 // When aNewHash contains "%00", the unescaped string may be empty, and
10809 // GoToAnchor asserts if we ask it to scroll to an empty ref.
10810 presShell->GoToAnchor(u""_ns, false);
10811 return NS_OK;
10814 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
10815 // fragmentBytes.
10816 nsAutoString decodedFragment;
10817 rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
10818 NS_ENSURE_SUCCESS(rv, rv);
10820 // 7. Set potentialIndicatedElement to the result of finding a potential
10821 // indicated element given document and decodedFragment.
10823 // Ignore the return value of GoToAnchor, since it will return an error if
10824 // there is no such anchor in the document, which is actually a success
10825 // condition for us (we want to update the session history with the new URI no
10826 // matter whether we actually scrolled somewhere).
10827 presShell->GoToAnchor(decodedFragment, scroll, ScrollFlags::ScrollSmoothAuto);
10829 return NS_OK;
10832 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10833 nsIPrincipal* aTriggeringPrincipal,
10834 nsIPrincipal* aPrincipalToInherit,
10835 nsIPrincipal* aPartitionedPrincipalToInherit,
10836 nsIContentSecurityPolicy* aCsp,
10837 bool aAddToGlobalHistory, bool aCloneSHChildren) {
10838 MOZ_ASSERT(aURI, "uri is null");
10839 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10841 MOZ_ASSERT(!aPrincipalToInherit ||
10842 (aPrincipalToInherit && aTriggeringPrincipal));
10844 #if defined(DEBUG)
10845 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10846 nsAutoCString chanName;
10847 if (aChannel) {
10848 aChannel->GetName(chanName);
10849 } else {
10850 chanName.AssignLiteral("<no channel>");
10853 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10854 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10855 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10857 #endif
10859 bool equalUri = false;
10861 // Get the post data and the HTTP response code from the channel.
10862 uint32_t responseStatus = 0;
10863 nsCOMPtr<nsIInputStream> inputStream;
10864 if (aChannel) {
10865 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10867 // Check if the HTTPChannel is hiding under a multiPartChannel
10868 if (!httpChannel) {
10869 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10872 if (httpChannel) {
10873 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10874 if (uploadChannel) {
10875 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10878 // If the response status indicates an error, unlink this session
10879 // history entry from any entries sharing its document.
10880 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10881 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10882 mLSHE->AbandonBFCacheEntry();
10883 // FIXME Do the same for mLoadingEntry
10888 // Determine if this type of load should update history.
10889 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
10891 // We don't update session history on reload unless we're loading
10892 // an iframe in shift-reload case.
10893 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
10895 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10896 // root browsing context.
10897 // FIXME If session history in the parent is enabled then we only do this if
10898 // the session history object is in process, otherwise we can't really
10899 // use the mLSHE anyway. Once session history is only stored in the
10900 // parent then this code will probably be removed anyway.
10901 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
10902 if (!rootSH) {
10903 updateSHistory = false;
10904 updateGHistory = false; // XXX Why global history too?
10907 // Check if the url to be loaded is the same as the one already loaded.
10908 if (mCurrentURI) {
10909 aURI->Equals(mCurrentURI, &equalUri);
10912 #ifdef DEBUG
10913 bool shAvailable = (rootSH != nullptr);
10915 // XXX This log message is almost useless because |updateSHistory|
10916 // and |updateGHistory| are not correct at this point.
10918 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10919 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10920 " equalURI=%i\n",
10921 shAvailable, updateSHistory, updateGHistory, equalUri));
10922 #endif
10924 /* If the url to be loaded is the same as the one already there,
10925 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10926 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10927 * AddToSessionHistory() won't mess with the current SHEntry and
10928 * if this page has any frame children, it also will be handled
10929 * properly. see bug 83684
10931 * NB: If mOSHE is null but we have a current URI, then it probably
10932 * means that we must be at the transient about:blank content viewer;
10933 * we should let the normal load continue, since there's nothing to
10934 * replace. Sometimes this happens after a session restore (eg process
10935 * switch) and mCurrentURI is not about:blank; we assume we can let the load
10936 * continue (Bug 1301399).
10938 * XXX Hopefully changing the loadType at this time will not hurt
10939 * anywhere. The other way to take care of sequentially repeating
10940 * frameset pages is to add new methods to nsIDocShellTreeItem.
10941 * Hopefully I don't have to do that.
10943 if (equalUri &&
10944 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
10945 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
10946 mLoadType == LOAD_STOP_CONTENT) &&
10947 !inputStream) {
10948 mLoadType = LOAD_NORMAL_REPLACE;
10951 // If this is a refresh to the currently loaded url, we don't
10952 // have to update session or global history.
10953 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10954 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
10957 /* If the user pressed shift-reload, cache will create a new cache key
10958 * for the page. Save the new cacheKey in Session History.
10959 * see bug 90098
10961 if (aChannel && IsForceReloadType(mLoadType)) {
10962 MOZ_ASSERT(!updateSHistory || IsSubframe(),
10963 "We shouldn't be updating session history for forced"
10964 " reloads unless we're in a newly created iframe!");
10966 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
10967 uint32_t cacheKey = 0;
10968 // Get the Cache Key and store it in SH.
10969 if (cacheChannel) {
10970 cacheChannel->GetCacheKey(&cacheKey);
10972 // If we already have a loading history entry, store the new cache key
10973 // in it. Otherwise, since we're doing a reload and won't be updating
10974 // our history entry, store the cache key in our current history entry.
10975 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
10977 if (!mozilla::SessionHistoryInParent()) {
10978 // Since we're force-reloading, clear all the sub frame history.
10979 ClearFrameHistory(mLSHE);
10980 ClearFrameHistory(mOSHE);
10984 if (!mozilla::SessionHistoryInParent()) {
10985 // Clear subframe history on refresh.
10986 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
10987 // this case. One should re-validate after bug 1331865 fixed.
10988 if (mLoadType == LOAD_REFRESH) {
10989 ClearFrameHistory(mLSHE);
10990 ClearFrameHistory(mOSHE);
10993 if (updateSHistory) {
10994 // Update session history if necessary...
10995 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
10996 /* This is a fresh page getting loaded for the first time
10997 *.Create a Entry for it and add it to SH, if this is the
10998 * rootDocShell
11000 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11001 aPrincipalToInherit,
11002 aPartitionedPrincipalToInherit, aCsp,
11003 aCloneSHChildren, getter_AddRefs(mLSHE));
11005 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11006 // Even if we don't add anything to SHistory, ensure the current index
11007 // points to the same SHEntry as our mLSHE.
11009 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11010 mLSHE);
11014 // If this is a POST request, we do not want to include this in global
11015 // history.
11016 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11017 !net::ChannelIsPost(aChannel)) {
11018 nsCOMPtr<nsIURI> previousURI;
11019 uint32_t previousFlags = 0;
11021 if (mLoadType & LOAD_CMD_RELOAD) {
11022 // On a reload request, we don't set redirecting flags.
11023 previousURI = aURI;
11024 } else {
11025 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11028 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11031 // If this was a history load or a refresh, or it was a history load but
11032 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11033 // in session history.
11034 if (!mozilla::SessionHistoryInParent() && rootSH &&
11035 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11036 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11037 mPreviousEntryIndex = rootSH->Index();
11038 if (!mozilla::SessionHistoryInParent()) {
11039 rootSH->LegacySHistory()->UpdateIndex();
11041 mLoadedEntryIndex = rootSH->Index();
11042 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11043 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11044 mLoadedEntryIndex));
11047 // aCloneSHChildren exactly means "we are not loading a new document".
11048 uint32_t locationFlags =
11049 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11051 bool onLocationChangeNeeded =
11052 SetCurrentURI(aURI, aChannel, false,
11053 /* aIsInitialAboutBlank */ false, locationFlags);
11054 // Make sure to store the referrer from the channel, if any
11055 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11056 if (httpChannel) {
11057 mReferrerInfo = httpChannel->GetReferrerInfo();
11059 return onLocationChangeNeeded;
11062 Maybe<Wireframe> nsDocShell::GetWireframe() {
11063 const bool collectWireFrame =
11064 mozilla::SessionHistoryInParent() &&
11065 StaticPrefs::browser_history_collectWireframes() &&
11066 mBrowsingContext->IsTopContent() && mActiveEntry;
11068 if (!collectWireFrame) {
11069 return Nothing();
11072 RefPtr<Document> doc = mDocumentViewer->GetDocument();
11073 Nullable<Wireframe> wireframe;
11074 doc->GetWireframeWithoutFlushing(false, wireframe);
11075 if (wireframe.IsNull()) {
11076 return Nothing();
11078 return Some(wireframe.Value());
11081 bool nsDocShell::CollectWireframe() {
11082 Maybe<Wireframe> wireframe = GetWireframe();
11083 if (wireframe.isNothing()) {
11084 return false;
11087 if (XRE_IsParentProcess()) {
11088 SessionHistoryEntry* entry =
11089 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11090 if (entry) {
11091 entry->SetWireframe(wireframe);
11093 } else {
11094 mozilla::Unused
11095 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11096 mBrowsingContext, wireframe.ref());
11099 return true;
11102 //*****************************************************************************
11103 // nsDocShell: Session History
11104 //*****************************************************************************
11106 NS_IMETHODIMP
11107 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11108 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11109 MOZ_LOG(gSHLog, LogLevel::Debug,
11110 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11111 NS_ConvertUTF16toUTF8(aTitle).get(),
11112 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11113 // Implements History.pushState and History.replaceState
11115 // Here's what we do, roughly in the order specified by HTML5. The specific
11116 // steps we are executing are at
11117 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11118 // and
11119 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11120 // This function basically implements #dom-history-pushstate and
11121 // UpdateURLAndHistory implements #url-and-history-update-steps.
11123 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11124 // step 5.
11125 // B. If the third argument is present, #dom-history-pushstate step 7.
11126 // 7.1. Resolve the url, relative to our document.
11127 // 7.2. If (a) fails, raise a SECURITY_ERR
11128 // 7.4. Compare the resulting absolute URL to the document's address. If
11129 // any part of the URLs difer other than the <path>, <query>, and
11130 // <fragment> components, raise a SECURITY_ERR and abort.
11131 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11132 // Remove from the session history all entries after the current entry,
11133 // as we would after a regular navigation, and save the current
11134 // entry's scroll position (bug 590573).
11135 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11136 // either add a state object entry to the session history after the
11137 // current entry with the following properties, or modify the current
11138 // session history entry to set
11139 // a. cloned data as the state object,
11140 // b. if the third argument was present, the absolute URL found in
11141 // step 2
11142 // Also clear the new history entry's POST data (see bug 580069).
11143 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11144 // replaceState), notify bfcache that we've navigated to a new page.
11145 // F. If the third argument is present, set the document's current address
11146 // to the absolute URL found in step B. This is
11147 // #url-and-history-update-steps step 4.
11149 // It's important that this function not run arbitrary scripts after step A
11150 // and before completing step E. For example, if a script called
11151 // history.back() before we completed step E, bfcache might destroy an
11152 // active content viewer. Since EvictOutOfRangeDocumentViewers at the end of
11153 // step E might run script, we can't just put a script blocker around the
11154 // critical section.
11156 // Note that we completely ignore the aTitle parameter.
11158 nsresult rv;
11160 // Don't clobber the load type of an existing network load.
11161 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11163 // pushState effectively becomes replaceState when we've started a network
11164 // load but haven't adopted its document yet. This mirrors what we do with
11165 // changes to the hash at this stage of the game.
11166 if (JustStartedNetworkLoad()) {
11167 aReplace = true;
11170 RefPtr<Document> document = GetDocument();
11171 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11173 // Step A: Serialize aData using structured clone.
11174 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11175 // step 5.
11176 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11178 // scContainer->Init might cause arbitrary JS to run, and this code might
11179 // navigate the page we're on, potentially to a different origin! (bug
11180 // 634834) To protect against this, we abort if our principal changes due
11181 // to the InitFromJSVal() call.
11183 RefPtr<Document> origDocument = GetDocument();
11184 if (!origDocument) {
11185 return NS_ERROR_DOM_SECURITY_ERR;
11187 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11189 scContainer = new nsStructuredCloneContainer();
11190 rv = scContainer->InitFromJSVal(aData, aCx);
11191 NS_ENSURE_SUCCESS(rv, rv);
11193 RefPtr<Document> newDocument = GetDocument();
11194 if (!newDocument) {
11195 return NS_ERROR_DOM_SECURITY_ERR;
11197 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11199 bool principalsEqual = false;
11200 origPrincipal->Equals(newPrincipal, &principalsEqual);
11201 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11204 // Check that the state object isn't too long.
11205 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11206 if (maxStateObjSize < 0) {
11207 maxStateObjSize = 0;
11210 uint64_t scSize;
11211 rv = scContainer->GetSerializedNBytes(&scSize);
11212 NS_ENSURE_SUCCESS(rv, rv);
11214 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11216 // Step B: Resolve aURL.
11217 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11218 // step 7.
11219 bool equalURIs = true;
11220 nsCOMPtr<nsIURI> currentURI;
11221 if (mCurrentURI) {
11222 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11223 } else {
11224 currentURI = mCurrentURI;
11226 nsCOMPtr<nsIURI> newURI;
11227 if (aURL.Length() == 0) {
11228 newURI = currentURI;
11229 } else {
11230 // 7.1: Resolve aURL relative to mURI
11232 nsIURI* docBaseURI = document->GetDocBaseURI();
11233 if (!docBaseURI) {
11234 return NS_ERROR_FAILURE;
11237 nsAutoCString spec;
11238 docBaseURI->GetSpec(spec);
11240 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11241 document->GetDocumentCharacterSet(), docBaseURI);
11243 // 7.2: If 2a fails, raise a SECURITY_ERR
11244 if (NS_FAILED(rv)) {
11245 return NS_ERROR_DOM_SECURITY_ERR;
11248 // 7.4 and 7.5: Same-origin check.
11249 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11250 // In addition to checking that the security manager says that
11251 // the new URI has the same origin as our current URI, we also
11252 // check that the two URIs have the same userpass. (The
11253 // security manager says that |http://foo.com| and
11254 // |http://me@foo.com| have the same origin.) currentURI
11255 // won't contain the password part of the userpass, so this
11256 // means that it's never valid to specify a password in a
11257 // pushState or replaceState URI.
11259 nsCOMPtr<nsIScriptSecurityManager> secMan =
11260 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11261 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11263 // It's very important that we check that newURI is of the same
11264 // origin as currentURI, not docBaseURI, because a page can
11265 // set docBaseURI arbitrarily to any domain.
11266 nsAutoCString currentUserPass, newUserPass;
11267 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11268 NS_ERROR_FAILURE);
11269 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11270 bool isPrivateWin =
11271 document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
11273 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11274 isPrivateWin)) ||
11275 !currentUserPass.Equals(newUserPass)) {
11276 return NS_ERROR_DOM_SECURITY_ERR;
11278 } else {
11279 // It's a file:// URI
11280 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11282 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11283 newURI, false, document->InnerWindowID()))) {
11284 return NS_ERROR_DOM_SECURITY_ERR;
11288 if (currentURI) {
11289 currentURI->Equals(newURI, &equalURIs);
11290 } else {
11291 equalURIs = false;
11294 } // end of same-origin check
11296 // Step 8: call "URL and history update steps"
11297 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11298 currentURI, equalURIs);
11299 NS_ENSURE_SUCCESS(rv, rv);
11301 return NS_OK;
11304 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11305 nsIStructuredCloneContainer* aData,
11306 const nsAString& aTitle, bool aReplace,
11307 nsIURI* aCurrentURI, bool aEqualURIs) {
11308 // Implements
11309 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11311 // If we have a pending title change, handle it before creating a new entry.
11312 aDocument->DoNotifyPossibleTitleChange();
11314 // Step 2, if aReplace is false: Create a new entry in the session
11315 // history. This will erase all SHEntries after the new entry and make this
11316 // entry the current one. This operation may modify mOSHE, which we need
11317 // later, so we keep a reference here.
11318 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11319 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11321 // If this push/replaceState changed the document's current URI and the new
11322 // URI differs from the old URI in more than the hash, or if the old
11323 // SHEntry's URI was modified in this way by a push/replaceState call
11324 // set URIWasModified to true for the current SHEntry (bug 669671).
11325 bool sameExceptHashes = true;
11326 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11327 bool uriWasModified;
11328 if (sameExceptHashes) {
11329 if (mozilla::SessionHistoryInParent()) {
11330 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11331 } else {
11332 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11334 } else {
11335 uriWasModified = true;
11338 mLoadType = LOAD_PUSHSTATE;
11340 nsCOMPtr<nsISHEntry> newSHEntry;
11341 if (!aReplace) {
11342 // Step 2.
11344 // Step 2.2, "Remove any tasks queued by the history traversal task
11345 // source that are associated with any Document objects in the
11346 // top-level browsing context's document family." This is very hard in
11347 // SessionHistoryInParent since we can't synchronously access the
11348 // pending navigations that are already sent to the parent. We can
11349 // abort any AsyncGo navigations that are waiting to be sent. If we
11350 // send a message to the parent, it would be processed after any
11351 // navigations previously sent. So long as we consider the "history
11352 // traversal task source" to be the list in this process we match the
11353 // spec. If we move the entire list to the parent, we can handle the
11354 // aborting of loads there, but we don't have a way to synchronously
11355 // remove entries as we do here for non-SHIP.
11356 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11357 if (shistory) {
11358 shistory->RemovePendingHistoryNavigations();
11361 nsPoint scrollPos = GetCurScrollPos();
11363 bool scrollRestorationIsManual;
11364 if (mozilla::SessionHistoryInParent()) {
11365 // FIXME Need to save the current scroll position on mActiveEntry.
11366 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11367 } else {
11368 // Save the current scroll position (bug 590573). Step 2.3.
11369 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11371 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11374 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11376 if (mozilla::SessionHistoryInParent()) {
11377 MOZ_LOG(gSHLog, LogLevel::Debug,
11378 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11379 nsString title(mActiveEntry->GetTitle());
11380 UpdateActiveEntry(false,
11381 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11382 /* aOriginalURI = */ nullptr,
11383 /* aReferrerInfo = */ nullptr,
11384 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11385 csp, title, scrollRestorationIsManual, aData,
11386 uriWasModified);
11387 } else {
11388 // Since we're not changing which page we have loaded, pass
11389 // true for aCloneChildren.
11390 nsresult rv = AddToSessionHistory(
11391 aNewURI, nullptr,
11392 aDocument->NodePrincipal(), // triggeringPrincipal
11393 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11394 NS_ENSURE_SUCCESS(rv, rv);
11396 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11398 // Session history entries created by pushState inherit scroll restoration
11399 // mode from the current entry.
11400 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11402 nsString title;
11403 mOSHE->GetTitle(title);
11405 // Set the new SHEntry's title (bug 655273).
11406 newSHEntry->SetTitle(title);
11408 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11409 // two entries correspond to the same document.
11410 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11411 NS_ERROR_FAILURE);
11413 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11414 // we'll just set mOSHE here.
11415 mOSHE = newSHEntry;
11417 } else if (mozilla::SessionHistoryInParent()) {
11418 MOZ_LOG(gSHLog, LogLevel::Debug,
11419 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11420 this, mActiveEntry.get()));
11421 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11422 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11423 // in our case. We could also set it to aNewURI, with the same result.
11424 // We don't use aTitle here, see bug 544535.
11425 nsString title;
11426 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11427 if (mActiveEntry) {
11428 title = mActiveEntry->GetTitle();
11429 referrerInfo = mActiveEntry->GetReferrerInfo();
11430 } else {
11431 referrerInfo = nullptr;
11433 UpdateActiveEntry(
11434 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11435 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
11436 aDocument->GetCsp(), title,
11437 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11438 uriWasModified);
11439 } else {
11440 // Step 3.
11441 newSHEntry = mOSHE;
11443 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11444 // Since we're not changing which page we have loaded, pass
11445 // true for aCloneChildren.
11446 if (!newSHEntry) {
11447 nsresult rv = AddToSessionHistory(
11448 aNewURI, nullptr,
11449 aDocument->NodePrincipal(), // triggeringPrincipal
11450 nullptr, nullptr, aDocument->GetCsp(), true,
11451 getter_AddRefs(newSHEntry));
11452 NS_ENSURE_SUCCESS(rv, rv);
11453 mOSHE = newSHEntry;
11456 newSHEntry->SetURI(aNewURI);
11457 newSHEntry->SetOriginalURI(aNewURI);
11458 // We replaced the URI of the entry, clear the unstripped URI as it
11459 // shouldn't be used for reloads anymore.
11460 newSHEntry->SetUnstrippedURI(nullptr);
11461 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11462 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11463 // in our case. We could also set it to aNewURI, with the same result.
11464 newSHEntry->SetResultPrincipalURI(nullptr);
11465 newSHEntry->SetLoadReplace(false);
11468 if (!mozilla::SessionHistoryInParent()) {
11469 // Step 2.4 and 3: Modify new/original session history entry and clear its
11470 // POST data, if there is any.
11471 newSHEntry->SetStateData(aData);
11472 newSHEntry->SetPostData(nullptr);
11474 newSHEntry->SetURIWasModified(uriWasModified);
11476 // Step E as described at the top of AddState: If aReplace is false,
11477 // indicating that we're doing a pushState rather than a replaceState,
11478 // notify bfcache that we've added a page to the history so it can evict
11479 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11480 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11481 // root session history if this call is coming from a document.open() in a
11482 // docshell subtree that disables session history.
11483 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11484 if (rootSH) {
11485 rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry,
11486 aReplace);
11490 // Step 4: If the document's URI changed, update document's URI and update
11491 // global history.
11493 // We need to call FireOnLocationChange so that the browser's address bar
11494 // gets updated and the back button is enabled, but we only need to
11495 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11496 // since SetCurrentURI will call FireOnLocationChange for us.
11498 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11499 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11500 // notification is allowed only when we know docshell is not loading a new
11501 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11502 // FireOnLocationChange(...) breaks security UI.
11504 // If the docshell is shutting down, don't update the document URI, as we
11505 // can't load into a docshell that is being destroyed.
11506 if (!aEqualURIs && !mIsBeingDestroyed) {
11507 aDocument->SetDocumentURI(aNewURI);
11508 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11509 /* aIsInitialAboutBlank */ false,
11510 GetSameDocumentNavigationFlags(aNewURI));
11512 AddURIVisit(aNewURI, aCurrentURI, 0);
11514 // AddURIVisit doesn't set the title for the new URI in global history,
11515 // so do that here.
11516 UpdateGlobalHistoryTitle(aNewURI);
11518 // Inform the favicon service that our old favicon applies to this new
11519 // URI.
11520 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11521 } else {
11522 FireDummyOnLocationChange();
11524 aDocument->SetStateObject(aData);
11526 return NS_OK;
11529 NS_IMETHODIMP
11530 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11531 if (mozilla::SessionHistoryInParent()) {
11532 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11533 return NS_OK;
11536 *aIsManual = false;
11537 if (mOSHE) {
11538 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11541 return NS_OK;
11544 NS_IMETHODIMP
11545 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11546 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11548 return NS_OK;
11551 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11552 nsISHEntry* aSHEntry, bool aIsManual) {
11553 if (aSHEntry) {
11554 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11557 if (mActiveEntry && mBrowsingContext) {
11558 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11559 if (XRE_IsParentProcess()) {
11560 SessionHistoryEntry* entry =
11561 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11562 if (entry) {
11563 entry->SetScrollRestorationIsManual(aIsManual);
11565 } else {
11566 mozilla::Unused << ContentChild::GetSingleton()
11567 ->SendSessionHistoryEntryScrollRestorationIsManual(
11568 mBrowsingContext, aIsManual);
11573 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11574 uint32_t aCacheKey) {
11575 if (aSHEntry) {
11576 aSHEntry->SetCacheKey(aCacheKey);
11579 if (mActiveEntry && mBrowsingContext) {
11580 mActiveEntry->SetCacheKey(aCacheKey);
11581 if (XRE_IsParentProcess()) {
11582 SessionHistoryEntry* entry =
11583 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11584 if (entry) {
11585 entry->SetCacheKey(aCacheKey);
11587 } else {
11588 mozilla::Unused
11589 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11590 mBrowsingContext, aCacheKey);
11595 /* static */
11596 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11597 // I believe none of the about: urls should go in the history. But then
11598 // that could just be me... If the intent is only deny about:blank then we
11599 // should just do a spec compare, rather than two gets of the scheme and
11600 // then the path. -Gagan
11601 nsresult rv;
11602 nsAutoCString buf;
11604 rv = aURI->GetScheme(buf);
11605 if (NS_FAILED(rv)) {
11606 return false;
11609 if (buf.EqualsLiteral("about")) {
11610 rv = aURI->GetPathQueryRef(buf);
11611 if (NS_FAILED(rv)) {
11612 return false;
11615 if (buf.EqualsLiteral("blank")) {
11616 return false;
11618 // We only want to add about:newtab if it's not privileged, and
11619 // if it is not configured to show the blank page.
11620 if (buf.EqualsLiteral("newtab")) {
11621 if (!StaticPrefs::browser_newtabpage_enabled()) {
11622 return false;
11625 NS_ENSURE_TRUE(aChannel, false);
11626 nsCOMPtr<nsIPrincipal> resultPrincipal;
11627 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11628 aChannel, getter_AddRefs(resultPrincipal));
11629 NS_ENSURE_SUCCESS(rv, false);
11630 return !resultPrincipal->IsSystemPrincipal();
11634 return true;
11637 nsresult nsDocShell::AddToSessionHistory(
11638 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11639 nsIPrincipal* aPrincipalToInherit,
11640 nsIPrincipal* aPartitionedPrincipalToInherit,
11641 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11642 nsISHEntry** aNewEntry) {
11643 MOZ_ASSERT(aURI, "uri is null");
11644 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11645 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11647 #if defined(DEBUG)
11648 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11649 nsAutoCString chanName;
11650 if (aChannel) {
11651 aChannel->GetName(chanName);
11652 } else {
11653 chanName.AssignLiteral("<no channel>");
11656 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11657 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11658 aURI->GetSpecOrDefault().get(), chanName.get()));
11660 #endif
11662 nsresult rv = NS_OK;
11663 nsCOMPtr<nsISHEntry> entry;
11666 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11667 * the existing SH entry in the page and replace the url and
11668 * other vitalities.
11670 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11671 !mBrowsingContext->IsTop()) {
11672 // This is a subframe
11673 entry = mOSHE;
11674 if (entry) {
11675 entry->ClearEntry();
11679 // Create a new entry if necessary.
11680 if (!entry) {
11681 entry = new nsSHEntry();
11684 // Get the post data & referrer
11685 nsCOMPtr<nsIInputStream> inputStream;
11686 nsCOMPtr<nsIURI> originalURI;
11687 nsCOMPtr<nsIURI> resultPrincipalURI;
11688 nsCOMPtr<nsIURI> unstrippedURI;
11689 bool loadReplace = false;
11690 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11691 uint32_t cacheKey = 0;
11692 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11693 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11694 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11695 aPartitionedPrincipalToInherit;
11696 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11697 bool expired = false; // by default the page is not expired
11698 bool discardLayoutState = false;
11699 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11700 bool userActivation = false;
11702 if (aChannel) {
11703 cacheChannel = do_QueryInterface(aChannel);
11705 /* If there is a caching channel, get the Cache Key and store it
11706 * in SH.
11708 if (cacheChannel) {
11709 cacheChannel->GetCacheKey(&cacheKey);
11711 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11713 // Check if the httpChannel is hiding under a multipartChannel
11714 if (!httpChannel) {
11715 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11717 if (httpChannel) {
11718 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11719 if (uploadChannel) {
11720 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11722 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11723 uint32_t loadFlags;
11724 aChannel->GetLoadFlags(&loadFlags);
11725 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11726 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11727 MOZ_ASSERT(NS_SUCCEEDED(rv));
11729 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11732 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11733 if (!triggeringPrincipal) {
11734 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11736 if (!csp) {
11737 csp = loadInfo->GetCspToInherit();
11740 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11742 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
11744 userActivation = loadInfo->GetHasValidUserGestureActivation();
11746 // For now keep storing just the principal in the SHEntry.
11747 if (!principalToInherit) {
11748 if (loadInfo->GetLoadingSandboxed()) {
11749 if (loadInfo->GetLoadingPrincipal()) {
11750 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11751 loadInfo->GetLoadingPrincipal());
11752 } else {
11753 // get the OriginAttributes
11754 OriginAttributes attrs;
11755 loadInfo->GetOriginAttributes(&attrs);
11756 principalToInherit = NullPrincipal::Create(attrs);
11758 } else {
11759 principalToInherit = loadInfo->PrincipalToInherit();
11763 if (!partitionedPrincipalToInherit) {
11764 // XXXehsan is it correct to fall back to the principal to inherit in all
11765 // cases? For example, what about the cases where we are using the load
11766 // info's principal to inherit? Do we need to add a similar concept to
11767 // load info for partitioned principal?
11768 partitionedPrincipalToInherit = principalToInherit;
11772 nsAutoString srcdoc;
11773 bool srcdocEntry = false;
11774 nsCOMPtr<nsIURI> baseURI;
11776 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11777 if (inStrmChan) {
11778 bool isSrcdocChannel;
11779 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11780 if (isSrcdocChannel) {
11781 inStrmChan->GetSrcdocData(srcdoc);
11782 srcdocEntry = true;
11783 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11784 } else {
11785 srcdoc.SetIsVoid(true);
11788 /* If cache got a 'no-store', ask SH not to store
11789 * HistoryLayoutState. By default, SH will set this
11790 * flag to true and save HistoryLayoutState.
11792 bool saveLayoutState = !discardLayoutState;
11794 if (cacheChannel) {
11795 // Check if the page has expired from cache
11796 uint32_t expTime = 0;
11797 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11798 uint32_t now = PRTimeToSeconds(PR_Now());
11799 if (expTime <= now) {
11800 expired = true;
11804 // Title is set in nsDocShell::SetTitle()
11805 entry->Create(aURI, // uri
11806 u""_ns, // Title
11807 inputStream, // Post data stream
11808 cacheKey, // CacheKey
11809 mContentTypeHint, // Content-type
11810 triggeringPrincipal, // Channel or provided principal
11811 principalToInherit, partitionedPrincipalToInherit, csp,
11812 HistoryID(), GetCreatedDynamically(), originalURI,
11813 resultPrincipalURI, unstrippedURI, loadReplace, referrerInfo,
11814 srcdoc, srcdocEntry, baseURI, saveLayoutState, expired,
11815 userActivation);
11817 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11818 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11819 Maybe<int32_t> previousEntryIndex;
11820 Maybe<int32_t> loadedEntryIndex;
11821 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11822 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11823 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11825 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11826 if (previousEntryIndex.isSome()) {
11827 mPreviousEntryIndex = previousEntryIndex.value();
11829 if (loadedEntryIndex.isSome()) {
11830 mLoadedEntryIndex = loadedEntryIndex.value();
11833 // aCloneChildren implies that we are retaining the same document, thus we
11834 // need to signal to the top WC that the new SHEntry may receive a fresh
11835 // user interaction flag.
11836 if (aCloneChildren) {
11837 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11838 if (topWc && !topWc->IsDiscarded()) {
11839 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11842 } else {
11843 // This is a subframe, make sure that this new SHEntry will be
11844 // marked with user interaction.
11845 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11846 if (topWc && !topWc->IsDiscarded()) {
11847 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11849 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11850 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11851 aCloneChildren);
11855 // Return the new SH entry...
11856 if (aNewEntry) {
11857 *aNewEntry = nullptr;
11858 if (NS_SUCCEEDED(rv)) {
11859 entry.forget(aNewEntry);
11863 return rv;
11866 void nsDocShell::UpdateActiveEntry(
11867 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11868 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
11869 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
11870 const nsAString& aTitle, bool aScrollRestorationIsManual,
11871 nsIStructuredCloneContainer* aData, bool aURIWasModified) {
11872 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11873 MOZ_ASSERT(aURI, "uri is null");
11874 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
11875 "This code only deals with pushState");
11876 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
11878 MOZ_LOG(gSHLog, LogLevel::Debug,
11879 ("Creating an active entry on nsDocShell %p to %s", this,
11880 aURI->GetSpecOrDefault().get()));
11882 // Even if we're replacing an existing entry we create new a
11883 // SessionHistoryInfo. In the parent process we'll keep the existing
11884 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11885 // entry keeps identity but its data is replaced.
11886 bool replace = aReplace && mActiveEntry;
11888 if (!replace) {
11889 CollectWireframe();
11892 if (mActiveEntry) {
11893 // Link this entry to the previous active entry.
11894 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
11895 } else {
11896 mActiveEntry = MakeUnique<SessionHistoryInfo>(
11897 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
11899 mActiveEntry->SetOriginalURI(aOriginalURI);
11900 mActiveEntry->SetUnstrippedURI(nullptr);
11901 mActiveEntry->SetReferrerInfo(aReferrerInfo);
11902 mActiveEntry->SetTitle(aTitle);
11903 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
11904 mActiveEntry->SetURIWasModified(aURIWasModified);
11905 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
11907 if (replace) {
11908 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
11909 } else {
11910 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
11911 // FIXME We should probably just compute mChildOffset in the parent
11912 // instead of passing it over IPC here.
11913 mBrowsingContext->SetActiveSessionHistoryEntry(
11914 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
11915 /* aCacheKey = */ 0);
11916 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
11920 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
11921 bool aUserActivation) {
11922 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11924 nsresult rv;
11925 RefPtr<nsDocShellLoadState> loadState;
11926 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
11927 NS_ENSURE_SUCCESS(rv, rv);
11929 // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if
11930 // that's the only thing holding a ref to aEntry that will cause aEntry to
11931 // die while we're loading it. So hold a strong ref to aEntry here, just
11932 // in case.
11933 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11935 loadState->SetHasValidUserGestureActivation(
11936 loadState->HasValidUserGestureActivation() || aUserActivation);
11938 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
11941 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
11942 uint32_t aLoadType,
11943 bool aUserActivation) {
11944 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
11945 loadState->SetHasValidUserGestureActivation(
11946 loadState->HasValidUserGestureActivation() || aUserActivation);
11948 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
11951 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
11952 uint32_t aLoadType,
11953 bool aLoadingCurrentEntry) {
11954 if (!IsNavigationAllowed()) {
11955 return NS_OK;
11958 // We are setting load type afterwards so we don't have to
11959 // send it in an IPC message
11960 aLoadState->SetLoadType(aLoadType);
11962 nsresult rv;
11963 if (SchemeIsJavascript(aLoadState->URI())) {
11964 // We're loading a URL that will execute script from inside asyncOpen.
11965 // Replace the current document with about:blank now to prevent
11966 // anything from the current document from leaking into any JavaScript
11967 // code in the URL.
11968 // Don't cache the presentation if we're going to just reload the
11969 // current entry. Caching would lead to trying to save the different
11970 // content viewers in the same nsISHEntry object.
11971 rv = CreateAboutBlankDocumentViewer(
11972 aLoadState->PrincipalToInherit(),
11973 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
11974 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
11976 if (NS_FAILED(rv)) {
11977 // The creation of the intermittent about:blank content
11978 // viewer failed for some reason (potentially because the
11979 // user prevented it). Interrupt the history load.
11980 return NS_OK;
11983 if (!aLoadState->TriggeringPrincipal()) {
11984 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
11985 // URIs will pick it up from the about:blank page we just loaded,
11986 // and we don't really want even that in this case.
11987 nsCOMPtr<nsIPrincipal> principal =
11988 NullPrincipal::Create(GetOriginAttributes());
11989 aLoadState->SetTriggeringPrincipal(principal);
11993 /* If there is a valid postdata *and* the user pressed
11994 * reload or shift-reload, take user's permission before we
11995 * repost the data to the server.
11997 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
11998 bool repost;
11999 rv = ConfirmRepost(&repost);
12000 if (NS_FAILED(rv)) {
12001 return rv;
12004 // If the user pressed cancel in the dialog, return. We're done here.
12005 if (!repost) {
12006 return NS_BINDING_ABORTED;
12010 // If there is no valid triggeringPrincipal, we deny the load
12011 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
12012 "need a valid triggeringPrincipal to load from history");
12013 if (!aLoadState->TriggeringPrincipal()) {
12014 return NS_ERROR_FAILURE;
12017 return InternalLoad(aLoadState); // No nsIRequest
12020 NS_IMETHODIMP
12021 nsDocShell::PersistLayoutHistoryState() {
12022 nsresult rv = NS_OK;
12024 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12025 bool scrollRestorationIsManual;
12026 if (mozilla::SessionHistoryInParent()) {
12027 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12028 } else {
12029 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12031 nsCOMPtr<nsILayoutHistoryState> layoutState;
12032 if (RefPtr<PresShell> presShell = GetPresShell()) {
12033 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12034 } else if (scrollRestorationIsManual) {
12035 // Even if we don't have layout anymore, we may want to reset the
12036 // current scroll state in layout history.
12037 GetLayoutHistoryState(getter_AddRefs(layoutState));
12040 if (scrollRestorationIsManual && layoutState) {
12041 layoutState->ResetScrollState();
12045 return rv;
12048 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12049 nsISHEntry* aNewEntry) {
12050 if (aOldEntry == mOSHE) {
12051 mOSHE = aNewEntry;
12054 if (aOldEntry == mLSHE) {
12055 mLSHE = aNewEntry;
12059 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12060 const Maybe<nsISHEntry*>& aOSHE) {
12061 // We want to hold on to the reference in mLSHE before we update it.
12062 // Otherwise, SetHistoryEntry could release the last reference to
12063 // the entry while aOSHE is pointing to it.
12064 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12065 if (aLSHE.isSome()) {
12066 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12067 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12069 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12070 if (aOSHE.isSome()) {
12071 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12072 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12076 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12077 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12078 // We need to sync up the docshell and session history trees for
12079 // subframe navigation. If the load was in a subframe, we forward up to
12080 // the root docshell, which will then recursively sync up all docshells
12081 // to their corresponding entries in the new session history tree.
12082 // If we don't do this, then we can cache a content viewer on the wrong
12083 // cloned entry, and subsequently restore it at the wrong time.
12084 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12085 if (topBC->IsDiscarded()) {
12086 topBC = nullptr;
12088 RefPtr<BrowsingContext> currBC =
12089 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12090 if (topBC && *aPtr) {
12091 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12093 nsCOMPtr<nsISHEntry> entry(aEntry);
12094 entry.swap(*aPtr);
12095 return entry.forget();
12098 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12099 RefPtr<ChildSHistory> childSHistory =
12100 mBrowsingContext->Top()->GetChildSessionHistory();
12101 return childSHistory.forget();
12104 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12105 nsIHttpChannel** aReturn) {
12106 NS_ENSURE_ARG_POINTER(aReturn);
12107 if (!aChannel) {
12108 return NS_ERROR_FAILURE;
12111 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12112 if (multiPartChannel) {
12113 nsCOMPtr<nsIChannel> baseChannel;
12114 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12115 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12116 *aReturn = httpChannel;
12117 NS_IF_ADDREF(*aReturn);
12119 return NS_OK;
12122 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12123 // By default layout State will be saved.
12124 if (!aChannel) {
12125 return false;
12128 // figure out if SH should be saving layout state
12129 bool noStore = false;
12130 Unused << aChannel->IsNoStoreResponse(&noStore);
12131 return noStore;
12134 NS_IMETHODIMP
12135 nsDocShell::GetEditor(nsIEditor** aEditor) {
12136 NS_ENSURE_ARG_POINTER(aEditor);
12137 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12138 htmlEditor.forget(aEditor);
12139 return NS_OK;
12142 NS_IMETHODIMP
12143 nsDocShell::SetEditor(nsIEditor* aEditor) {
12144 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12145 // If TextEditor comes, throw an error.
12146 if (aEditor && !htmlEditor) {
12147 return NS_ERROR_INVALID_ARG;
12149 return SetHTMLEditorInternal(htmlEditor);
12152 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12153 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12156 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12157 if (!aHTMLEditor && !mEditorData) {
12158 return NS_OK;
12161 nsresult rv = EnsureEditorData();
12162 if (NS_FAILED(rv)) {
12163 return rv;
12166 return mEditorData->SetHTMLEditor(aHTMLEditor);
12169 NS_IMETHODIMP
12170 nsDocShell::GetEditable(bool* aEditable) {
12171 NS_ENSURE_ARG_POINTER(aEditable);
12172 *aEditable = mEditorData && mEditorData->GetEditable();
12173 return NS_OK;
12176 NS_IMETHODIMP
12177 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12178 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12180 if (mEditorData) {
12181 *aHasEditingSession = !!mEditorData->GetEditingSession();
12182 } else {
12183 *aHasEditingSession = false;
12186 return NS_OK;
12189 NS_IMETHODIMP
12190 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12191 nsresult rv = EnsureEditorData();
12192 if (NS_FAILED(rv)) {
12193 return rv;
12196 return mEditorData->MakeEditable(aInWaitForUriLoad);
12199 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12200 bool needToAddURIVisit = true;
12201 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12202 if (props) {
12203 mozilla::Unused << props->GetPropertyAsBool(
12204 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12207 return needToAddURIVisit;
12210 /* static */ void nsDocShell::ExtractLastVisit(
12211 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12212 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12213 if (!props) {
12214 return;
12217 nsresult rv;
12218 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12219 if (NS_SUCCEEDED(rv)) {
12220 uri.forget(aURI);
12222 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12223 aChannelRedirectFlags);
12225 NS_WARNING_ASSERTION(
12226 NS_SUCCEEDED(rv),
12227 "Could not fetch previous flags, URI will be treated like referrer");
12229 } else {
12230 // There is no last visit for this channel, so this must be the first
12231 // link. Link the visit to the referrer of this request, if any.
12232 // Treat referrer as null if there is an error getting it.
12233 NS_GetReferrerFromChannel(aChannel, aURI);
12237 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12238 uint32_t aChannelRedirectFlags) {
12239 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12240 if (!props || !aURI) {
12241 return;
12244 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12245 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12246 aChannelRedirectFlags);
12249 /* static */ void nsDocShell::InternalAddURIVisit(
12250 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12251 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12252 nsIWidget* aWidget, uint32_t aLoadType, bool aWasUpgraded) {
12253 MOZ_ASSERT(aURI, "Visited URI is null!");
12254 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12255 "Do not add error or bypass pages to global history");
12257 bool usePrivateBrowsing = false;
12258 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12260 // Only content-type docshells save URI visits. Also don't do
12261 // anything here if we're not supposed to use global history.
12262 if (!aBrowsingContext->IsContent() ||
12263 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12264 return;
12267 nsCOMPtr<IHistory> history = components::History::Service();
12269 if (history) {
12270 uint32_t visitURIFlags = 0;
12272 if (aBrowsingContext->IsTop()) {
12273 visitURIFlags |= IHistory::TOP_LEVEL;
12276 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12277 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12278 } else if (aChannelRedirectFlags &
12279 nsIChannelEventSink::REDIRECT_PERMANENT) {
12280 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12281 } else {
12282 MOZ_ASSERT(!aChannelRedirectFlags,
12283 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12284 "if any flags in aChannelRedirectFlags is set.");
12287 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12288 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12289 if (aResponseStatus == 301 || aResponseStatus == 308) {
12290 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12293 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12294 // simple retry attempt by the user is unlikely to solve them.
12295 // 408 is special cased, since may actually indicate a temporary
12296 // connection problem.
12297 else if (aResponseStatus != 408 &&
12298 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12299 aResponseStatus == 505)) {
12300 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12303 if (aWasUpgraded) {
12304 visitURIFlags |=
12305 IHistory::REDIRECT_SOURCE | IHistory::REDIRECT_SOURCE_UPGRADED;
12308 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12309 visitURIFlags,
12310 aBrowsingContext->BrowserId());
12314 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12315 uint32_t aChannelRedirectFlags,
12316 uint32_t aResponseStatus) {
12317 nsPIDOMWindowOuter* outer = GetWindow();
12318 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12320 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12321 aResponseStatus, mBrowsingContext, widget, mLoadType,
12322 false);
12325 //*****************************************************************************
12326 // nsDocShell: Helper Routines
12327 //*****************************************************************************
12329 NS_IMETHODIMP
12330 nsDocShell::SetLoadType(uint32_t aLoadType) {
12331 mLoadType = aLoadType;
12332 return NS_OK;
12335 NS_IMETHODIMP
12336 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12337 *aLoadType = mLoadType;
12338 return NS_OK;
12341 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12342 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12343 *aRepost = true;
12344 return NS_OK;
12347 nsCOMPtr<nsIPromptCollection> prompter =
12348 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12349 if (!prompter) {
12350 return NS_ERROR_NOT_AVAILABLE;
12353 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12356 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12357 nsIStringBundle** aStringBundle) {
12358 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12359 NS_ERROR_FAILURE);
12361 nsCOMPtr<nsIStringBundleService> stringBundleService =
12362 mozilla::components::StringBundle::Service();
12363 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12365 NS_ENSURE_SUCCESS(
12366 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12367 NS_ERROR_FAILURE);
12369 return NS_OK;
12372 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12373 PresShell* presShell = GetPresShell();
12374 NS_ENSURE_TRUE(presShell, nullptr);
12376 return presShell->GetRootScrollFrameAsScrollable();
12379 nsresult nsDocShell::EnsureScriptEnvironment() {
12380 if (mScriptGlobal) {
12381 return NS_OK;
12384 if (mIsBeingDestroyed) {
12385 return NS_ERROR_NOT_AVAILABLE;
12388 #ifdef DEBUG
12389 NS_ASSERTION(!mInEnsureScriptEnv,
12390 "Infinite loop! Calling EnsureScriptEnvironment() from "
12391 "within EnsureScriptEnvironment()!");
12393 // Yeah, this isn't re-entrant safe, but that's ok since if we
12394 // re-enter this method, we'll infinitely loop...
12395 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12396 mInEnsureScriptEnv = true;
12397 #endif
12399 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12400 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12402 uint32_t chromeFlags;
12403 browserChrome->GetChromeFlags(&chromeFlags);
12405 // If our window is modal and we're not opened as chrome, make
12406 // this window a modal content window.
12407 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12408 MOZ_ASSERT(mScriptGlobal);
12410 // Ensure the script object is set up to run script.
12411 return mScriptGlobal->EnsureScriptEnvironment();
12414 nsresult nsDocShell::EnsureEditorData() {
12415 MOZ_ASSERT(!mIsBeingDestroyed);
12417 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12418 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12419 // We shouldn't recreate the editor data if it already exists, or
12420 // we're shutting down, or we already have a detached editor data
12421 // stored in the session history. We should only have one editordata
12422 // per docshell.
12423 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12426 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12429 nsresult nsDocShell::EnsureFind() {
12430 if (!mFind) {
12431 mFind = new nsWebBrowserFind();
12434 // we promise that the nsIWebBrowserFind that we return has been set
12435 // up to point to the focused, or content window, so we have to
12436 // set that up each time.
12438 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12439 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12441 // default to our window
12442 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12443 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12444 nsFocusManager::GetFocusedDescendant(ourWindow,
12445 nsFocusManager::eIncludeAllDescendants,
12446 getter_AddRefs(windowToSearch));
12448 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12449 if (!findInFrames) {
12450 return NS_ERROR_NO_INTERFACE;
12453 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12454 if (NS_FAILED(rv)) {
12455 return rv;
12457 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12458 if (NS_FAILED(rv)) {
12459 return rv;
12462 return NS_OK;
12465 NS_IMETHODIMP
12466 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12467 NS_ENSURE_ARG(aDoomed);
12468 *aDoomed = mIsBeingDestroyed;
12469 return NS_OK;
12472 NS_IMETHODIMP
12473 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12474 NS_ENSURE_ARG(aResult);
12475 *aResult = mIsExecutingOnLoadHandler;
12476 return NS_OK;
12479 NS_IMETHODIMP
12480 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12481 nsCOMPtr<nsILayoutHistoryState> state;
12482 if (mozilla::SessionHistoryInParent()) {
12483 if (mActiveEntry) {
12484 state = mActiveEntry->GetLayoutHistoryState();
12486 } else {
12487 if (mOSHE) {
12488 state = mOSHE->GetLayoutHistoryState();
12491 state.forget(aLayoutHistoryState);
12492 return NS_OK;
12495 NS_IMETHODIMP
12496 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12497 if (mOSHE) {
12498 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12500 if (mActiveEntry) {
12501 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12503 return NS_OK;
12506 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12507 nsIInterfaceRequestor* aRequestor) {
12508 if (aRequestor) {
12509 mWeakPtr = do_GetWeakReference(aRequestor);
12513 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12514 mWeakPtr = nullptr;
12517 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12519 NS_IMETHODIMP
12520 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12521 void** aSink) {
12522 NS_ENSURE_ARG_POINTER(aSink);
12523 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12524 if (ifReq) {
12525 return ifReq->GetInterface(aIID, aSink);
12527 *aSink = nullptr;
12528 return NS_NOINTERFACE;
12531 //*****************************************************************************
12532 // nsDocShell::nsIAuthPromptProvider
12533 //*****************************************************************************
12535 NS_IMETHODIMP
12536 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12537 void** aResult) {
12538 // a priority prompt request will override a false mAllowAuth setting
12539 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12541 if (!mAllowAuth && !priorityPrompt) {
12542 return NS_ERROR_NOT_AVAILABLE;
12545 // we're either allowing auth, or it's a proxy request
12546 nsresult rv;
12547 nsCOMPtr<nsIPromptFactory> wwatch =
12548 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12549 NS_ENSURE_SUCCESS(rv, rv);
12551 rv = EnsureScriptEnvironment();
12552 NS_ENSURE_SUCCESS(rv, rv);
12554 // Get the an auth prompter for our window so that the parenting
12555 // of the dialogs works as it should when using tabs.
12557 return wwatch->GetPrompt(mScriptGlobal, aIID,
12558 reinterpret_cast<void**>(aResult));
12561 //*****************************************************************************
12562 // nsDocShell::nsILoadContext
12563 //*****************************************************************************
12565 NS_IMETHODIMP
12566 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12567 CallGetInterface(this, aWindow);
12568 return NS_OK;
12571 NS_IMETHODIMP
12572 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12573 return mBrowsingContext->GetTopWindow(aWindow);
12576 NS_IMETHODIMP
12577 nsDocShell::GetTopFrameElement(Element** aElement) {
12578 return mBrowsingContext->GetTopFrameElement(aElement);
12581 NS_IMETHODIMP
12582 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12583 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12586 NS_IMETHODIMP
12587 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12588 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12591 NS_IMETHODIMP
12592 nsDocShell::GetIsContent(bool* aIsContent) {
12593 *aIsContent = (mItemType == typeContent);
12594 return NS_OK;
12597 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12598 MOZ_ASSERT(aURI, "Must have a URI!");
12600 if (!mFiredUnloadEvent) {
12601 return true;
12604 if (!mLoadingURI) {
12605 return false;
12608 bool isPrivateWin = false;
12609 Document* doc = GetDocument();
12610 if (doc) {
12611 isPrivateWin =
12612 doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
12615 nsCOMPtr<nsIScriptSecurityManager> secMan =
12616 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12617 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12618 aURI, mLoadingURI, false, isPrivateWin));
12622 // Routines for selection and clipboard
12624 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12625 nsIController** aResult) {
12626 NS_ENSURE_ARG_POINTER(aResult);
12627 *aResult = nullptr;
12629 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12631 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12632 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12634 return root->GetControllerForCommand(aCommand, false /* for any window */,
12635 aResult);
12638 NS_IMETHODIMP
12639 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12640 NS_ENSURE_ARG_POINTER(aResult);
12641 *aResult = false;
12643 nsresult rv = NS_ERROR_FAILURE;
12645 nsCOMPtr<nsIController> controller;
12646 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12647 if (controller) {
12648 rv = controller->IsCommandEnabled(aCommand, aResult);
12651 return rv;
12654 NS_IMETHODIMP
12655 nsDocShell::DoCommand(const char* aCommand) {
12656 nsresult rv = NS_ERROR_FAILURE;
12658 nsCOMPtr<nsIController> controller;
12659 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12660 if (controller) {
12661 rv = controller->DoCommand(aCommand);
12664 return rv;
12667 NS_IMETHODIMP
12668 nsDocShell::DoCommandWithParams(const char* aCommand,
12669 nsICommandParams* aParams) {
12670 nsCOMPtr<nsIController> controller;
12671 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12672 if (NS_WARN_IF(NS_FAILED(rv))) {
12673 return rv;
12676 nsCOMPtr<nsICommandController> commandController =
12677 do_QueryInterface(controller, &rv);
12678 if (NS_WARN_IF(NS_FAILED(rv))) {
12679 return rv;
12682 return commandController->DoCommandWithParams(aCommand, aParams);
12685 nsresult nsDocShell::EnsureCommandHandler() {
12686 if (!mCommandManager) {
12687 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12688 mCommandManager = new nsCommandManager(domWindow);
12691 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12694 // link handling
12696 class OnLinkClickEvent : public Runnable {
12697 public:
12698 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12699 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12700 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12702 NS_IMETHOD Run() override {
12703 AutoPopupStatePusher popupStatePusher(mPopupState);
12705 // We need to set up an AutoJSAPI here for the following reason: When we
12706 // do OnLinkClickSync we'll eventually end up in
12707 // nsGlobalWindow::OpenInternal which only does popup blocking if
12708 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12709 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12710 // concerned.
12711 AutoJSAPI jsapi;
12712 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12713 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12714 mTriggeringPrincipal);
12716 return NS_OK;
12719 private:
12720 RefPtr<nsDocShell> mHandler;
12721 nsCOMPtr<nsIContent> mContent;
12722 RefPtr<nsDocShellLoadState> mLoadState;
12723 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12724 PopupBlocker::PopupControlState mPopupState;
12725 bool mNoOpenerImplied;
12726 bool mIsTrusted;
12729 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12730 nsDocShellLoadState* aLoadState,
12731 bool aNoOpenerImplied, bool aIsTrusted,
12732 nsIPrincipal* aTriggeringPrincipal)
12733 : mozilla::Runnable("OnLinkClickEvent"),
12734 mHandler(aHandler),
12735 mContent(aContent),
12736 mLoadState(aLoadState),
12737 mTriggeringPrincipal(aTriggeringPrincipal),
12738 mPopupState(PopupBlocker::GetPopupControlState()),
12739 mNoOpenerImplied(aNoOpenerImplied),
12740 mIsTrusted(aIsTrusted) {}
12742 nsresult nsDocShell::OnLinkClick(
12743 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12744 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12745 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12746 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12747 #ifndef ANDROID
12748 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12749 #endif
12750 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12752 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12753 return NS_OK;
12756 // On history navigation through Back/Forward buttons, don't execute
12757 // automatic JavaScript redirection such as |anchorElement.click()| or
12758 // |formElement.submit()|.
12760 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12761 // nsDocShell::OnLinkClickSync(...) instead.
12762 if (ShouldBlockLoadingForBackButton()) {
12763 return NS_OK;
12766 if (aContent->IsEditable()) {
12767 return NS_OK;
12770 Document* ownerDoc = aContent->OwnerDoc();
12771 if (nsContentUtils::IsExternalProtocol(aURI)) {
12772 ownerDoc->EnsureNotEnteringAndExitFullscreen();
12775 bool noOpenerImplied = false;
12776 nsAutoString target(aTargetSpec);
12777 if (aFileName.IsVoid() &&
12778 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
12779 target = u"_blank";
12780 if (!aTargetSpec.Equals(target)) {
12781 noOpenerImplied = true;
12785 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12786 loadState->SetTarget(target);
12787 loadState->SetFileName(aFileName);
12788 loadState->SetPostDataStream(aPostDataStream);
12789 loadState->SetHeadersStream(aHeadersDataStream);
12790 loadState->SetFirstParty(true);
12791 loadState->SetTriggeringPrincipal(
12792 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12793 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12794 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12795 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12797 nsCOMPtr<nsIRunnable> ev =
12798 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12799 aIsTrusted, aTriggeringPrincipal);
12800 return Dispatch(ev.forget());
12803 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12804 nsIURI* aLinkURI, nsIContent* aContent,
12805 bool aIsUserTriggered) {
12806 if (net::SchemeIsJavascript(aLinkURI)) {
12807 return false;
12810 // External links from within app tabs should always open in new tabs
12811 // instead of replacing the app tab's page (Bug 575561)
12812 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12813 // get either host, just return false to use the original target.
12814 nsAutoCString linkHost;
12815 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12816 return false;
12819 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12820 // privileged code to change the default targeting behaviour. In particular,
12821 // if a user-initiated link click for the (or targetting the) top-level frame
12822 // is detected, we default the target to "_blank" to give it a new
12823 // top-level BrowsingContext.
12824 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
12825 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
12826 aOriginalTarget == u"_top"_ns)) {
12827 return true;
12830 // Don't modify non-default targets.
12831 if (!aOriginalTarget.IsEmpty()) {
12832 return false;
12835 // Only check targets that are in extension panels or app tabs.
12836 // (isAppTab will be false for app tab subframes).
12837 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12838 if (!mmGroup.EqualsLiteral("webext-browsers") &&
12839 !mBrowsingContext->IsAppTab()) {
12840 return false;
12843 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12844 if (!docURI) {
12845 return false;
12848 nsAutoCString docHost;
12849 if (NS_FAILED(docURI->GetHost(docHost))) {
12850 return false;
12853 if (linkHost.Equals(docHost)) {
12854 return false;
12857 // Special case: ignore "www" prefix if it is part of host string
12858 return linkHost.Length() < docHost.Length()
12859 ? !docHost.Equals("www."_ns + linkHost)
12860 : !linkHost.Equals("www."_ns + docHost);
12863 static bool ElementCanHaveNoopener(nsIContent* aContent) {
12864 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12865 // the HTML, XHTML, or SVG namespace.
12866 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
12867 nsGkAtoms::form) ||
12868 aContent->IsSVGElement(nsGkAtoms::a);
12871 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12872 nsDocShellLoadState* aLoadState,
12873 bool aNoOpenerImplied,
12874 nsIPrincipal* aTriggeringPrincipal) {
12875 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12876 return NS_OK;
12879 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12880 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12881 // but |HTMLFormElement::SubmitSubmission(...)|.
12882 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
12883 ShouldBlockLoadingForBackButton()) {
12884 return NS_OK;
12887 if (aContent->IsEditable()) {
12888 return NS_OK;
12891 // if the triggeringPrincipal is not passed explicitly, then we
12892 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12893 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
12894 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
12897 // defer to an external protocol handler if necessary...
12898 nsCOMPtr<nsIExternalProtocolService> extProtService =
12899 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12900 if (extProtService) {
12901 nsAutoCString scheme;
12902 aLoadState->URI()->GetScheme(scheme);
12903 if (!scheme.IsEmpty()) {
12904 // if the URL scheme does not correspond to an exposed protocol, then
12905 // we need to hand this link click over to the external protocol
12906 // handler.
12907 bool isExposed;
12908 nsresult rv =
12909 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12910 if (NS_SUCCEEDED(rv) && !isExposed) {
12911 return extProtService->LoadURI(
12912 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
12913 /* aTriggeredExternally */
12914 false,
12915 /* aHasValidUserGestureActivation */
12916 aContent->OwnerDoc()->HasValidTransientUserGestureActivation());
12921 uint32_t triggeringSandboxFlags = 0;
12922 uint64_t triggeringWindowId = 0;
12923 bool triggeringStorageAccess = false;
12924 if (mBrowsingContext) {
12925 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
12926 triggeringWindowId = aContent->OwnerDoc()->InnerWindowID();
12927 triggeringStorageAccess = aContent->OwnerDoc()->UsingStorageAccess();
12930 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12931 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
12932 bool triggeringPrincipalIsSystemPrincipal =
12933 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
12934 if (elementCanHaveNoopener) {
12935 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
12936 nsAutoString relString;
12937 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
12938 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
12939 relString);
12941 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
12942 bool explicitOpenerSet = false;
12944 // The opener behaviour follows a hierarchy, such that if a higher
12945 // priority behaviour is specified, it always takes priority. That
12946 // priority is currently: norefrerer > noopener > opener > default
12948 while (tok.hasMoreTokens()) {
12949 const nsAString& token = tok.nextToken();
12950 if (token.LowerCaseEqualsLiteral("noreferrer")) {
12951 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
12952 INTERNAL_LOAD_FLAGS_NO_OPENER;
12953 // noreferrer cannot be overwritten by a 'rel=opener'.
12954 explicitOpenerSet = true;
12955 break;
12958 if (token.LowerCaseEqualsLiteral("noopener")) {
12959 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12960 explicitOpenerSet = true;
12963 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12964 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
12965 explicitOpenerSet = true;
12969 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12970 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
12971 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12974 if (aNoOpenerImplied) {
12975 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12979 // Get the owner document of the link that was clicked, this will be
12980 // the document that the link is in, or the last document that the
12981 // link was in. From that document, we'll get the URI to use as the
12982 // referrer, since the current URI in this docshell may be a
12983 // new document that we're in the process of loading.
12984 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
12986 // Now check that the referrerDoc's inner window is the current inner
12987 // window for mScriptGlobal. If it's not, then we don't want to
12988 // follow this link.
12989 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
12990 if (!mScriptGlobal || !referrerInner ||
12991 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
12992 // We're no longer the current inner window
12993 return NS_OK;
12996 // referrer could be null here in some odd cases, but that's ok,
12997 // we'll just load the link w/o sending a referrer in those cases.
12999 // If this is an anchor element, grab its type property to use as a hint
13000 nsAutoString typeHint;
13001 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13002 if (anchor) {
13003 anchor->GetType(typeHint);
13004 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13005 nsAutoCString type, dummy;
13006 NS_ParseRequestContentType(utf8Hint, type, dummy);
13007 CopyUTF8toUTF16(type, typeHint);
13010 uint32_t loadType = LOAD_LINK;
13011 if (aLoadState->IsFormSubmission()) {
13012 if (aLoadState->Target().IsEmpty()) {
13013 // We set the right load type here for form submissions with an empty
13014 // target. Form submission with a non-empty target are handled in
13015 // nsDocShell::PerformRetargeting after we've selected the correct target
13016 // BC.
13017 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
13019 } else {
13020 // Link click can be triggered inside an onload handler, and we don't want
13021 // to add history entry in this case.
13022 bool inOnLoadHandler = false;
13023 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13024 if (inOnLoadHandler) {
13025 loadType = LOAD_NORMAL_REPLACE;
13029 nsCOMPtr<nsIReferrerInfo> referrerInfo =
13030 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
13031 : new ReferrerInfo(*referrerDoc);
13032 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
13034 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
13035 aLoadState->SetTriggeringWindowId(triggeringWindowId);
13036 aLoadState->SetTriggeringStorageAccess(triggeringStorageAccess);
13037 aLoadState->SetReferrerInfo(referrerInfo);
13038 aLoadState->SetInternalLoadFlags(flags);
13039 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
13040 aLoadState->SetLoadType(loadType);
13041 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
13042 aLoadState->SetHasValidUserGestureActivation(
13043 context && context->HasValidTransientUserGestureActivation());
13045 nsresult rv = InternalLoad(aLoadState);
13047 if (NS_SUCCEEDED(rv)) {
13048 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
13049 referrerInfo);
13052 return rv;
13055 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13056 const nsAString& aTargetSpec) {
13057 if (aContent->IsEditable()) {
13058 return NS_OK;
13061 nsresult rv = NS_ERROR_FAILURE;
13063 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13064 if (!browserChrome) {
13065 return rv;
13068 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13069 nsAutoCString spec;
13070 rv = exposableURI->GetDisplaySpec(spec);
13071 NS_ENSURE_SUCCESS(rv, rv);
13073 NS_ConvertUTF8toUTF16 uStr(spec);
13075 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13076 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13078 rv = browserChrome->SetLinkStatus(uStr);
13079 return rv;
13082 nsresult nsDocShell::OnLeaveLink() {
13083 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13084 nsresult rv = NS_ERROR_FAILURE;
13086 if (browserChrome) {
13087 rv = browserChrome->SetLinkStatus(u""_ns);
13089 return rv;
13092 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13093 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13094 UserActivation::IsHandlingUserInput() ||
13095 !Preferences::GetBool("accessibility.blockjsredirection")) {
13096 return false;
13099 bool canGoForward = false;
13100 GetCanGoForward(&canGoForward);
13101 return canGoForward;
13104 //----------------------------------------------------------------------
13105 // Web Shell Services API
13107 // This functions is only called when a new charset is detected in loading a
13108 // document.
13109 nsresult nsDocShell::CharsetChangeReloadDocument(
13110 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13111 // XXX hack. keep the aCharset and aSource wait to pick it up
13112 nsCOMPtr<nsIDocumentViewer> viewer;
13113 NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer)), NS_ERROR_FAILURE);
13114 if (viewer) {
13115 int32_t source;
13116 Unused << viewer->GetReloadEncodingAndSource(&source);
13117 if (aSource > source) {
13118 viewer->SetReloadEncodingAndSource(aEncoding, aSource);
13119 if (eCharsetReloadRequested != mCharsetReloadState) {
13120 mCharsetReloadState = eCharsetReloadRequested;
13121 switch (mLoadType) {
13122 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13123 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13124 LOAD_FLAGS_BYPASS_PROXY);
13125 case LOAD_RELOAD_BYPASS_CACHE:
13126 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13127 default:
13128 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13133 // return failure if this request is not accepted due to mCharsetReloadState
13134 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13137 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13138 if (eCharsetReloadRequested != mCharsetReloadState) {
13139 Stop(nsIWebNavigation::STOP_ALL);
13140 return NS_OK;
13142 // return failer if this request is not accepted due to mCharsetReloadState
13143 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13146 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13147 #if NS_PRINT_PREVIEW
13148 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mDocumentViewer);
13149 return viewer->ExitPrintPreview();
13150 #else
13151 return NS_OK;
13152 #endif
13155 /* [infallible] */
13156 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13157 bool* aIsTopLevelContentDocShell) {
13158 *aIsTopLevelContentDocShell = false;
13160 if (mItemType == typeContent) {
13161 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13164 return NS_OK;
13167 // Implements nsILoadContext.originAttributes
13168 NS_IMETHODIMP
13169 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13170 JS::MutableHandle<JS::Value> aVal) {
13171 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13174 // Implements nsIDocShell.GetOriginAttributes()
13175 NS_IMETHODIMP
13176 nsDocShell::GetOriginAttributes(JSContext* aCx,
13177 JS::MutableHandle<JS::Value> aVal) {
13178 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13181 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13182 nsIURI* aURI) {
13183 MOZ_ASSERT(aPrincipal);
13184 MOZ_ASSERT(aURI);
13186 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13187 return false;
13190 nsCOMPtr<nsIDocShellTreeItem> parent;
13191 GetInProcessSameTypeParent(getter_AddRefs(parent));
13192 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13193 nsPIDOMWindowInner* parentInner =
13194 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13196 StorageAccess storage =
13197 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13199 // If the partitioned service worker is enabled, service worker is allowed to
13200 // control the window if partition is enabled.
13201 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13202 RefPtr<Document> doc = parentInner->GetExtantDoc();
13204 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13205 return true;
13209 return storage == StorageAccess::eAllow;
13212 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13213 MOZ_ASSERT(!mIsBeingDestroyed);
13214 return mBrowsingContext->SetOriginAttributes(aAttrs);
13217 NS_IMETHODIMP
13218 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13219 RefPtr<nsDocShell> self = this;
13220 RefPtr<ChildProcessChannelListener> cpcl =
13221 ChildProcessChannelListener::GetSingleton();
13223 // Call into InternalLoad with the pending channel when it is received.
13224 cpcl->RegisterCallback(
13225 aIdentifier, [self, aHistoryIndex](
13226 nsDocShellLoadState* aLoadState,
13227 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13228 aStreamFilterEndpoints,
13229 nsDOMNavigationTiming* aTiming) {
13230 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13231 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13232 aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
13233 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
13234 return NS_BINDING_ABORTED;
13237 self->mLoadType = aLoadState->LoadType();
13238 nsCOMPtr<nsIURI> previousURI;
13239 uint32_t previousFlags = 0;
13240 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13241 getter_AddRefs(previousURI), &previousFlags);
13242 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13243 previousURI, previousFlags);
13245 if (aTiming) {
13246 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13247 self->mBlankTiming = false;
13250 // If we're performing a history load, locate the correct history entry,
13251 // and set the relevant bits on our loadState.
13252 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13253 !mozilla::SessionHistoryInParent()) {
13254 nsCOMPtr<nsISHistory> legacySHistory =
13255 self->GetSessionHistory()->LegacySHistory();
13257 nsCOMPtr<nsISHEntry> entry;
13258 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13259 getter_AddRefs(entry));
13260 if (NS_SUCCEEDED(rv)) {
13261 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13262 aLoadState->SetLoadType(LOAD_HISTORY);
13263 aLoadState->SetSHEntry(entry);
13267 self->InternalLoad(aLoadState);
13269 if (aLoadState->GetOriginalURIString().isSome()) {
13270 // Save URI string in case it's needed later when
13271 // sending to search engine service in EndPageLoad()
13272 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13275 for (auto& endpoint : aStreamFilterEndpoints) {
13276 extensions::StreamFilterParent::Attach(
13277 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13280 // If the channel isn't pending, then it means that InternalLoad
13281 // never connected it, and we shouldn't try to continue. This
13282 // can happen even if InternalLoad returned NS_OK.
13283 bool pending = false;
13284 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13285 NS_ASSERTION(pending, "We should have connected the pending channel!");
13286 if (!pending) {
13287 return NS_BINDING_ABORTED;
13289 return NS_OK;
13291 return NS_OK;
13294 NS_IMETHODIMP
13295 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13296 JSContext* aCx) {
13297 OriginAttributes attrs;
13298 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13299 return NS_ERROR_INVALID_ARG;
13302 return SetOriginAttributes(attrs);
13305 NS_IMETHODIMP
13306 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13307 if (PresShell* presShell = GetPresShell()) {
13308 *aOut = presShell->AsyncPanZoomEnabled();
13309 return NS_OK;
13312 // If we don't have a presShell, fall back to the default platform value of
13313 // whether or not APZ is enabled.
13314 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13315 return NS_OK;
13318 bool nsDocShell::HasUnloadedParent() {
13319 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13320 wc = wc->GetParentWindowContext()) {
13321 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13322 wc->GetBrowsingContext()->IsDiscarded()) {
13323 // If a parent is OOP and the parent WindowContext is no
13324 // longer current, we can assume the parent was unloaded.
13325 return true;
13328 if (wc->GetBrowsingContext()->IsInProcess() &&
13329 (!wc->GetBrowsingContext()->GetDocShell() ||
13330 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13331 return true;
13334 return false;
13337 /* static */
13338 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13339 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13340 aLoadType & LOAD_CMD_HISTORY);
13343 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13344 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13345 return;
13348 // Global history is interested into sub-frame visits only for link-coloring
13349 // purposes, thus title updates are skipped for those.
13351 // Moreover, some iframe documents (such as the ones created via
13352 // document.open()) inherit the document uri of the caller, which would cause
13353 // us to override a previously set page title with one from the subframe.
13354 if (IsSubframe()) {
13355 return;
13358 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13359 history->SetURITitle(aURI, mTitle);
13363 bool nsDocShell::IsInvisible() { return mInvisible; }
13365 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13367 /* static */
13368 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13369 const nsString& aKeyword) {
13370 if (aProvider.IsEmpty()) {
13371 return;
13373 nsresult rv;
13374 nsCOMPtr<nsISupportsString> isupportsString =
13375 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13376 NS_ENSURE_SUCCESS_VOID(rv);
13378 rv = isupportsString->SetData(aProvider);
13379 NS_ENSURE_SUCCESS_VOID(rv);
13381 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13382 if (obsSvc) {
13383 // Note that "keyword-search" refers to a search via the url
13384 // bar, not a bookmarks keyword search.
13385 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13389 NS_IMETHODIMP
13390 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13391 bool* aShouldIntercept) {
13392 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13393 aShouldIntercept);
13396 NS_IMETHODIMP
13397 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13398 return mInterceptController->ChannelIntercepted(aChannel);
13401 bool nsDocShell::InFrameSwap() {
13402 RefPtr<nsDocShell> shell = this;
13403 do {
13404 if (shell->mInFrameSwap) {
13405 return true;
13407 shell = shell->GetInProcessParentDocshell();
13408 } while (shell);
13409 return false;
13412 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13413 return std::move(mInitialClientSource);
13416 NS_IMETHODIMP
13417 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13418 if (!NS_SUCCEEDED(EnsureEditorData())) {
13419 return NS_ERROR_FAILURE;
13422 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13423 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13426 NS_IMETHODIMP
13427 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13428 *aBrowserChild = GetBrowserChild().take();
13429 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13432 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13433 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13434 return tc.forget();
13437 nsCommandManager* nsDocShell::GetCommandManager() {
13438 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13439 return mCommandManager;
13442 NS_IMETHODIMP_(void)
13443 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13444 mBrowsingContext->GetOriginAttributes(aAttrs);
13447 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13448 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13449 return docShell->GetHTMLEditorInternal();
13452 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13453 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13454 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13457 #define MATRIX_LENGTH 20
13459 NS_IMETHODIMP
13460 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13461 if (aMatrix.Length() == MATRIX_LENGTH) {
13462 mColorMatrix.reset(new gfx::Matrix5x4());
13463 static_assert(
13464 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13465 "Size mismatch for our memcpy");
13466 memcpy(mColorMatrix->components, aMatrix.Elements(),
13467 sizeof(mColorMatrix->components));
13468 } else if (aMatrix.Length() == 0) {
13469 mColorMatrix.reset();
13470 } else {
13471 return NS_ERROR_INVALID_ARG;
13474 PresShell* presShell = GetPresShell();
13475 if (!presShell) {
13476 return NS_ERROR_FAILURE;
13479 nsIFrame* frame = presShell->GetRootFrame();
13480 if (!frame) {
13481 return NS_ERROR_FAILURE;
13484 frame->SchedulePaint();
13486 return NS_OK;
13489 NS_IMETHODIMP
13490 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13491 if (mColorMatrix) {
13492 aMatrix.SetLength(MATRIX_LENGTH);
13493 static_assert(
13494 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13495 "Size mismatch for our memcpy");
13496 memcpy(aMatrix.Elements(), mColorMatrix->components,
13497 MATRIX_LENGTH * sizeof(float));
13500 return NS_OK;
13503 #undef MATRIX_LENGTH
13505 NS_IMETHODIMP
13506 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13507 *aForceReload = IsForceReloading();
13508 return NS_OK;
13511 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13513 NS_IMETHODIMP
13514 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13515 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13516 return NS_OK;
13519 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13521 bool nsDocShell::GetIsAttemptingToNavigate() {
13522 // XXXbz the document.open spec says to abort even if there's just a
13523 // queued navigation task, sort of. It's not clear whether browsers
13524 // actually do that, and we didn't use to do it, so for now let's
13525 // not do that.
13526 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13527 if (mDocumentRequest) {
13528 // There's definitely a navigation in progress.
13529 return true;
13532 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13533 // until the script runs, which means they're not sending loadgroup
13534 // notifications and hence not getting set as mDocumentRequest. Look through
13535 // our loadgroup for document-level javascript: loads.
13536 if (!mLoadGroup) {
13537 return false;
13540 nsCOMPtr<nsISimpleEnumerator> requests;
13541 mLoadGroup->GetRequests(getter_AddRefs(requests));
13542 bool hasMore = false;
13543 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13544 nsCOMPtr<nsISupports> elem;
13545 requests->GetNext(getter_AddRefs(elem));
13546 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13547 if (!scriptChannel) {
13548 continue;
13551 if (scriptChannel->GetIsDocumentLoad()) {
13552 // This is a javascript: load that might lead to a new document,
13553 // hence a navigation.
13554 return true;
13558 return mCheckingSessionHistory;
13561 void nsDocShell::SetLoadingSessionHistoryInfo(
13562 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
13563 bool aNeedToReportActiveAfterLoadingBecomesActive) {
13564 // FIXME Would like to assert this, but can't yet.
13565 // MOZ_ASSERT(!mLoadingEntry);
13566 MOZ_LOG(gSHLog, LogLevel::Debug,
13567 ("Setting the loading entry on nsDocShell %p to %s", this,
13568 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13569 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13570 mNeedToReportActiveAfterLoadingBecomesActive =
13571 aNeedToReportActiveAfterLoadingBecomesActive;
13574 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13575 uint32_t aCacheKey,
13576 nsIURI* aPreviousURI) {
13577 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13579 MOZ_LOG(gSHLog, LogLevel::Debug,
13580 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13582 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
13583 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13584 mActiveEntryIsLoadingFromSessionHistory =
13585 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13586 if (mLoadingEntry) {
13587 MOZ_LOG(gSHLog, LogLevel::Debug,
13588 ("Moving the loading entry to the active entry on nsDocShell %p "
13589 "to %s",
13590 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13591 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13592 mLoadingEntry.swap(loadingEntry);
13593 if (!mActiveEntryIsLoadingFromSessionHistory) {
13594 if (mNeedToReportActiveAfterLoadingBecomesActive) {
13595 // Needed to pass various history length WPTs.
13596 mBrowsingContext->SetActiveSessionHistoryEntry(
13597 mozilla::Nothing(), mActiveEntry.get(), mLoadType,
13598 /* aUpdatedCacheKey = */ 0, false);
13600 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13603 mNeedToReportActiveAfterLoadingBecomesActive = false;
13605 if (mActiveEntry) {
13606 if (aCacheKey != 0) {
13607 mActiveEntry->SetCacheKey(aCacheKey);
13609 MOZ_ASSERT(loadingEntry);
13610 uint32_t loadType =
13611 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13613 if (loadingEntry->mLoadId != UINT64_MAX) {
13614 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13615 // does require a non-null uri if this is for a refresh load of the same
13616 // URI, but in that case mCurrentURI won't be null here.
13617 mBrowsingContext->SessionHistoryCommit(
13618 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
13619 aPersist, false, aExpired, aCacheKey);
13624 static bool IsFaviconLoad(nsIRequest* aRequest) {
13625 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13626 if (!channel) {
13627 return false;
13630 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13631 return li && li->InternalContentPolicyType() ==
13632 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13635 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13636 nsIRequest* aRequest) {
13637 // Ignore favicon loads, they don't need to block caching.
13638 if (IsFaviconLoad(aRequest)) {
13639 return;
13642 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13644 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13646 if (mBrowsingContext->GetCurrentWindowContext()) {
13647 // We have three states: no request, one request with an id and
13648 // eiher one request without an id or multiple requests. Nothing() is no
13649 // request, Some(non-zero) is one request with an id and Some(0) is one
13650 // request without an id or multiple requests.
13651 Maybe<uint64_t> singleChannelId;
13652 if (mRequestForBlockingFromBFCacheCount > 1) {
13653 singleChannelId = Some(0);
13654 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13655 nsCOMPtr<nsIIdentChannel> identChannel;
13656 if (aStartRequest) {
13657 identChannel = do_QueryInterface(aRequest);
13658 } else {
13659 // aChannel is the channel that's being removed, but we need to check if
13660 // the remaining channel in the loadgroup has an id.
13661 nsCOMPtr<nsISimpleEnumerator> requests;
13662 mLoadGroup->GetRequests(getter_AddRefs(requests));
13663 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13664 if (!IsFaviconLoad(request) &&
13665 !!(identChannel = do_QueryInterface(request))) {
13666 break;
13671 if (identChannel) {
13672 singleChannelId = Some(identChannel->ChannelId());
13673 } else {
13674 singleChannelId = Some(0);
13676 } else {
13677 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13678 singleChannelId = Nothing();
13681 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13682 nsAutoCString uri("[no uri]");
13683 if (mCurrentURI) {
13684 uri = mCurrentURI->GetSpecOrDefault();
13686 if (singleChannelId.isNothing()) {
13687 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13688 ("Loadgroup for %s doesn't have any requests relevant for "
13689 "blocking BFCache",
13690 uri.get()));
13691 } else if (singleChannelId.value() == 0) {
13692 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13693 ("Loadgroup for %s has multiple requests relevant for blocking "
13694 "BFCache",
13695 uri.get()));
13696 } else {
13697 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13698 ("Loadgroup for %s has one request with id %" PRIu64
13699 " relevant for blocking BFCache",
13700 uri.get(), singleChannelId.value()));
13704 if (mSingleChannelId != singleChannelId) {
13705 mSingleChannelId = singleChannelId;
13706 WindowGlobalChild* wgc =
13707 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13708 if (wgc) {
13709 wgc->SendSetSingleChannelId(singleChannelId);
13715 NS_IMETHODIMP
13716 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13717 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13718 nsAutoCString uri("[no uri]");
13719 if (mCurrentURI) {
13720 uri = mCurrentURI->GetSpecOrDefault();
13722 nsAutoCString name;
13723 aRequest->GetName(name);
13724 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13725 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13727 RecordSingleChannelId(true, aRequest);
13728 return nsDocLoader::OnStartRequest(aRequest);
13731 NS_IMETHODIMP
13732 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13733 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13734 nsAutoCString uri("[no uri]");
13735 if (mCurrentURI) {
13736 uri = mCurrentURI->GetSpecOrDefault();
13738 nsAutoCString name;
13739 aRequest->GetName(name);
13740 MOZ_LOG(
13741 gSHIPBFCacheLog, LogLevel::Verbose,
13742 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13744 RecordSingleChannelId(false, aRequest);
13745 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13748 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13749 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13751 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13752 nsCOMPtr<nsISimpleEnumerator> requests;
13753 mLoadGroup->GetRequests(getter_AddRefs(requests));
13754 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13755 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13756 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13757 static_cast<DocumentChannelChild*>(channel.get())
13758 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13761 mChannelToDisconnectOnPageHide = 0;