Backed out changeset 7b83373f7a9e (bug 1883860) for causing build bustages @ caps...
[gecko.git] / docshell / base / nsDocShell.cpp
blob3404597343e0d21c42c5adc2f2849888869cf75a
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 (SessionStorePlatformCollection()) {
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 (SessionStorePlatformCollection()) {
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 PROFILER_MARKER_UNTYPED("CreateAboutBlankDocumentViewer", DOM,
6479 MarkerStack::Capture());
6481 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6483 /* mCreatingDocument should never be true at this point. However, it's
6484 a theoretical possibility. We want to know about it and make it stop,
6485 and this sounds like a job for an assertion. */
6486 NS_ASSERTION(!mCreatingDocument,
6487 "infinite(?) loop creating document averted");
6488 if (mCreatingDocument) {
6489 return NS_ERROR_FAILURE;
6492 if (!mBrowsingContext->AncestorsAreCurrent() ||
6493 mBrowsingContext->IsInBFCache()) {
6494 mBrowsingContext->RemoveRootFromBFCacheSync();
6495 return NS_ERROR_NOT_AVAILABLE;
6498 // mDocumentViewer->PermitUnload may release |this| docshell.
6499 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6501 AutoRestore<bool> creatingDocument(mCreatingDocument);
6502 mCreatingDocument = true;
6504 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6505 mItemType != typeChrome) {
6506 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6507 mBrowsingContext->OriginAttributesRef());
6510 // Make sure timing is created. But first record whether we had it
6511 // already, so we don't clobber the timing for an in-progress load.
6512 bool hadTiming = mTiming;
6513 bool toBeReset = MaybeInitTiming();
6514 if (mDocumentViewer) {
6515 if (aCheckPermitUnload) {
6516 // We've got a content viewer already. Make sure the user
6517 // permits us to discard the current document and replace it
6518 // with about:blank. And also ensure we fire the unload events
6519 // in the current document.
6521 // Unload gets fired first for
6522 // document loaded from the session history.
6523 mTiming->NotifyBeforeUnload();
6525 bool okToUnload;
6526 rv = mDocumentViewer->PermitUnload(&okToUnload);
6528 if (NS_SUCCEEDED(rv) && !okToUnload) {
6529 // The user chose not to unload the page, interrupt the load.
6530 MaybeResetInitTiming(toBeReset);
6531 return NS_ERROR_FAILURE;
6533 if (mTiming) {
6534 mTiming->NotifyUnloadAccepted(mCurrentURI);
6538 mSavingOldViewer =
6539 aTryToSaveOldPresentation &&
6540 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
6541 /* aReportBFCacheComboTelemetry */ true);
6543 // Make sure to blow away our mLoadingURI just in case. No loads
6544 // from inside this pagehide.
6545 mLoadingURI = nullptr;
6547 // Stop any in-progress loading, so that we don't accidentally trigger any
6548 // PageShow notifications from Embed() interrupting our loading below.
6549 Stop();
6551 // Notify the current document that it is about to be unloaded!!
6553 // It is important to fire the unload() notification *before* any state
6554 // is changed within the DocShell - otherwise, javascript will get the
6555 // wrong information :-(
6557 (void)FirePageHideNotification(!mSavingOldViewer);
6558 // pagehide notification might destroy this docshell.
6559 if (mIsBeingDestroyed) {
6560 return NS_ERROR_DOCSHELL_DYING;
6564 // Now make sure we don't think we're in the middle of firing unload after
6565 // this point. This will make us fire unload when the about:blank document
6566 // unloads... but that's ok, more or less. Would be nice if it fired load
6567 // too, of course.
6568 mFiredUnloadEvent = false;
6570 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6571 nsContentUtils::FindInternalDocumentViewer("text/html"_ns);
6573 if (docFactory) {
6574 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6575 const uint32_t sandboxFlags =
6576 mBrowsingContext->GetHasLoadedNonInitialDocument()
6577 ? mBrowsingContext->GetSandboxFlags()
6578 : mBrowsingContext->GetInitialSandboxFlags();
6579 // If we're sandboxed, then create a new null principal. We skip
6580 // this if we're being created from WindowGlobalChild, since in
6581 // that case we already have a null principal if required.
6582 // We can't compare againt the BrowsingContext sandbox flag, since
6583 // the value was taken when the load initiated and may have since
6584 // changed.
6585 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6586 if (aPrincipal) {
6587 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6588 } else {
6589 principal = NullPrincipal::Create(GetOriginAttributes());
6591 partitionedPrincipal = principal;
6592 } else {
6593 principal = aPrincipal;
6594 partitionedPrincipal = aPartitionedPrincipal;
6597 // We cannot get the foreign partitioned prinicpal for the initial
6598 // about:blank page. So, we change to check if we need to use the
6599 // partitioned principal for the service worker here.
6600 MaybeCreateInitialClientSource(
6601 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6602 this)
6603 ? partitionedPrincipal
6604 : principal);
6606 // generate (about:blank) document to load
6607 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6608 partitionedPrincipal, this);
6609 if (blankDoc) {
6610 // Hack: manually set the CSP for the new document
6611 // Please create an actual copy of the CSP (do not share the same
6612 // reference) otherwise appending a new policy within the new
6613 // document will be incorrectly propagated to the opening doc.
6614 if (aCSP) {
6615 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6616 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6617 blankDoc->SetCsp(cspToInherit);
6620 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6622 blankDoc->SetEmbedderPolicy(aCOEP);
6624 // Hack: set the base URI manually, since this document never
6625 // got Reset() with a channel.
6626 blankDoc->SetBaseURI(aBaseURI);
6628 // Copy our sandbox flags to the document. These are immutable
6629 // after being set here.
6630 blankDoc->SetSandboxFlags(sandboxFlags);
6632 blankDoc->InitFeaturePolicy();
6634 // create a content viewer for us and the new document
6635 docFactory->CreateInstanceForDocument(
6636 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6637 getter_AddRefs(viewer));
6639 // hook 'em up
6640 if (viewer) {
6641 viewer->SetContainer(this);
6642 rv = Embed(viewer, aActor, true, false, nullptr, mCurrentURI);
6643 NS_ENSURE_SUCCESS(rv, rv);
6645 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6646 /* aFireLocationChange */ true,
6647 /* aIsInitialAboutBlank */ true,
6648 /* aLocationFlags */ 0);
6649 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6654 // The transient about:blank viewer doesn't have a session history entry.
6655 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6657 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6658 // have one before entering this function.
6659 if (!hadTiming) {
6660 mTiming = nullptr;
6661 mBlankTiming = true;
6664 return rv;
6667 NS_IMETHODIMP
6668 nsDocShell::CreateAboutBlankDocumentViewer(nsIPrincipal* aPrincipal,
6669 nsIPrincipal* aPartitionedPrincipal,
6670 nsIContentSecurityPolicy* aCSP) {
6671 return CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6672 nullptr,
6673 /* aIsInitialDocument */ false);
6676 nsresult nsDocShell::CreateDocumentViewerForActor(
6677 WindowGlobalChild* aWindowActor) {
6678 MOZ_ASSERT(aWindowActor);
6680 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6681 // FIXME: We may want to support non-initial documents here.
6682 nsresult rv = CreateAboutBlankDocumentViewer(
6683 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6684 /* aCsp */ nullptr,
6685 /* aBaseURI */ nullptr,
6686 /* aIsInitialDocument */ true,
6687 /* aCOEP */ Nothing(),
6688 /* aTryToSaveOldPresentation */ true,
6689 /* aCheckPermitUnload */ true, aWindowActor);
6690 #ifdef DEBUG
6691 if (NS_SUCCEEDED(rv)) {
6692 RefPtr<Document> doc(GetDocument());
6693 MOZ_ASSERT(
6694 doc,
6695 "Should have a document if CreateAboutBlankDocumentViewer succeeded");
6696 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6697 "New document should be in the same global as our actor");
6698 MOZ_ASSERT(doc->IsInitialDocument(),
6699 "New document should be an initial document");
6701 #endif
6703 return rv;
6706 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6707 nsIRequest* aNewRequest,
6708 Document* aNewDocument,
6709 bool aReportBFCacheComboTelemetry) {
6710 if (!mOSHE) {
6711 return false; // no entry to save into
6714 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6715 "mOSHE cannot be non-null with SHIP");
6716 nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
6717 if (viewer) {
6718 NS_WARNING("mOSHE already has a content viewer!");
6719 return false;
6722 // Only save presentation for "normal" loads and link loads. Anything else
6723 // probably wants to refetch the page, so caching the old presentation
6724 // would be incorrect.
6725 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6726 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6727 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6728 aLoadType != LOAD_ERROR_PAGE) {
6729 return false;
6732 // If the session history entry has the saveLayoutState flag set to false,
6733 // then we should not cache the presentation.
6734 if (!mOSHE->GetSaveLayoutStateFlag()) {
6735 return false;
6738 // If the document is not done loading, don't cache it.
6739 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6740 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6741 ("Blocked due to document still loading"));
6742 return false;
6745 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6746 return false;
6749 // Avoid doing the work of saving the presentation state in the case where
6750 // the content viewer cache is disabled.
6751 if (nsSHistory::GetMaxTotalViewers() == 0) {
6752 return false;
6755 // Don't cache the content viewer if we're in a subframe.
6756 if (mBrowsingContext->GetParent()) {
6757 return false; // this is a subframe load
6760 // If the document does not want its presentation cached, then don't.
6761 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6763 uint32_t bfCacheCombo = 0;
6764 bool canSavePresentation =
6765 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6766 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6767 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6768 auto* browsingContextGroup = mBrowsingContext->Group();
6769 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6770 browsingContextGroup->Toplevels();
6772 for (const auto& browsingContext : topLevelContext) {
6773 if (browsingContext != mBrowsingContext) {
6774 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6775 canSavePresentation = false;
6777 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6778 break;
6783 if (aReportBFCacheComboTelemetry) {
6784 ReportBFCacheComboTelemetry(bfCacheCombo);
6786 return doc && canSavePresentation;
6789 /* static */
6790 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6791 // There are 11 possible reasons to make a request fails to use BFCache
6792 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6793 // the common combinations for reasons which make requests fail to use
6794 // BFCache. These combinations are generated based on some local browsings,
6795 // we need to adjust them when necessary.
6796 enum BFCacheStatusCombo : uint32_t {
6797 BFCACHE_SUCCESS,
6798 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6799 // If both unload and beforeunload listeners are presented, it'll be
6800 // recorded as unload
6801 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6802 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6803 mozilla::dom::BFCacheStatus::REQUEST,
6804 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6805 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6806 mozilla::dom::BFCacheStatus::REQUEST |
6807 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6808 UNLOAD_REQUEST_PEER_MSE =
6809 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6810 mozilla::dom::BFCacheStatus::REQUEST |
6811 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6812 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6813 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6814 mozilla::dom::BFCacheStatus::REQUEST |
6815 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6816 SUSPENDED_UNLOAD_REQUEST_PEER =
6817 mozilla::dom::BFCacheStatus::SUSPENDED |
6818 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6819 mozilla::dom::BFCacheStatus::REQUEST |
6820 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6821 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6822 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6825 // Beforeunload is recorded as a blocker only if it is the only one to block
6826 // bfcache.
6827 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6828 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6830 switch (aCombo) {
6831 case BFCACHE_SUCCESS:
6832 Telemetry::AccumulateCategorical(
6833 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6834 break;
6835 case NOT_ONLY_TOPLEVEL:
6836 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6837 Telemetry::AccumulateCategorical(
6838 Telemetry::LABELS_BFCACHE_COMBO::Other);
6839 break;
6841 Telemetry::AccumulateCategorical(
6842 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6843 Telemetry::AccumulateCategorical(
6844 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6845 break;
6846 case UNLOAD:
6847 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6848 break;
6849 case BEFOREUNLOAD:
6850 Telemetry::AccumulateCategorical(
6851 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6852 break;
6853 case UNLOAD_REQUEST:
6854 Telemetry::AccumulateCategorical(
6855 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6856 break;
6857 case REQUEST:
6858 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6859 break;
6860 case UNLOAD_REQUEST_PEER:
6861 Telemetry::AccumulateCategorical(
6862 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6863 break;
6864 case UNLOAD_REQUEST_PEER_MSE:
6865 Telemetry::AccumulateCategorical(
6866 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6867 break;
6868 case UNLOAD_REQUEST_MSE:
6869 Telemetry::AccumulateCategorical(
6870 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6871 break;
6872 case SUSPENDED_UNLOAD_REQUEST_PEER:
6873 Telemetry::AccumulateCategorical(
6874 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6875 break;
6876 case REMOTE_SUBFRAMES:
6877 Telemetry::AccumulateCategorical(
6878 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6879 break;
6880 default:
6881 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6882 break;
6886 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6887 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6888 MOZ_ASSERT(!mIsBeingDestroyed);
6890 NS_ASSERTION(!mEditorData,
6891 "Why reattach an editor when we already have one?");
6892 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6893 "Reattaching when there's not a detached editor.");
6895 if (mEditorData || !aSHEntry) {
6896 return;
6899 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6900 if (mEditorData) {
6901 #ifdef DEBUG
6902 nsresult rv =
6903 #endif
6904 mEditorData->ReattachToWindow(this);
6905 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
6909 void nsDocShell::DetachEditorFromWindow() {
6910 if (!mEditorData || mEditorData->WaitingForLoad()) {
6911 // If there's nothing to detach, or if the editor data is actually set
6912 // up for the _new_ page that's coming in, don't detach.
6913 return;
6916 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6917 "Detaching editor when it's already detached.");
6919 nsresult res = mEditorData->DetachFromWindow();
6920 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6922 if (NS_SUCCEEDED(res)) {
6923 // Make mOSHE hold the owning ref to the editor data.
6924 if (mOSHE) {
6925 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
6926 "We should not set the editor data again once after we "
6927 "detached the editor data during destroying this docshell");
6928 mOSHE->SetEditorData(mEditorData.release());
6929 } else {
6930 mEditorData = nullptr;
6934 #ifdef DEBUG
6936 bool isEditable;
6937 GetEditable(&isEditable);
6938 NS_ASSERTION(!isEditable,
6939 "Window is still editable after detaching editor.");
6941 #endif // DEBUG
6944 nsresult nsDocShell::CaptureState() {
6945 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6947 if (!mOSHE || mOSHE == mLSHE) {
6948 // No entry to save into, or we're replacing the existing entry.
6949 return NS_ERROR_FAILURE;
6952 if (!mScriptGlobal) {
6953 return NS_ERROR_FAILURE;
6956 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
6957 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
6959 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
6960 nsAutoCString spec;
6961 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
6962 if (uri) {
6963 uri->GetSpec(spec);
6965 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6966 ("Saving presentation into session history, URI: %s", spec.get()));
6969 mOSHE->SetWindowState(windowState);
6971 // Suspend refresh URIs and save off the timer queue
6972 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6974 // Capture the current content viewer bounds.
6975 if (mDocumentViewer) {
6976 nsIntRect bounds;
6977 mDocumentViewer->GetBounds(bounds);
6978 mOSHE->SetViewerBounds(bounds);
6981 // Capture the docshell hierarchy.
6982 mOSHE->ClearChildShells();
6984 uint32_t childCount = mChildList.Length();
6985 for (uint32_t i = 0; i < childCount; ++i) {
6986 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6987 NS_ASSERTION(childShell, "null child shell");
6989 mOSHE->AddChildShell(childShell);
6992 return NS_OK;
6995 NS_IMETHODIMP
6996 nsDocShell::RestorePresentationEvent::Run() {
6997 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6999 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7000 NS_WARNING("RestoreFromHistory failed");
7002 return NS_OK;
7005 NS_IMETHODIMP
7006 nsDocShell::BeginRestore(nsIDocumentViewer* aDocumentViewer, bool aTop) {
7007 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7009 nsresult rv;
7010 if (!aDocumentViewer) {
7011 rv = EnsureDocumentViewer();
7012 NS_ENSURE_SUCCESS(rv, rv);
7014 aDocumentViewer = mDocumentViewer;
7017 // Dispatch events for restoring the presentation. We try to simulate
7018 // the progress notifications loading the document would cause, so we add
7019 // the document's channel to the loadgroup to initiate stateChange
7020 // notifications.
7022 RefPtr<Document> doc = aDocumentViewer->GetDocument();
7023 if (doc) {
7024 nsIChannel* channel = doc->GetChannel();
7025 if (channel) {
7026 mEODForCurrentDocument = false;
7027 mIsRestoringDocument = true;
7028 mLoadGroup->AddRequest(channel, nullptr);
7029 mIsRestoringDocument = false;
7033 if (!aTop) {
7034 // This point corresponds to us having gotten OnStartRequest or
7035 // STATE_START, so do the same thing that CreateDocumentViewer does at
7036 // this point to ensure that unload/pagehide events for this document
7037 // will fire when it's unloaded again.
7038 mFiredUnloadEvent = false;
7040 // For non-top frames, there is no notion of making sure that the
7041 // previous document is in the domwindow when STATE_START notifications
7042 // happen. We can just call BeginRestore for all of the child shells
7043 // now.
7044 rv = BeginRestoreChildren();
7045 NS_ENSURE_SUCCESS(rv, rv);
7048 return NS_OK;
7051 nsresult nsDocShell::BeginRestoreChildren() {
7052 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7054 for (auto* childDocLoader : mChildList.ForwardRange()) {
7055 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7056 if (child) {
7057 nsresult rv = child->BeginRestore(nullptr, false);
7058 NS_ENSURE_SUCCESS(rv, rv);
7061 return NS_OK;
7064 NS_IMETHODIMP
7065 nsDocShell::FinishRestore() {
7066 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7068 // First we call finishRestore() on our children. In the simulated load,
7069 // all of the child frames finish loading before the main document.
7071 for (auto* childDocLoader : mChildList.ForwardRange()) {
7072 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7073 if (child) {
7074 child->FinishRestore();
7078 if (mOSHE && mOSHE->HasDetachedEditor()) {
7079 ReattachEditorToWindow(mOSHE);
7082 RefPtr<Document> doc = GetDocument();
7083 if (doc) {
7084 // Finally, we remove the request from the loadgroup. This will
7085 // cause onStateChange(STATE_STOP) to fire, which will fire the
7086 // pageshow event to the chrome.
7088 nsIChannel* channel = doc->GetChannel();
7089 if (channel) {
7090 mIsRestoringDocument = true;
7091 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7092 mIsRestoringDocument = false;
7096 return NS_OK;
7099 NS_IMETHODIMP
7100 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7101 *aRestoring = mIsRestoringDocument;
7102 return NS_OK;
7105 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7106 bool* aRestoring) {
7107 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7108 MOZ_ASSERT(!mIsBeingDestroyed);
7110 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7111 "RestorePresentation should only be called for history loads");
7113 nsCOMPtr<nsIDocumentViewer> viewer = aSHEntry->GetDocumentViewer();
7115 nsAutoCString spec;
7116 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7117 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7118 if (uri) {
7119 uri->GetSpec(spec);
7123 *aRestoring = false;
7125 if (!viewer) {
7126 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7127 ("no saved presentation for uri: %s", spec.get()));
7128 return NS_OK;
7131 // We need to make sure the content viewer's container is this docshell.
7132 // In subframe navigation, it's possible for the docshell that the
7133 // content viewer was originally loaded into to be replaced with a
7134 // different one. We don't currently support restoring the presentation
7135 // in that case.
7137 nsCOMPtr<nsIDocShell> container;
7138 viewer->GetContainer(getter_AddRefs(container));
7139 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7140 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7141 ("No valid container, clearing presentation"));
7142 aSHEntry->SetDocumentViewer(nullptr);
7143 return NS_ERROR_FAILURE;
7146 NS_ASSERTION(mDocumentViewer != viewer, "Restoring existing presentation");
7148 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7149 ("restoring presentation from session history: %s", spec.get()));
7151 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7153 // Post an event that will remove the request after we've returned
7154 // to the event loop. This mimics the way it is called by nsIChannel
7155 // implementations.
7157 // Revoke any pending restore (just in case).
7158 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7159 "should only have one RestorePresentationEvent");
7160 mRestorePresentationEvent.Revoke();
7162 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7163 nsresult rv = Dispatch(do_AddRef(evt));
7164 if (NS_SUCCEEDED(rv)) {
7165 mRestorePresentationEvent = evt.get();
7166 // The rest of the restore processing will happen on our event
7167 // callback.
7168 *aRestoring = true;
7171 return rv;
7174 namespace {
7175 class MOZ_STACK_CLASS PresentationEventForgetter {
7176 public:
7177 explicit PresentationEventForgetter(
7178 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7179 aRestorePresentationEvent)
7180 : mRestorePresentationEvent(aRestorePresentationEvent),
7181 mEvent(aRestorePresentationEvent.get()) {}
7183 ~PresentationEventForgetter() { Forget(); }
7185 void Forget() {
7186 if (mRestorePresentationEvent.get() == mEvent) {
7187 mRestorePresentationEvent.Forget();
7188 mEvent = nullptr;
7192 private:
7193 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7194 mRestorePresentationEvent;
7195 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7198 } // namespace
7200 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7201 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7204 nsresult nsDocShell::RestoreFromHistory() {
7205 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7206 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7207 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7209 // This section of code follows the same ordering as CreateDocumentViewer.
7210 if (!mLSHE) {
7211 return NS_ERROR_FAILURE;
7214 nsCOMPtr<nsIDocumentViewer> viewer = mLSHE->GetDocumentViewer();
7215 if (!viewer) {
7216 return NS_ERROR_FAILURE;
7219 if (mSavingOldViewer) {
7220 // We determined that it was safe to cache the document presentation
7221 // at the time we initiated the new load. We need to check whether
7222 // it's still safe to do so, since there may have been DOM mutations
7223 // or new requests initiated.
7224 RefPtr<Document> doc = viewer->GetDocument();
7225 nsIRequest* request = nullptr;
7226 if (doc) {
7227 request = doc->GetChannel();
7229 mSavingOldViewer = CanSavePresentation(
7230 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
7233 // Protect against mLSHE going away via a load triggered from
7234 // pagehide or unload.
7235 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7237 // Make sure to blow away our mLoadingURI just in case. No loads
7238 // from inside this pagehide.
7239 mLoadingURI = nullptr;
7241 // Notify the old content viewer that it's being hidden.
7242 FirePageHideNotification(!mSavingOldViewer);
7243 // pagehide notification might destroy this docshell.
7244 if (mIsBeingDestroyed) {
7245 return NS_ERROR_DOCSHELL_DYING;
7248 // If mLSHE was changed as a result of the pagehide event, then
7249 // something else was loaded. Don't finish restoring.
7250 if (mLSHE != origLSHE) {
7251 return NS_OK;
7254 // Add the request to our load group. We do this before swapping out
7255 // the content viewers so that consumers of STATE_START can access
7256 // the old document. We only deal with the toplevel load at this time --
7257 // to be consistent with normal document loading, subframes cannot start
7258 // loading until after data arrives, which is after STATE_START completes.
7260 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7261 mRestorePresentationEvent.get();
7262 Stop();
7263 // Make sure we're still restoring the same presentation.
7264 // If we aren't, docshell is in process doing another load already.
7265 NS_ENSURE_STATE(currentPresentationRestoration ==
7266 mRestorePresentationEvent.get());
7267 BeginRestore(viewer, true);
7268 NS_ENSURE_STATE(currentPresentationRestoration ==
7269 mRestorePresentationEvent.get());
7270 forgetter.Forget();
7272 // Set mFiredUnloadEvent = false so that the unload handler for the
7273 // *new* document will fire.
7274 mFiredUnloadEvent = false;
7276 mURIResultedInDocument = true;
7277 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7278 if (rootSH) {
7279 mPreviousEntryIndex = rootSH->Index();
7280 rootSH->LegacySHistory()->UpdateIndex();
7281 mLoadedEntryIndex = rootSH->Index();
7282 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7283 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7284 mLoadedEntryIndex));
7287 // Rather than call Embed(), we will retrieve the viewer from the session
7288 // history entry and swap it in.
7289 // XXX can we refactor this so that we can just call Embed()?
7290 PersistLayoutHistoryState();
7291 nsresult rv;
7292 if (mDocumentViewer) {
7293 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7294 if (mOSHE) {
7295 mOSHE->SyncPresentationState();
7297 mSavingOldViewer = false;
7301 mSavedRefreshURIList = nullptr;
7303 // In cases where we use a transient about:blank viewer between loads,
7304 // we never show the transient viewer, so _its_ previous viewer is never
7305 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7306 // before we grab the root view sibling, so that we don't grab a view
7307 // that's about to go away.
7309 if (mDocumentViewer) {
7310 // Make sure to hold a strong ref to previousViewer here while we
7311 // drop the reference to it from mDocumentViewer.
7312 nsCOMPtr<nsIDocumentViewer> previousViewer =
7313 mDocumentViewer->GetPreviousViewer();
7314 if (previousViewer) {
7315 mDocumentViewer->SetPreviousViewer(nullptr);
7316 previousViewer->Destroy();
7320 // Save off the root view's parent and sibling so that we can insert the
7321 // new content viewer's root view at the same position. Also save the
7322 // bounds of the root view's widget.
7324 nsView* rootViewSibling = nullptr;
7325 nsView* rootViewParent = nullptr;
7326 nsIntRect newBounds(0, 0, 0, 0);
7328 PresShell* oldPresShell = GetPresShell();
7329 if (oldPresShell) {
7330 nsViewManager* vm = oldPresShell->GetViewManager();
7331 if (vm) {
7332 nsView* oldRootView = vm->GetRootView();
7334 if (oldRootView) {
7335 rootViewSibling = oldRootView->GetNextSibling();
7336 rootViewParent = oldRootView->GetParent();
7338 mDocumentViewer->GetBounds(newBounds);
7343 nsCOMPtr<nsIContent> container;
7344 RefPtr<Document> sibling;
7345 if (rootViewParent && rootViewParent->GetParent()) {
7346 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7347 container = frame ? frame->GetContent() : nullptr;
7349 if (rootViewSibling) {
7350 nsIFrame* frame = rootViewSibling->GetFrame();
7351 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7354 // Transfer ownership to mDocumentViewer. By ensuring that either the
7355 // docshell or the session history, but not both, have references to the
7356 // content viewer, we prevent the viewer from being torn down after
7357 // Destroy() is called.
7359 if (mDocumentViewer) {
7360 mDocumentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7361 viewer->SetPreviousViewer(mDocumentViewer);
7363 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
7364 // We don't plan to save a viewer in mOSHE; tell it to drop
7365 // any other state it's holding.
7366 mOSHE->SyncPresentationState();
7369 // Order the mDocumentViewer setup just like Embed does.
7370 mDocumentViewer = nullptr;
7372 // Now that we're about to switch documents, forget all of our children.
7373 // Note that we cached them as needed up in CaptureState above.
7374 DestroyChildren();
7376 mDocumentViewer.swap(viewer);
7378 // Grab all of the related presentation from the SHEntry now.
7379 // Clearing the viewer from the SHEntry will clear all of this state.
7380 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7381 mLSHE->SetWindowState(nullptr);
7383 bool sticky = mLSHE->GetSticky();
7385 RefPtr<Document> document = mDocumentViewer->GetDocument();
7387 nsCOMArray<nsIDocShellTreeItem> childShells;
7388 int32_t i = 0;
7389 nsCOMPtr<nsIDocShellTreeItem> child;
7390 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7391 child) {
7392 childShells.AppendObject(child);
7395 // get the previous content viewer size
7396 nsIntRect oldBounds(0, 0, 0, 0);
7397 mLSHE->GetViewerBounds(oldBounds);
7399 // Restore the refresh URI list. The refresh timers will be restarted
7400 // when EndPageLoad() is called.
7401 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7403 // Reattach to the window object.
7404 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7405 rv = mDocumentViewer->Open(windowState, mLSHE);
7406 mIsRestoringDocument = false;
7408 // Hack to keep nsDocShellEditorData alive across the
7409 // SetContentViewer(nullptr) call below.
7410 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7412 // Now remove it from the cached presentation.
7413 mLSHE->SetDocumentViewer(nullptr);
7414 mEODForCurrentDocument = false;
7416 mLSHE->SetEditorData(data.release());
7418 #ifdef DEBUG
7420 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7421 nsCOMPtr<nsIDocShellTreeItem> childShell;
7422 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7423 NS_ASSERTION(!refreshURIs && !childShell,
7424 "SHEntry should have cleared presentation state");
7426 #endif
7428 // Restore the sticky state of the viewer. The viewer has set this state
7429 // on the history entry in Destroy() just before marking itself non-sticky,
7430 // to avoid teardown of the presentation.
7431 mDocumentViewer->SetSticky(sticky);
7433 NS_ENSURE_SUCCESS(rv, rv);
7435 // mLSHE is now our currently-loaded document.
7436 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7438 // We aren't going to restore any items from the LayoutHistoryState,
7439 // but we don't want them to stay around in case the page is reloaded.
7440 SetLayoutHistoryState(nullptr);
7442 // This is the end of our Embed() replacement
7444 mSavingOldViewer = false;
7445 mEODForCurrentDocument = false;
7447 if (document) {
7448 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7449 if (parent) {
7450 RefPtr<Document> d = parent->GetDocument();
7451 if (d) {
7452 if (d->EventHandlingSuppressed()) {
7453 document->SuppressEventHandling(d->EventHandlingSuppressed());
7458 // Use the uri from the mLSHE we had when we entered this function
7459 // (which need not match the document's URI if anchors are involved),
7460 // since that's the history entry we're loading. Note that if we use
7461 // origLSHE we don't have to worry about whether the entry in question
7462 // is still mLSHE or whether it's now mOSHE.
7463 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7464 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7465 /* aIsInitialAboutBlank */ false,
7466 /* aLocationFlags */ 0);
7469 // This is the end of our CreateDocumentViewer() replacement.
7470 // Now we simulate a load. First, we restore the state of the javascript
7471 // window object.
7472 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7473 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7475 // Now, dispatch a title change event which would happen as the
7476 // <head> is parsed.
7477 document->NotifyPossibleTitleChange(false);
7479 // Now we simulate appending child docshells for subframes.
7480 for (i = 0; i < childShells.Count(); ++i) {
7481 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7482 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7484 // Make sure to not clobber the state of the child. Since AddChild
7485 // always clobbers it, save it off first.
7486 bool allowRedirects;
7487 childShell->GetAllowMetaRedirects(&allowRedirects);
7489 bool allowSubframes;
7490 childShell->GetAllowSubframes(&allowSubframes);
7492 bool allowImages;
7493 childShell->GetAllowImages(&allowImages);
7495 bool allowMedia = childShell->GetAllowMedia();
7497 bool allowDNSPrefetch;
7498 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7500 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7501 bool allowContentRetargetingOnChildren =
7502 childShell->GetAllowContentRetargetingOnChildren();
7504 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7505 // the child inherits our state. Among other things, this means that the
7506 // child inherits our mPrivateBrowsingId, which is what we want.
7507 AddChild(childItem);
7509 childShell->SetAllowMetaRedirects(allowRedirects);
7510 childShell->SetAllowSubframes(allowSubframes);
7511 childShell->SetAllowImages(allowImages);
7512 childShell->SetAllowMedia(allowMedia);
7513 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7514 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7515 childShell->SetAllowContentRetargetingOnChildren(
7516 allowContentRetargetingOnChildren);
7518 rv = childShell->BeginRestore(nullptr, false);
7519 NS_ENSURE_SUCCESS(rv, rv);
7522 // Make sure to restore the window state after adding the child shells back
7523 // to the tree. This is necessary for Thaw() and Resume() to propagate
7524 // properly.
7525 rv = privWin->RestoreWindowState(windowState);
7526 NS_ENSURE_SUCCESS(rv, rv);
7528 RefPtr<PresShell> presShell = GetPresShell();
7530 // We may be displayed on a different monitor (or in a different
7531 // HiDPI mode) than when we got into the history list. So we need
7532 // to check if this has happened. See bug 838239.
7534 // Because the prescontext normally handles resolution changes via
7535 // a runnable (see nsPresContext::UIResolutionChanged), its device
7536 // context won't be -immediately- updated as a result of calling
7537 // presShell->BackingScaleFactorChanged().
7539 // But we depend on that device context when adjusting the view size
7540 // via mDocumentViewer->SetBounds(newBounds) below. So we need to
7541 // explicitly tell it to check for changed resolution here.
7542 if (presShell) {
7543 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7544 if (pc->DeviceContext()->CheckDPIChange()) {
7545 presShell->BackingScaleFactorChanged();
7547 // Recompute zoom and text-zoom and such.
7548 pc->RecomputeBrowsingContextDependentData();
7551 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7552 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7554 // Insert the new root view at the correct location in the view tree.
7555 if (container) {
7556 nsSubDocumentFrame* subDocFrame =
7557 do_QueryFrame(container->GetPrimaryFrame());
7558 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7559 } else {
7560 rootViewParent = nullptr;
7562 if (sibling && sibling->GetPresShell() &&
7563 sibling->GetPresShell()->GetViewManager()) {
7564 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7565 } else {
7566 rootViewSibling = nullptr;
7568 if (rootViewParent && newRootView &&
7569 newRootView->GetParent() != rootViewParent) {
7570 nsViewManager* parentVM = rootViewParent->GetViewManager();
7571 if (parentVM) {
7572 // InsertChild(parent, child, sib, true) inserts the child after
7573 // sib in content order, which is before sib in view order. BUT
7574 // when sib is null it inserts at the end of the the document
7575 // order, i.e., first in view order. But when oldRootSibling is
7576 // null, the old root as at the end of the view list --- last in
7577 // content order --- and we want to call InsertChild(parent, child,
7578 // nullptr, false) in that case.
7579 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7580 rootViewSibling ? true : false);
7582 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7583 "error in InsertChild");
7587 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7589 // If parent is suspended, increase suspension count.
7590 // This can't be done as early as event suppression since this
7591 // depends on docshell tree.
7592 privWinInner->SyncStateFromParentWindow();
7594 // Now that all of the child docshells have been put into place, we can
7595 // restart the timers for the window and all of the child frames.
7596 privWinInner->Resume();
7598 // Now that we have found the inner window of the page restored
7599 // from the history, we have to make sure that
7600 // performance.navigation.type is 2.
7601 Performance* performance = privWinInner->GetPerformance();
7602 if (performance) {
7603 performance->GetDOMTiming()->NotifyRestoreStart();
7606 // Restore the refresh URI list. The refresh timers will be restarted
7607 // when EndPageLoad() is called.
7608 mRefreshURIList = refreshURIList;
7610 // Meta-refresh timers have been restarted for this shell, but not
7611 // for our children. Walk the child shells and restart their timers.
7612 for (auto* childDocLoader : mChildList.ForwardRange()) {
7613 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7614 if (child) {
7615 child->ResumeRefreshURIs();
7619 // Make sure this presentation is the same size as the previous
7620 // presentation. If this is not the same size we showed it at last time,
7621 // then we need to resize the widget.
7623 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7624 // presentation had the infobar visible, then we will resize the new
7625 // presentation to that smaller size. However, firing the locationchanged
7626 // event will hide the infobar, which will immediately resize the window
7627 // back to the larger size. A future optimization might be to restore
7628 // the presentation at the "wrong" size, then fire the locationchanged
7629 // event and check whether the docshell's new size is the same as the
7630 // cached viewer size (skipping the resize if they are equal).
7632 if (newRootView) {
7633 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7634 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7635 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7636 newBounds.width, newBounds.height));
7637 mDocumentViewer->SetBounds(newBounds);
7638 } else {
7639 nsIScrollableFrame* rootScrollFrame =
7640 presShell->GetRootScrollFrameAsScrollable();
7641 if (rootScrollFrame) {
7642 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7647 // The FinishRestore call below can kill these, null them out so we don't
7648 // have invalid pointer lying around.
7649 newRootView = rootViewSibling = rootViewParent = nullptr;
7650 newVM = nullptr;
7652 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7653 // update it.
7654 if (oldPresShell && presShell &&
7655 presShell->IsUnderHiddenEmbedderElement() !=
7656 oldPresShell->IsUnderHiddenEmbedderElement()) {
7657 presShell->SetIsUnderHiddenEmbedderElement(
7658 oldPresShell->IsUnderHiddenEmbedderElement());
7661 // Simulate the completion of the load.
7662 nsDocShell::FinishRestore();
7664 // Restart plugins, and paint the content.
7665 if (presShell) {
7666 presShell->Thaw();
7669 return privWin->FireDelayedDOMEvents(true);
7672 nsresult nsDocShell::CreateDocumentViewer(const nsACString& aContentType,
7673 nsIRequest* aRequest,
7674 nsIStreamListener** aContentHandler) {
7675 *aContentHandler = nullptr;
7677 if (!mTreeOwner || mIsBeingDestroyed) {
7678 // If we don't have a tree owner, then we're in the process of being
7679 // destroyed. Rather than continue trying to load something, just give up.
7680 return NS_ERROR_DOCSHELL_DYING;
7683 if (!mBrowsingContext->AncestorsAreCurrent() ||
7684 mBrowsingContext->IsInBFCache()) {
7685 mBrowsingContext->RemoveRootFromBFCacheSync();
7686 return NS_ERROR_NOT_AVAILABLE;
7689 // Can we check the content type of the current content viewer
7690 // and reuse it without destroying it and re-creating it?
7692 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7694 // Instantiate the content viewer object
7695 nsCOMPtr<nsIDocumentViewer> viewer;
7696 nsresult rv = NewDocumentViewerObj(aContentType, aRequest, mLoadGroup,
7697 aContentHandler, getter_AddRefs(viewer));
7699 if (NS_FAILED(rv)) {
7700 return rv;
7703 // Notify the current document that it is about to be unloaded!!
7705 // It is important to fire the unload() notification *before* any state
7706 // is changed within the DocShell - otherwise, javascript will get the
7707 // wrong information :-(
7710 if (mSavingOldViewer) {
7711 // We determined that it was safe to cache the document presentation
7712 // at the time we initiated the new load. We need to check whether
7713 // it's still safe to do so, since there may have been DOM mutations
7714 // or new requests initiated.
7715 RefPtr<Document> doc = viewer->GetDocument();
7716 mSavingOldViewer = CanSavePresentation(
7717 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
7720 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7722 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7723 if (aOpenedChannel) {
7724 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7727 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7728 // it before we do call Embed.
7729 nsCOMPtr<nsIURI> previousURI = mCurrentURI;
7731 FirePageHideNotification(!mSavingOldViewer);
7732 if (mIsBeingDestroyed) {
7733 // Force to stop the newly created orphaned viewer.
7734 viewer->Stop();
7735 return NS_ERROR_DOCSHELL_DYING;
7737 mLoadingURI = nullptr;
7739 // Set mFiredUnloadEvent = false so that the unload handler for the
7740 // *new* document will fire.
7741 mFiredUnloadEvent = false;
7743 // we've created a new document so go ahead and call
7744 // OnNewURI(), but don't fire OnLocationChange()
7745 // notifications before we've called Embed(). See bug 284993.
7746 mURIResultedInDocument = true;
7747 bool errorOnLocationChangeNeeded = false;
7748 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7749 nsCOMPtr<nsIURI> failedURI;
7751 if (mLoadType == LOAD_ERROR_PAGE) {
7752 // We need to set the SH entry and our current URI here and not
7753 // at the moment we load the page. We want the same behavior
7754 // of Stop() as for a normal page load. See bug 514232 for details.
7756 // Revert mLoadType to load type to state the page load failed,
7757 // following function calls need it.
7758 mLoadType = mFailedLoadType;
7760 Document* doc = viewer->GetDocument();
7761 if (doc) {
7762 doc->SetFailedChannel(failedChannel);
7765 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7766 if (failedChannel) {
7767 // Make sure we have a URI to set currentURI.
7768 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7769 } else {
7770 // if there is no failed channel we have to explicitly provide
7771 // a triggeringPrincipal for the history entry.
7772 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7775 if (!failedURI) {
7776 failedURI = mFailedURI;
7778 if (!failedURI) {
7779 // We need a URI object to store a session history entry, so make up a URI
7780 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7783 // When we don't have failedURI, something wrong will happen. See
7784 // bug 291876.
7785 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7787 mFailedChannel = nullptr;
7788 mFailedURI = nullptr;
7790 // Create an shistory entry for the old load.
7791 if (failedURI) {
7792 errorOnLocationChangeNeeded =
7793 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7794 nullptr, nullptr, false, false);
7797 // Be sure to have a correct mLSHE, it may have been cleared by
7798 // EndPageLoad. See bug 302115.
7799 ChildSHistory* shistory = GetSessionHistory();
7800 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7801 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7802 if (idx == -1) {
7803 idx = shistory->Index();
7805 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7808 mLoadType = LOAD_ERROR_PAGE;
7811 nsCOMPtr<nsIURI> finalURI;
7812 // If this a redirect, use the final url (uri)
7813 // else use the original url
7815 // Note that this should match what documents do (see Document::Reset).
7816 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7818 bool onLocationChangeNeeded = false;
7819 if (finalURI) {
7820 // Pass false for aCloneSHChildren, since we're loading a new page here.
7821 onLocationChangeNeeded = OnNewURI(finalURI, aOpenedChannel, nullptr,
7822 nullptr, nullptr, nullptr, true, false);
7825 // let's try resetting the load group if we need to...
7826 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7827 NS_ENSURE_SUCCESS(
7828 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7829 NS_ERROR_FAILURE);
7831 if (currentLoadGroup != mLoadGroup) {
7832 nsLoadFlags loadFlags = 0;
7834 // Cancel any URIs that are currently loading...
7835 // XXX: Need to do this eventually Stop();
7837 // Retarget the document to this loadgroup...
7839 /* First attach the channel to the right loadgroup
7840 * and then remove from the old loadgroup. This
7841 * puts the notifications in the right order and
7842 * we don't null-out mLSHE in OnStateChange() for
7843 * all redirected urls
7845 aOpenedChannel->SetLoadGroup(mLoadGroup);
7847 // Mark the channel as being a document URI...
7848 aOpenedChannel->GetLoadFlags(&loadFlags);
7849 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7850 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7851 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7852 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7855 aOpenedChannel->SetLoadFlags(loadFlags);
7857 mLoadGroup->AddRequest(aRequest, nullptr);
7858 if (currentLoadGroup) {
7859 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7862 // Update the notification callbacks, so that progress and
7863 // status information are sent to the right docshell...
7864 aOpenedChannel->SetNotificationCallbacks(this);
7867 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
7868 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
7869 aOpenedChannel, previousURI),
7870 NS_ERROR_FAILURE);
7872 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
7873 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
7876 mSavedRefreshURIList = nullptr;
7877 mSavingOldViewer = false;
7878 mEODForCurrentDocument = false;
7880 // if this document is part of a multipart document,
7881 // the ID can be used to distinguish it from the other parts.
7882 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
7883 if (multiPartChannel) {
7884 if (PresShell* presShell = GetPresShell()) {
7885 if (Document* doc = presShell->GetDocument()) {
7886 uint32_t partID;
7887 multiPartChannel->GetPartID(&partID);
7888 doc->SetPartID(partID);
7893 if (errorOnLocationChangeNeeded) {
7894 FireOnLocationChange(this, failedChannel, failedURI,
7895 LOCATION_CHANGE_ERROR_PAGE);
7896 } else if (onLocationChangeNeeded) {
7897 uint32_t locationFlags =
7898 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
7899 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
7902 return NS_OK;
7905 nsresult nsDocShell::NewDocumentViewerObj(const nsACString& aContentType,
7906 nsIRequest* aRequest,
7907 nsILoadGroup* aLoadGroup,
7908 nsIStreamListener** aContentHandler,
7909 nsIDocumentViewer** aViewer) {
7910 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7912 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
7913 nsContentUtils::FindInternalDocumentViewer(aContentType);
7914 if (!docLoaderFactory) {
7915 return NS_ERROR_FAILURE;
7918 // Now create an instance of the content viewer nsLayoutDLF makes the
7919 // determination if it should be a "view-source" instead of "view"
7920 nsresult rv = docLoaderFactory->CreateInstance(
7921 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
7922 aContentHandler, aViewer);
7923 NS_ENSURE_SUCCESS(rv, rv);
7925 (*aViewer)->SetContainer(this);
7926 return NS_OK;
7929 nsresult nsDocShell::SetupNewViewer(nsIDocumentViewer* aNewViewer,
7930 WindowGlobalChild* aWindowActor) {
7931 MOZ_ASSERT(!mIsBeingDestroyed);
7934 // Copy content viewer state from previous or parent content viewer.
7936 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7938 // Do NOT to maintain a reference to the old content viewer outside
7939 // of this "copying" block, or it will not be destroyed until the end of
7940 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7942 // In this block of code, if we get an error result, we return it
7943 // but if we get a null pointer, that's perfectly legal for parent
7944 // and parentContentViewer.
7947 int32_t x = 0;
7948 int32_t y = 0;
7949 int32_t cx = 0;
7950 int32_t cy = 0;
7952 // This will get the size from the current content viewer or from the
7953 // Init settings
7954 DoGetPositionAndSize(&x, &y, &cx, &cy);
7956 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7957 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
7958 NS_ERROR_FAILURE);
7959 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7961 const Encoding* reloadEncoding = nullptr;
7962 int32_t reloadEncodingSource = kCharsetUninitialized;
7963 // |newMUDV| also serves as a flag to set the data from the above vars
7964 nsCOMPtr<nsIDocumentViewer> newViewer;
7966 if (mDocumentViewer || parent) {
7967 nsCOMPtr<nsIDocumentViewer> oldViewer;
7968 if (mDocumentViewer) {
7969 // Get any interesting state from old content viewer
7970 // XXX: it would be far better to just reuse the document viewer ,
7971 // since we know we're just displaying the same document as before
7972 oldViewer = mDocumentViewer;
7974 // Tell the old content viewer to hibernate in session history when
7975 // it is destroyed.
7977 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7978 if (mOSHE) {
7979 mOSHE->SyncPresentationState();
7981 mSavingOldViewer = false;
7983 } else {
7984 // No old content viewer, so get state from parent's content viewer
7985 parent->GetDocViewer(getter_AddRefs(oldViewer));
7988 if (oldViewer) {
7989 newViewer = aNewViewer;
7990 if (newViewer) {
7991 reloadEncoding =
7992 oldViewer->GetReloadEncodingAndSource(&reloadEncodingSource);
7997 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
7998 bool isUnderHiddenEmbedderElement = false;
7999 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8000 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
8001 if (viewer) {
8002 // Stop any activity that may be happening in the old document before
8003 // releasing it...
8004 viewer->Stop();
8006 // Try to extract the canvas background color from the old
8007 // presentation shell, so we can use it for the next document.
8008 if (PresShell* presShell = viewer->GetPresShell()) {
8009 bgcolor = presShell->GetCanvasBackground();
8010 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
8013 viewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8014 aNewViewer->SetPreviousViewer(viewer);
8016 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
8017 // We don't plan to save a viewer in mOSHE; tell it to drop
8018 // any other state it's holding.
8019 mOSHE->SyncPresentationState();
8022 mDocumentViewer = nullptr;
8024 // Now that we're about to switch documents, forget all of our children.
8025 // Note that we cached them as needed up in CaptureState above.
8026 DestroyChildren();
8028 mDocumentViewer = aNewViewer;
8030 nsCOMPtr<nsIWidget> widget;
8031 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8033 nsIntRect bounds(x, y, cx, cy);
8035 mDocumentViewer->SetNavigationTiming(mTiming);
8037 if (NS_FAILED(mDocumentViewer->Init(widget, bounds, aWindowActor))) {
8038 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
8039 viewer->Close(nullptr);
8040 viewer->Destroy();
8041 mDocumentViewer = nullptr;
8042 SetCurrentURIInternal(nullptr);
8043 NS_WARNING("ContentViewer Initialization failed");
8044 return NS_ERROR_FAILURE;
8047 // If we have old state to copy, set the old state onto the new content
8048 // viewer
8049 if (newViewer) {
8050 newViewer->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8053 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8055 // Stuff the bgcolor from the old pres shell into the new
8056 // pres shell. This improves page load continuity.
8057 if (RefPtr<PresShell> presShell = mDocumentViewer->GetPresShell()) {
8058 presShell->SetCanvasBackground(bgcolor);
8059 presShell->ActivenessMaybeChanged();
8060 if (isUnderHiddenEmbedderElement) {
8061 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8065 // XXX: It looks like the LayoutState gets restored again in Embed()
8066 // right after the call to SetupNewViewer(...)
8068 // We don't show the mDocumentViewer yet, since we want to draw the old page
8069 // until we have enough of the new page to show. Just return with the new
8070 // viewer still set to hidden.
8072 return NS_OK;
8075 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8076 SessionHistoryInfo* aInfo) {
8077 NS_ENSURE_TRUE_VOID(mDocumentViewer);
8079 RefPtr<Document> document = GetDocument();
8080 NS_ENSURE_TRUE_VOID(document);
8082 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8083 if (mozilla::SessionHistoryInParent()) {
8084 // If aInfo is null, just set the document's state object to null.
8085 if (aInfo) {
8086 scContainer = aInfo->GetStateData();
8088 MOZ_LOG(gSHLog, LogLevel::Debug,
8089 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8090 } else {
8091 if (aShEntry) {
8092 scContainer = aShEntry->GetStateData();
8094 // If aShEntry is null, just set the document's state object to null.
8098 // It's OK for scContainer too be null here; that just means there's no
8099 // state data associated with this history entry.
8100 document->SetStateObject(scContainer);
8103 nsresult nsDocShell::CheckLoadingPermissions() {
8104 // This method checks whether the caller may load content into
8105 // this docshell. Even though we've done our best to hide windows
8106 // from code that doesn't have the right to access them, it's
8107 // still possible for an evil site to open a window and access
8108 // frames in the new window through window.frames[] (which is
8109 // allAccess for historic reasons), so we still need to do this
8110 // check on load.
8111 nsresult rv = NS_OK;
8113 if (!IsSubframe()) {
8114 // We're not a frame. Permit all loads.
8115 return rv;
8118 // Note - The check for a current JSContext here isn't necessarily sensical.
8119 // It's just designed to preserve the old semantics during a mass-conversion
8120 // patch.
8121 if (!nsContentUtils::GetCurrentJSContext()) {
8122 return NS_OK;
8125 // Check if the caller is from the same origin as this docshell,
8126 // or any of its ancestors.
8127 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8128 bc = bc->GetParent()) {
8129 // If the BrowsingContext is not in process, then it
8130 // is true by construction that its principal will not
8131 // subsume the current docshell principal.
8132 if (!bc->IsInProcess()) {
8133 continue;
8136 nsCOMPtr<nsIScriptGlobalObject> sgo =
8137 bc->GetDocShell()->GetScriptGlobalObject();
8138 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8140 nsIPrincipal* p;
8141 if (!sop || !(p = sop->GetPrincipal())) {
8142 return NS_ERROR_UNEXPECTED;
8145 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8146 // Same origin, permit load
8147 return NS_OK;
8151 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8154 //*****************************************************************************
8155 // nsDocShell: Site Loading
8156 //*****************************************************************************
8158 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8159 bool aInPrivateBrowsing) {
8160 if (XRE_IsContentProcess()) {
8161 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8162 if (contentChild) {
8163 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8165 return;
8168 #ifdef MOZ_PLACES
8169 nsCOMPtr<nsIFaviconService> favSvc =
8170 do_GetService("@mozilla.org/browser/favicon-service;1");
8171 if (favSvc) {
8172 favSvc->CopyFavicons(aOldURI, aNewURI,
8173 aInPrivateBrowsing
8174 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8175 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8176 nullptr);
8178 #endif
8181 class InternalLoadEvent : public Runnable {
8182 public:
8183 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8184 : mozilla::Runnable("InternalLoadEvent"),
8185 mDocShell(aDocShell),
8186 mLoadState(aLoadState) {
8187 // For events, both target and filename should be the version of "null" they
8188 // expect. By the time the event is fired, both window targeting and file
8189 // downloading have been handled, so we should never have an internal load
8190 // event that retargets or had a download.
8191 mLoadState->SetTarget(u""_ns);
8192 mLoadState->SetFileName(VoidString());
8195 NS_IMETHOD
8196 Run() override {
8197 #ifndef ANDROID
8198 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8199 "InternalLoadEvent: Should always have a principal here");
8200 #endif
8201 return mDocShell->InternalLoad(mLoadState);
8204 private:
8205 RefPtr<nsDocShell> mDocShell;
8206 RefPtr<nsDocShellLoadState> mLoadState;
8210 * Returns true if we started an asynchronous load (i.e., from the network), but
8211 * the document we're loading there hasn't yet become this docshell's active
8212 * document.
8214 * When JustStartedNetworkLoad is true, you should be careful about modifying
8215 * mLoadType and mLSHE. These are both set when the asynchronous load first
8216 * starts, and the load expects that, when it eventually runs InternalLoad,
8217 * mLoadType and mLSHE will have their original values.
8219 bool nsDocShell::JustStartedNetworkLoad() {
8220 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8223 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8224 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8225 // <embed>, etc)
8227 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8228 // later when we call DoURILoad.
8229 nsContentPolicyType nsDocShell::DetermineContentType() {
8230 if (!IsSubframe()) {
8231 return nsIContentPolicy::TYPE_DOCUMENT;
8234 const auto& maybeEmbedderElementType =
8235 GetBrowsingContext()->GetEmbedderElementType();
8236 if (!maybeEmbedderElementType) {
8237 // If the EmbedderElementType hasn't been set yet, just assume we're
8238 // an iframe since that's more common.
8239 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8242 return maybeEmbedderElementType->EqualsLiteral("iframe")
8243 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8244 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8247 bool nsDocShell::NoopenerForceEnabled() {
8248 // If current's top-level browsing context's active document's
8249 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8250 // if currentDoc's origin is not same origin with currentDoc's top-level
8251 // origin, noopener is force enabled, and name is cleared to "_blank".
8252 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8253 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8254 topPolicy ==
8255 nsILoadInfo::
8256 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8257 !mBrowsingContext->SameOriginWithTop();
8260 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8261 MOZ_ASSERT(aLoadState, "need a load state!");
8262 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8263 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8264 "should not have picked target yet");
8266 nsresult rv = NS_OK;
8267 RefPtr<BrowsingContext> targetContext;
8269 // Only _self, _parent, and _top are supported in noopener case. But we
8270 // have to be careful to not apply that to the noreferrer case. See bug
8271 // 1358469.
8272 bool allowNamedTarget =
8273 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8274 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8275 if (allowNamedTarget ||
8276 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8277 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8278 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8279 Document* document = GetDocument();
8280 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8281 WindowGlobalChild* wgc = document->GetWindowGlobalChild();
8282 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
8283 targetContext = wgc->FindBrowsingContextWithName(
8284 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8287 if (!targetContext) {
8288 // If the targetContext doesn't exist, then this is a new docShell and we
8289 // should consider this a TYPE_DOCUMENT load
8291 // For example, when target="_blank"
8293 // If there's no targetContext, that means we are about to create a new
8294 // window. Perform a content policy check before creating the window. Please
8295 // note for all other docshell loads content policy checks are performed
8296 // within the contentSecurityManager when the channel is about to be
8297 // openend.
8298 nsISupports* requestingContext = nullptr;
8299 if (XRE_IsContentProcess()) {
8300 // In e10s the child process doesn't have access to the element that
8301 // contains the browsing context (because that element is in the chrome
8302 // process). So we just pass mScriptGlobal.
8303 requestingContext = ToSupports(mScriptGlobal);
8304 } else {
8305 // This is for loading non-e10s tabs and toplevel windows of various
8306 // sorts.
8307 // For the toplevel window cases, requestingElement will be null.
8308 nsCOMPtr<Element> requestingElement =
8309 mScriptGlobal->GetFrameElementInternal();
8310 requestingContext = requestingElement;
8313 // Ideally we should use the same loadinfo as within DoURILoad which
8314 // should match this one when both are applicable.
8315 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
8316 new LoadInfo(mScriptGlobal, aLoadState->URI(),
8317 aLoadState->TriggeringPrincipal(), requestingContext,
8318 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8320 // Since Content Policy checks are performed within docShell as well as
8321 // the ContentSecurityManager we need a reliable way to let certain
8322 // nsIContentPolicy consumers ignore duplicate calls.
8323 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8325 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8326 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8327 &shouldLoad);
8329 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8330 if (NS_SUCCEEDED(rv)) {
8331 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8332 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8334 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8335 return NS_ERROR_BLOCKED_BY_POLICY;
8339 return NS_ERROR_CONTENT_BLOCKED;
8344 // Resolve the window target before going any further...
8345 // If the load has been targeted to another DocShell, then transfer the
8346 // load to it...
8349 // We've already done our owner-inheriting. Mask out that bit, so we
8350 // don't try inheriting an owner from the target window if we came up
8351 // with a null owner above.
8352 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8354 if (!targetContext) {
8355 // If the docshell's document is sandboxed, only open a new window
8356 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8357 // (i.e. if allow-popups is specified)
8358 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8359 Document* doc = mDocumentViewer->GetDocument();
8361 const bool isDocumentAuxSandboxed =
8362 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8364 if (isDocumentAuxSandboxed) {
8365 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8368 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8369 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8371 RefPtr<BrowsingContext> newBC;
8372 nsAutoCString spec;
8373 aLoadState->URI()->GetSpec(spec);
8375 // If we are a noopener load, we just hand the whole thing over to our
8376 // window.
8377 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8378 NoopenerForceEnabled()) {
8379 // Various asserts that we know to hold because NO_OPENER loads can only
8380 // happen for links.
8381 MOZ_ASSERT(!aLoadState->LoadReplace());
8382 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8383 aLoadState->TriggeringPrincipal());
8384 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8385 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8386 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8387 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8388 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8389 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8390 aLoadState->IsFormSubmission());
8391 MOZ_ASSERT(!aLoadState->HeadersStream());
8392 // If OnLinkClickSync was invoked inside the onload handler, the load
8393 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8394 // LOAD_LINK.
8395 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8396 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8397 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8398 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8400 RefPtr<nsDocShellLoadState> loadState =
8401 new nsDocShellLoadState(aLoadState->URI());
8403 // Set up our loadinfo so it will do the load as much like we would have
8404 // as possible.
8405 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8406 loadState->SetOriginalURI(aLoadState->OriginalURI());
8408 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8409 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8411 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8412 loadState->SetKeepResultPrincipalURIIfSet(
8413 aLoadState->KeepResultPrincipalURIIfSet());
8414 // LoadReplace will always be false due to asserts above, skip setting
8415 // it.
8416 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8417 loadState->SetTriggeringSandboxFlags(
8418 aLoadState->TriggeringSandboxFlags());
8419 loadState->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
8420 loadState->SetTriggeringStorageAccess(
8421 aLoadState->TriggeringStorageAccess());
8422 loadState->SetCsp(aLoadState->Csp());
8423 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8424 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8425 // Explicit principal because we do not want any guesses as to what the
8426 // principal to inherit is: it should be aTriggeringPrincipal.
8427 loadState->SetPrincipalIsExplicit(true);
8428 loadState->SetLoadType(aLoadState->LoadType());
8429 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8430 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8432 loadState->SetHasValidUserGestureActivation(
8433 aLoadState->HasValidUserGestureActivation());
8435 // Propagate POST data to the new load.
8436 loadState->SetPostDataStream(aLoadState->PostDataStream());
8437 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8439 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8440 aLoadState->Target(), // window name
8441 u""_ns, // Features
8442 loadState,
8443 true, // aForceNoOpener
8444 getter_AddRefs(newBC));
8445 MOZ_ASSERT(!newBC);
8446 return rv;
8449 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8450 aLoadState->Target(), // window name
8451 u""_ns, // Features
8452 getter_AddRefs(newBC));
8454 // In some cases the Open call doesn't actually result in a new
8455 // window being opened. We can detect these cases by examining the
8456 // document in |newBC|, if any.
8457 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8458 newBC ? newBC->GetDOMWindow() : nullptr;
8459 if (piNewWin) {
8460 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8461 if (!newDoc || newDoc->IsInitialDocument()) {
8462 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8466 if (newBC) {
8467 targetContext = newBC;
8470 NS_ENSURE_SUCCESS(rv, rv);
8471 NS_ENSURE_TRUE(targetContext, rv);
8473 // If our target BrowsingContext is still pending initialization, ignore the
8474 // navigation request targeting it.
8475 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8476 return NS_OK;
8479 aLoadState->SetTargetBrowsingContext(targetContext);
8480 if (aLoadState->IsFormSubmission()) {
8481 aLoadState->SetLoadType(
8482 GetLoadTypeForFormSubmission(targetContext, aLoadState));
8486 // Transfer the load to the target BrowsingContext... Clear the window target
8487 // name to the empty string to prevent recursive retargeting!
8489 // No window target
8490 aLoadState->SetTarget(u""_ns);
8491 // No forced download
8492 aLoadState->SetFileName(VoidString());
8493 return targetContext->InternalLoad(aLoadState);
8496 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8497 nsAutoCString result;
8498 if (NS_FAILED(aURI->GetRef(result))) {
8499 result.SetIsVoid(true);
8501 return result;
8504 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8505 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8507 bool equal = false;
8508 if (mCurrentURI &&
8509 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8510 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8511 flags |= LOCATION_CHANGE_HASHCHANGE;
8514 return flags;
8517 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8518 SameDocumentNavigationState& aState) {
8519 MOZ_ASSERT(aLoadState);
8520 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8521 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8522 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8523 LOAD_FLAGS_REPLACE_HISTORY) ||
8524 aLoadState->LoadType() == LOAD_HISTORY ||
8525 aLoadState->LoadType() == LOAD_LINK)) {
8526 return false;
8529 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8531 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8532 if (NS_SUCCEEDED(rvURINew)) {
8533 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8536 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8537 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8538 if (NS_SUCCEEDED(rvURIOld)) {
8539 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8541 if (NS_SUCCEEDED(rvURIOld)) {
8542 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8543 &aState.mSameExceptHashes))) {
8544 aState.mSameExceptHashes = false;
8549 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8550 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8551 nsCOMPtr<nsIURI> currentExposableURI =
8552 nsIOService::CreateExposableURI(currentURI);
8553 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8554 if (NS_SUCCEEDED(rvURIOld)) {
8555 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8557 if (NS_SUCCEEDED(rvURIOld)) {
8558 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8559 aLoadState->URI(), &aState.mSameExceptHashes))) {
8560 aState.mSameExceptHashes = false;
8562 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8563 // have to perform a special check here to avoid an actual navigation. If
8564 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8565 // fact that the new URI is currently http), then set mSameExceptHashes to
8566 // true and only perform a fragment navigation.
8567 if (!aState.mSameExceptHashes) {
8568 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
8569 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8570 if (!docLoadInfo->GetLoadErrorPage() &&
8571 nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8572 currentExposableURI, aLoadState->URI(), docLoadInfo)) {
8573 uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
8574 if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
8575 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
8576 // At this point the requested URI is for sure a fragment
8577 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8578 // enabled. Also it is not interfering the upgrade order of
8579 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8580 // Since we are on an HTTPS site the fragment
8581 // navigation should also be an HTTPS.
8582 // For that reason we should upgrade the URI to HTTPS.
8583 aState.mSecureUpgradeURI = true;
8584 aState.mSameExceptHashes = true;
8592 if (mozilla::SessionHistoryInParent()) {
8593 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8594 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8595 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8597 MOZ_LOG(gSHLog, LogLevel::Debug,
8598 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8599 this, aState.mHistoryNavBetweenSameDoc));
8600 } else {
8601 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8602 // We're doing a history load.
8604 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8605 &aState.mHistoryNavBetweenSameDoc);
8609 // A same document navigation happens when we navigate between two SHEntries
8610 // for the same document. We do a same document navigation under two
8611 // circumstances. Either
8613 // a) we're navigating between two different SHEntries which share a
8614 // document, or
8616 // b) we're navigating to a new shentry whose URI differs from the
8617 // current URI only in its hash, the new hash is non-empty, and
8618 // we're not doing a POST.
8620 // The restriction that the SHEntries in (a) must be different ensures
8621 // that history.go(0) and the like trigger full refreshes, rather than
8622 // same document navigations.
8623 if (!mozilla::SessionHistoryInParent()) {
8624 bool doSameDocumentNavigation =
8625 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8626 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8627 aState.mSameExceptHashes && aState.mNewURIHasRef);
8628 MOZ_LOG(gSHLog, LogLevel::Debug,
8629 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8630 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8631 return doSameDocumentNavigation;
8634 if (aState.mHistoryNavBetweenSameDoc &&
8635 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8636 return true;
8639 MOZ_LOG(
8640 gSHLog, LogLevel::Debug,
8641 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8642 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8643 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8644 !aLoadState->PostDataStream() ? "true" : "false",
8645 aState.mSameExceptHashes ? "true" : "false",
8646 aState.mNewURIHasRef ? "true" : "false"));
8647 return !aLoadState->LoadIsFromSessionHistory() &&
8648 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8649 aState.mNewURIHasRef;
8652 nsresult nsDocShell::HandleSameDocumentNavigation(
8653 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState,
8654 bool& aSameDocument) {
8655 aSameDocument = true;
8656 #ifdef DEBUG
8657 SameDocumentNavigationState state;
8658 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8659 #endif
8661 MOZ_LOG(gSHLog, LogLevel::Debug,
8662 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8663 mCurrentURI->GetSpecOrDefault().get(),
8664 aLoadState->URI()->GetSpecOrDefault().get()));
8666 RefPtr<Document> doc = GetDocument();
8667 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8668 doc->DoNotifyPossibleTitleChange();
8670 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8672 // We need to upgrade the new URI from http: to https:
8673 nsCOMPtr<nsIURI> newURI = aLoadState->URI();
8674 if (aState.mSecureUpgradeURI) {
8675 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
8676 MOZ_LOG(gSHLog, LogLevel::Debug,
8677 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
8680 if (StaticPrefs::dom_security_setdocumenturi()) {
8681 // check if aLoadState->URI(), principalURI, mCurrentURI are same origin
8682 // skip handling otherwise
8683 nsCOMPtr<nsIPrincipal> origPrincipal = doc->NodePrincipal();
8684 nsCOMPtr<nsIURI> principalURI = origPrincipal->GetURI();
8685 if (origPrincipal->GetIsNullPrincipal()) {
8686 nsCOMPtr<nsIPrincipal> precursor = origPrincipal->GetPrecursorPrincipal();
8687 if (precursor) {
8688 principalURI = precursor->GetURI();
8692 auto isLoadableViaInternet = [](nsIURI* uri) {
8693 return (uri && (net::SchemeIsHTTP(uri) || net::SchemeIsHTTPS(uri)));
8696 if (isLoadableViaInternet(principalURI) &&
8697 isLoadableViaInternet(mCurrentURI) && isLoadableViaInternet(newURI)) {
8698 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
8699 if (!NS_SUCCEEDED(
8700 ssm->CheckSameOriginURI(newURI, principalURI, false, false)) ||
8701 !NS_SUCCEEDED(ssm->CheckSameOriginURI(mCurrentURI, principalURI,
8702 false, false))) {
8703 MOZ_LOG(gSHLog, LogLevel::Debug,
8704 ("nsDocShell[%p]: possible violation of the same origin policy "
8705 "during same document navigation",
8706 this));
8707 aSameDocument = false;
8708 return NS_OK;
8713 #ifdef DEBUG
8714 if (aState.mSameExceptHashes) {
8715 bool sameExceptHashes = false;
8716 currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
8717 MOZ_ASSERT(sameExceptHashes);
8719 #endif
8721 // Save the position of the scrollers.
8722 nsPoint scrollPos = GetCurScrollPos();
8724 // Reset mLoadType to its original value once we exit this block, because this
8725 // same document navigation might have started after a normal, network load,
8726 // and we don't want to clobber its load type. See bug 737307.
8727 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8729 // If a non-same-document-navigation (i.e., a network load) is pending, make
8730 // this a replacement load, so that we don't add a SHEntry here and the
8731 // network load goes into the SHEntry it expects to.
8732 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8733 mLoadType = LOAD_NORMAL_REPLACE;
8734 } else {
8735 mLoadType = aLoadState->LoadType();
8738 mURIResultedInDocument = true;
8740 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8742 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8743 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8744 // onLocationChange() notifications to the browser to update back/forward
8745 // buttons.
8746 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8747 Nothing());
8748 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8749 mLoadingEntry.swap(oldLoadingEntry);
8750 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8751 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8752 *aLoadState->GetLoadingSessionHistoryInfo());
8753 mNeedToReportActiveAfterLoadingBecomesActive = false;
8756 // Set the doc's URI according to the new history entry's URI.
8757 doc->SetDocumentURI(newURI);
8759 /* This is a anchor traversal within the same page.
8760 * call OnNewURI() so that, this traversal will be
8761 * recorded in session and global history.
8763 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8764 newURIPartitionedPrincipalToInherit;
8765 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8766 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8767 if (mozilla::SessionHistoryInParent()) {
8768 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8769 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8770 newURIPartitionedPrincipalToInherit =
8771 mActiveEntry->GetPartitionedPrincipalToInherit();
8772 newCsp = mActiveEntry->GetCsp();
8773 } else {
8774 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8775 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8776 newURIPartitionedPrincipalToInherit =
8777 mOSHE->GetPartitionedPrincipalToInherit();
8778 newCsp = mOSHE->GetCsp();
8780 } else {
8781 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8782 newURIPrincipalToInherit = doc->NodePrincipal();
8783 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8784 newCsp = doc->GetCsp();
8787 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
8789 // Pass true for aCloneSHChildren, since we're not
8790 // changing documents here, so all of our subframes are
8791 // still relevant to the new session history entry.
8793 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8794 // flag on firing onLocationChange(...).
8795 // Anyway, aCloneSHChildren param is simply reflecting
8796 // doSameDocumentNavigation in this scope.
8798 // Note: we'll actually fire onLocationChange later, in order to preserve
8799 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8800 // 1668126)
8801 bool locationChangeNeeded = OnNewURI(
8802 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
8803 newURIPartitionedPrincipalToInherit, newCsp, true, true);
8805 nsCOMPtr<nsIInputStream> postData;
8806 nsCOMPtr<nsIReferrerInfo> referrerInfo;
8807 uint32_t cacheKey = 0;
8809 bool scrollRestorationIsManual = false;
8810 if (!mozilla::SessionHistoryInParent()) {
8811 if (mOSHE) {
8812 /* save current position of scroller(s) (bug 59774) */
8813 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8814 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8815 // Get the postdata, page ident and referrer info from the current page,
8816 // if the new load is being done via normal means. Note that "normal
8817 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
8818 // the loadType and allowScroll check above -- it filters out some
8819 // LOAD_CMD_NORMAL cases that we wouldn't want here.
8820 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8821 postData = mOSHE->GetPostData();
8822 cacheKey = mOSHE->GetCacheKey();
8823 referrerInfo = mOSHE->GetReferrerInfo();
8826 // Link our new SHEntry to the old SHEntry's back/forward
8827 // cache data, since the two SHEntries correspond to the
8828 // same document.
8829 if (mLSHE) {
8830 if (!aLoadState->LoadIsFromSessionHistory()) {
8831 // If we're not doing a history load, scroll restoration
8832 // should be inherited from the previous session history entry.
8833 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8834 scrollRestorationIsManual);
8836 mLSHE->AdoptBFCacheEntry(mOSHE);
8839 } else {
8840 if (mActiveEntry) {
8841 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8842 if (mBrowsingContext) {
8843 CollectWireframe();
8844 if (XRE_IsParentProcess()) {
8845 SessionHistoryEntry* entry =
8846 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8847 if (entry) {
8848 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8850 } else {
8851 mozilla::Unused << ContentChild::GetSingleton()
8852 ->SendSessionHistoryEntryScrollPosition(
8853 mBrowsingContext, scrollPos.x,
8854 scrollPos.y);
8858 if (mLoadingEntry) {
8859 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8860 // If we're not doing a history load, scroll restoration
8861 // should be inherited from the previous session history entry.
8862 // XXX This needs most probably tweaks once fragment navigation is
8863 // fixed to work with session-history-in-parent.
8864 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8865 scrollRestorationIsManual);
8870 // If we're doing a history load, use its scroll restoration state.
8871 if (aLoadState->LoadIsFromSessionHistory()) {
8872 if (mozilla::SessionHistoryInParent()) {
8873 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8874 ->mInfo.GetScrollRestorationIsManual();
8875 } else {
8876 scrollRestorationIsManual =
8877 aLoadState->SHEntry()->GetScrollRestorationIsManual();
8881 /* Assign mLSHE to mOSHE. This will either be a new entry created
8882 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8883 * loads.
8885 if (!mozilla::SessionHistoryInParent()) {
8886 if (mLSHE) {
8887 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8888 // Save the postData obtained from the previous page
8889 // in to the session history entry created for the
8890 // anchor page, so that any history load of the anchor
8891 // page will restore the appropriate postData.
8892 if (postData) {
8893 mOSHE->SetPostData(postData);
8896 // Make sure we won't just repost without hitting the
8897 // cache first
8898 if (cacheKey != 0) {
8899 mOSHE->SetCacheKey(cacheKey);
8902 // As the document has not changed, the referrer info hasn't changed too,
8903 // so we can just copy it over.
8904 if (referrerInfo) {
8905 mOSHE->SetReferrerInfo(referrerInfo);
8909 /* Set the title for the SH entry for this target url so that
8910 * SH menus in go/back/forward buttons won't be empty for this.
8911 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
8912 * the code above.
8913 * Note, when session history lives in the parent process, this does not
8914 * update the title there.
8916 SetTitleOnHistoryEntry(false);
8917 } else {
8918 if (aLoadState->LoadIsFromSessionHistory()) {
8919 MOZ_LOG(
8920 gSHLog, LogLevel::Debug,
8921 ("Moving the loading entry to the active entry on nsDocShell %p to "
8922 "%s",
8923 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
8925 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
8926 if (mActiveEntry) {
8927 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
8930 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
8931 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
8932 if (currentLayoutHistoryState) {
8933 // Restore the existing nsILayoutHistoryState object, since it is
8934 // possibly being used by the layout. When doing a new load, the
8935 // shared state is copied from the existing active entry, so this
8936 // special case is needed only with the history loads.
8937 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
8940 if (cacheKey != 0) {
8941 mActiveEntry->SetCacheKey(cacheKey);
8943 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
8944 // does require a non-null uri if this is for a refresh load of the same
8945 // URI, but in that case mCurrentURI won't be null here.
8946 mBrowsingContext->SessionHistoryCommit(
8947 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
8948 true, true,
8949 /* No expiration update on the same document loads*/
8950 false, cacheKey);
8951 // FIXME Need to set postdata.
8953 // Set the title for the SH entry for this target url so that
8954 // SH menus in go/back/forward buttons won't be empty for this.
8955 // Note, when session history lives in the parent process, this does not
8956 // update the title there.
8957 SetTitleOnHistoryEntry(false);
8958 } else {
8959 Maybe<bool> scrollRestorationIsManual;
8960 if (mActiveEntry) {
8961 scrollRestorationIsManual.emplace(
8962 mActiveEntry->GetScrollRestorationIsManual());
8964 // Get the postdata, page ident and referrer info from the current page,
8965 // if the new load is being done via normal means. Note that "normal
8966 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
8967 // the loadType and allowScroll check above -- it filters out some
8968 // LOAD_CMD_NORMAL cases that we wouldn't want here.
8969 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8970 postData = mActiveEntry->GetPostData();
8971 cacheKey = mActiveEntry->GetCacheKey();
8972 referrerInfo = mActiveEntry->GetReferrerInfo();
8976 MOZ_LOG(gSHLog, LogLevel::Debug,
8977 ("Creating an active entry on nsDocShell %p to %s", this,
8978 newURI->GetSpecOrDefault().get()));
8979 if (mActiveEntry) {
8980 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, newURI);
8981 } else {
8982 mActiveEntry = MakeUnique<SessionHistoryInfo>(
8983 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
8984 newURIPartitionedPrincipalToInherit, newCsp, mContentTypeHint);
8987 // Save the postData obtained from the previous page in to the session
8988 // history entry created for the anchor page, so that any history load of
8989 // the anchor page will restore the appropriate postData.
8990 if (postData) {
8991 mActiveEntry->SetPostData(postData);
8994 // Make sure we won't just repost without hitting the
8995 // cache first
8996 if (cacheKey != 0) {
8997 mActiveEntry->SetCacheKey(cacheKey);
9000 // As the document has not changed, the referrer info hasn't changed too,
9001 // so we can just copy it over.
9002 if (referrerInfo) {
9003 mActiveEntry->SetReferrerInfo(referrerInfo);
9006 // Set the title for the SH entry for this target url so that
9007 // SH menus in go/back/forward buttons won't be empty for this.
9008 mActiveEntry->SetTitle(mTitle);
9010 if (scrollRestorationIsManual.isSome()) {
9011 mActiveEntry->SetScrollRestorationIsManual(
9012 scrollRestorationIsManual.value());
9015 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9016 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
9017 } else {
9018 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
9019 // FIXME We should probably just compute mChildOffset in the parent
9020 // instead of passing it over IPC here.
9021 mBrowsingContext->SetActiveSessionHistoryEntry(
9022 Some(scrollPos), mActiveEntry.get(), mLoadType, cacheKey);
9023 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9028 if (locationChangeNeeded) {
9029 FireOnLocationChange(this, nullptr, newURI, locationChangeFlags);
9032 /* Restore the original LSHE if we were loading something
9033 * while same document navigation was initiated.
9035 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
9036 mLoadingEntry.swap(oldLoadingEntry);
9038 /* Set the title for the Global History entry for this anchor url.
9040 UpdateGlobalHistoryTitle(newURI);
9042 SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
9044 // Inform the favicon service that the favicon for oldURI also
9045 // applies to newURI.
9046 CopyFavicon(currentURI, newURI, UsePrivateBrowsing());
9048 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9049 nsCOMPtr<nsPIDOMWindowInner> win =
9050 scriptGlobal ? scriptGlobal->GetCurrentInnerWindow() : nullptr;
9052 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9053 // the function decides whether a scroll is appropriate based on the
9054 // arguments it receives. But even if we don't end up scrolling,
9055 // ScrollToAnchor performs other important tasks, such as informing
9056 // the presShell that we have a new hash. See bug 680257.
9057 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
9058 aState.mNewHash, aLoadState->LoadType());
9059 NS_ENSURE_SUCCESS(rv, rv);
9061 /* restore previous position of scroller(s), if we're moving
9062 * back in history (bug 59774)
9064 nscoord bx = 0;
9065 nscoord by = 0;
9066 bool needsScrollPosUpdate = false;
9067 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
9068 (aLoadState->LoadType() == LOAD_HISTORY ||
9069 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
9070 !scrollRestorationIsManual) {
9071 needsScrollPosUpdate = true;
9072 if (mozilla::SessionHistoryInParent()) {
9073 mActiveEntry->GetScrollPosition(&bx, &by);
9074 } else {
9075 mOSHE->GetScrollPosition(&bx, &by);
9079 // Dispatch the popstate and hashchange events, as appropriate.
9081 // The event dispatch below can cause us to re-enter script and
9082 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9083 // reference to avoid null derefs. See bug 914521.
9084 if (win) {
9085 // Fire a hashchange event URIs differ, and only in their hashes.
9086 bool doHashchange = aState.mSameExceptHashes &&
9087 (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
9088 !aState.mCurrentHash.Equals(aState.mNewHash));
9090 if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
9091 win->DispatchSyncPopState();
9094 if (needsScrollPosUpdate && win->HasActiveDocument()) {
9095 SetCurScrollPosEx(bx, by);
9098 if (doHashchange) {
9099 // Note that currentURI hasn't changed because it's on the
9100 // stack, so we can just use it directly as the old URI.
9101 win->DispatchAsyncHashchange(currentURI, newURI);
9105 return NS_OK;
9108 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
9109 nsDocShellLoadState* aLoadState) {
9110 if (!aLoadState->AllowFocusMove()) {
9111 return false;
9113 if (!aLoadState->HasValidUserGestureActivation()) {
9114 return false;
9116 const auto& sourceBC = aLoadState->SourceBrowsingContext();
9117 if (!sourceBC || !sourceBC->IsActive()) {
9118 // If the navigation didn't come from a foreground tab, then we don't steal
9119 // focus.
9120 return false;
9122 auto* bc = aDocShell->GetBrowsingContext();
9123 if (sourceBC.get() == bc) {
9124 // If it comes from the same tab / frame, don't steal focus either.
9125 return false;
9127 auto* fm = nsFocusManager::GetFocusManager();
9128 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
9129 // If we're already on the foreground tab of the foreground window, then we
9130 // don't need to do this. This helps to e.g. not steal focus from the
9131 // browser chrome unnecessarily.
9132 return false;
9134 if (auto* doc = aDocShell->GetExtantDocument()) {
9135 if (doc->IsInitialDocument()) {
9136 // If we're the initial load for the browsing context, the browser
9137 // chrome determines what to focus. This is important because the
9138 // browser chrome may want to e.g focus the url-bar
9139 return false;
9142 // Take loadDivertedInBackground into account so the behavior would be the
9143 // same as how the tab first opened.
9144 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9147 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
9148 BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) {
9149 MOZ_ASSERT(aLoadState->IsFormSubmission());
9151 // https://html.spec.whatwg.org/#form-submission-algorithm
9152 // 22. Let historyHandling be "push".
9153 // 23. If form document equals targetNavigable's active document, and
9154 // form document has not yet completely loaded, then set
9155 // historyHandling to "replace".
9156 return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument
9157 ? LOAD_NORMAL_REPLACE
9158 : LOAD_LINK;
9161 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
9162 Maybe<uint32_t> aCacheKey) {
9163 MOZ_ASSERT(aLoadState, "need a load state!");
9164 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
9165 "need a valid TriggeringPrincipal");
9167 if (!aLoadState->TriggeringPrincipal()) {
9168 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9169 return NS_ERROR_FAILURE;
9171 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
9172 return NS_ERROR_NOT_AVAILABLE;
9175 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
9177 mOriginalUriString.Truncate();
9179 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9180 ("DOCSHELL %p InternalLoad %s\n", this,
9181 aLoadState->URI()->GetSpecOrDefault().get()));
9183 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
9185 // Cancel loads coming from Docshells that are being destroyed.
9186 if (mIsBeingDestroyed) {
9187 return NS_ERROR_NOT_AVAILABLE;
9190 nsresult rv = EnsureScriptEnvironment();
9191 if (NS_FAILED(rv)) {
9192 return rv;
9195 // If we have a target to move to, do that now.
9196 if (!aLoadState->Target().IsEmpty()) {
9197 return PerformRetargeting(aLoadState);
9200 // This is the non-retargeting load path, we've already set the right loadtype
9201 // for form submissions in nsDocShell::OnLinkClickSync.
9202 if (aLoadState->TargetBrowsingContext().IsNull()) {
9203 aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
9206 MOZ_DIAGNOSTIC_ASSERT(
9207 aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
9208 "Load must be targeting this BrowsingContext");
9210 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
9212 // If we don't have a target, we're loading into ourselves, and our load
9213 // delegate may want to intercept that load.
9214 SameDocumentNavigationState sameDocumentNavigationState;
9215 bool sameDocument =
9216 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
9217 !aLoadState->GetPendingRedirectedChannel();
9219 // Note: We do this check both here and in BrowsingContext::
9220 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9221 // available in the process triggering the load, and we don't want the target
9222 // process to have to trust the triggering process to do the appropriate
9223 // checks for the BrowsingContext's sandbox flags.
9224 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
9226 NS_ENSURE_STATE(!HasUnloadedParent());
9228 rv = CheckLoadingPermissions();
9229 if (NS_FAILED(rv)) {
9230 return rv;
9233 if (mFiredUnloadEvent) {
9234 if (IsOKToLoadURI(aLoadState->URI())) {
9235 MOZ_ASSERT(aLoadState->Target().IsEmpty(),
9236 "Shouldn't have a window target here!");
9238 // If this is a replace load, make whatever load triggered
9239 // the unload event also a replace load, so we don't
9240 // create extra history entries.
9241 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
9242 LOAD_FLAGS_REPLACE_HISTORY)) {
9243 mLoadType = LOAD_NORMAL_REPLACE;
9246 // Do this asynchronously
9247 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
9248 return Dispatch(ev.forget());
9251 // Just ignore this load attempt
9252 return NS_OK;
9255 // If we are loading a URI that should inherit a security context (basically
9256 // javascript: at this point), and the caller has said that principal
9257 // inheritance is allowed, there are a few possible cases:
9259 // 1) We are provided with the principal to inherit. In that case, we just use
9260 // it.
9262 // 2) The load is coming from some other application. In this case we don't
9263 // want to inherit from whatever document we have loaded now, since the
9264 // load is unrelated to it.
9266 // 3) It's a load from our application, but does not provide an explicit
9267 // principal to inherit. In that case, we want to inherit the principal of
9268 // our current document, or of our parent document (if any) if we don't
9269 // have a current document.
9271 bool inherits;
9273 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
9274 !aLoadState->PrincipalToInherit() &&
9275 (aLoadState->HasInternalLoadFlags(
9276 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
9277 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9278 aLoadState->URI(), &inherits)) &&
9279 inherits) {
9280 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
9282 // If principalToInherit is still null (e.g. if some of the conditions of
9283 // were not satisfied), then no inheritance of any sort will happen: the
9284 // load will just get a principal based on the URI being loaded.
9287 // If this docshell is owned by a frameloader, make sure to cancel
9288 // possible frameloader initialization before loading a new page.
9289 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
9290 if (parent) {
9291 RefPtr<Document> doc = parent->GetDocument();
9292 if (doc) {
9293 doc->TryCancelFrameLoaderInitialization(this);
9297 // Before going any further vet loads initiated by external programs.
9298 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9299 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
9301 // Disallow external chrome: loads targetted at content windows
9302 if (SchemeIsChrome(aLoadState->URI())) {
9303 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9304 return NS_ERROR_FAILURE;
9307 // clear the decks to prevent context bleed-through (bug 298255)
9308 rv = CreateAboutBlankDocumentViewer(nullptr, nullptr, nullptr, nullptr,
9309 /* aIsInitialDocument */ false);
9310 if (NS_FAILED(rv)) {
9311 return NS_ERROR_FAILURE;
9315 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
9316 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
9317 mURIResultedInDocument = false; // reset the clock...
9319 // See if this is actually a load between two history entries for the same
9320 // document. If the process fails, or if we successfully navigate within the
9321 // same document, return.
9322 if (sameDocument) {
9323 nsresult rv = HandleSameDocumentNavigation(
9324 aLoadState, sameDocumentNavigationState, sameDocument);
9325 NS_ENSURE_SUCCESS(rv, rv);
9326 if (shouldTakeFocus) {
9327 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9329 if (sameDocument) {
9330 return rv;
9334 // mDocumentViewer->PermitUnload can destroy |this| docShell, which
9335 // causes the next call of CanSavePresentation to crash.
9336 // Hold onto |this| until we return, to prevent a crash from happening.
9337 // (bug#331040)
9338 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9340 // Don't init timing for javascript:, since it generally doesn't
9341 // actually start a load or anything. If it does, we'll init
9342 // timing then, from OnStateChange.
9344 // XXXbz mTiming should know what channel it's for, so we don't
9345 // need this hackery.
9346 bool toBeReset = false;
9347 bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
9349 if (!isJavaScript) {
9350 toBeReset = MaybeInitTiming();
9352 bool isNotDownload = aLoadState->FileName().IsVoid();
9353 if (mTiming && isNotDownload) {
9354 mTiming->NotifyBeforeUnload();
9356 // Check if the page doesn't want to be unloaded. The javascript:
9357 // protocol handler deals with this for javascript: URLs.
9358 if (!isJavaScript && isNotDownload &&
9359 !aLoadState->NotifiedBeforeUnloadListeners() && mDocumentViewer) {
9360 bool okToUnload;
9362 // Check if request is exempted from HTTPSOnlyMode and if https-first is
9363 // enabled, if so it means:
9364 // * https-first failed to upgrade request to https
9365 // * we already asked for permission to unload and the user accepted
9366 // otherwise we wouldn't be here.
9367 bool isPrivateWin = GetOriginAttributes().mPrivateBrowsingId > 0;
9368 bool isHistoryOrReload = false;
9369 uint32_t loadType = aLoadState->LoadType();
9371 // Check if request is a reload.
9372 if (loadType == LOAD_RELOAD_NORMAL ||
9373 loadType == LOAD_RELOAD_BYPASS_CACHE ||
9374 loadType == LOAD_RELOAD_BYPASS_PROXY ||
9375 loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
9376 loadType == LOAD_HISTORY) {
9377 isHistoryOrReload = true;
9380 // If it isn't a reload, the request already failed to be upgraded and
9381 // https-first is enabled then don't ask the user again for permission to
9382 // unload and just unload.
9383 if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSFirstMode() &&
9384 nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
9385 rv = mDocumentViewer->PermitUnload(
9386 nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload,
9387 &okToUnload);
9388 } else {
9389 rv = mDocumentViewer->PermitUnload(&okToUnload);
9392 if (NS_SUCCEEDED(rv) && !okToUnload) {
9393 // The user chose not to unload the page, interrupt the
9394 // load.
9395 MaybeResetInitTiming(toBeReset);
9396 return NS_OK;
9400 if (mTiming && isNotDownload) {
9401 mTiming->NotifyUnloadAccepted(mCurrentURI);
9404 // In e10s, in the parent process, we refuse to load anything other than
9405 // "safe" resources that we ship or trust enough to give "special" URLs.
9406 // Similar check will be performed by the ParentProcessDocumentChannel if in
9407 // use.
9408 if (XRE_IsE10sParentProcess() &&
9409 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
9410 !CanLoadInParentProcess(aLoadState->URI())) {
9411 return NS_ERROR_FAILURE;
9414 // Whenever a top-level browsing context is navigated, the user agent MUST
9415 // lock the orientation of the document to the document's default
9416 // orientation. We don't explicitly check for a top-level browsing context
9417 // here because orientation is only set on top-level browsing contexts.
9418 if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) {
9419 MOZ_ASSERT(mBrowsingContext->IsTop());
9420 MOZ_ALWAYS_SUCCEEDS(
9421 mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None));
9422 if (mBrowsingContext->IsActive()) {
9423 ScreenOrientation::UpdateActiveOrientationLock(
9424 hal::ScreenOrientation::None);
9428 // Check for saving the presentation here, before calling Stop().
9429 // This is necessary so that we can catch any pending requests.
9430 // Since the new request has not been created yet, we pass null for the
9431 // new request parameter.
9432 // Also pass nullptr for the document, since it doesn't affect the return
9433 // value for our purposes here.
9434 bool savePresentation =
9435 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr,
9436 /* aReportBFCacheComboTelemetry */ true);
9438 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9439 // separate check for SHIP so that we know if there are ongoing requests
9440 // before calling Stop() below.
9441 if (mozilla::SessionHistoryInParent()) {
9442 Document* document = GetDocument();
9443 uint32_t flags = 0;
9444 if (document && !document->CanSavePresentation(nullptr, flags, true)) {
9445 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9446 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9447 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9448 // Also, we want to report all the flags to the parent process here (and
9449 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9450 // telemetry data correctly.
9451 document->DisallowBFCaching(flags);
9455 // Don't stop current network activity for javascript: URL's since
9456 // they might not result in any data, and thus nothing should be
9457 // stopped in those cases. In the case where they do result in
9458 // data, the javascript: URL channel takes care of stopping
9459 // current network activity.
9460 if (!isJavaScript && isNotDownload) {
9461 // Stop any current network activity.
9462 // Also stop content if this is a zombie doc. otherwise
9463 // the onload will be delayed by other loads initiated in the
9464 // background by the first document that
9465 // didn't fully load before the next load was initiated.
9466 // If not a zombie, don't stop content until data
9467 // starts arriving from the new URI...
9469 if ((mDocumentViewer && mDocumentViewer->GetPreviousViewer()) ||
9470 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
9471 rv = Stop(nsIWebNavigation::STOP_ALL);
9472 } else {
9473 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9476 if (NS_FAILED(rv)) {
9477 return rv;
9481 mLoadType = aLoadState->LoadType();
9483 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9484 // been called. But when loading an error page, do not clear the
9485 // mLSHE for the real page.
9486 if (mLoadType != LOAD_ERROR_PAGE) {
9487 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
9488 Nothing());
9489 if (aLoadState->LoadIsFromSessionHistory() &&
9490 !mozilla::SessionHistoryInParent()) {
9491 // We're making history navigation or a reload. Make sure our history ID
9492 // points to the same ID as SHEntry's docshell ID.
9493 nsID historyID = {};
9494 aLoadState->SHEntry()->GetDocshellID(historyID);
9496 Unused << mBrowsingContext->SetHistoryID(historyID);
9500 mSavingOldViewer = savePresentation;
9502 // If we have a saved content viewer in history, restore and show it now.
9503 if (aLoadState->LoadIsFromSessionHistory() &&
9504 (mLoadType & LOAD_CMD_HISTORY)) {
9505 // https://html.spec.whatwg.org/#history-traversal:
9506 // To traverse the history
9507 // "If entry has a different Document object than the current entry, then
9508 // run the following substeps: Remove any tasks queued by the history
9509 // traversal task source..."
9510 // Same document object case was handled already above with
9511 // HandleSameDocumentNavigation call.
9512 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
9513 if (shistory) {
9514 shistory->RemovePendingHistoryNavigations();
9516 if (!mozilla::SessionHistoryInParent()) {
9517 // It's possible that the previous viewer of mDocumentViewer is the
9518 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9519 // If that's the case, we need to go ahead and force it into its shentry
9520 // so we can restore it.
9521 if (mDocumentViewer) {
9522 nsCOMPtr<nsIDocumentViewer> prevViewer =
9523 mDocumentViewer->GetPreviousViewer();
9524 if (prevViewer) {
9525 #ifdef DEBUG
9526 nsCOMPtr<nsIDocumentViewer> prevPrevViewer =
9527 prevViewer->GetPreviousViewer();
9528 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9529 #endif
9530 nsCOMPtr<nsISHEntry> viewerEntry;
9531 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9532 if (viewerEntry == aLoadState->SHEntry()) {
9533 // Make sure this viewer ends up in the right place
9534 mDocumentViewer->SetPreviousViewer(nullptr);
9535 prevViewer->Destroy();
9539 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9540 bool restoring;
9541 rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
9542 if (restoring) {
9543 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
9544 return rv;
9546 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
9548 // We failed to restore the presentation, so clean up.
9549 // Both the old and new history entries could potentially be in
9550 // an inconsistent state.
9551 if (NS_FAILED(rv)) {
9552 if (oldEntry) {
9553 oldEntry->SyncPresentationState();
9556 aLoadState->SHEntry()->SyncPresentationState();
9561 bool isTopLevelDoc = mBrowsingContext->IsTopContent();
9563 OriginAttributes attrs = GetOriginAttributes();
9564 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
9566 PredictorLearn(aLoadState->URI(), nullptr,
9567 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
9568 PredictorPredict(aLoadState->URI(), nullptr,
9569 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
9571 nsCOMPtr<nsIRequest> req;
9572 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
9574 if (NS_SUCCEEDED(rv)) {
9575 if (shouldTakeFocus) {
9576 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9580 if (NS_FAILED(rv)) {
9581 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9582 UnblockEmbedderLoadEventForFailure();
9583 nsCOMPtr<nsIURI> uri = aLoadState->URI();
9584 if (DisplayLoadError(rv, uri, nullptr, chan) &&
9585 // FIXME: At this point code was using internal load flags, but checking
9586 // non-internal load flags?
9587 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
9588 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9591 // We won't report any error if this is an unknown protocol error. The
9592 // reason behind this is that it will allow enumeration of external
9593 // protocols if we report an error for each unknown protocol.
9594 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
9595 return NS_OK;
9599 return rv;
9602 /* static */
9603 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
9604 nsCOMPtr<nsIURI> uri = aURI;
9605 // In e10s, in the parent process, we refuse to load anything other than
9606 // "safe" resources that we ship or trust enough to give "special" URLs.
9607 bool canLoadInParent = false;
9608 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9609 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
9610 canLoadInParent) {
9611 // We allow UI resources.
9612 return true;
9614 // For about: and extension-based URIs, which don't get
9615 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9616 while (uri && uri->SchemeIs("view-source")) {
9617 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
9618 if (nested) {
9619 nested->GetInnerURI(getter_AddRefs(uri));
9620 } else {
9621 break;
9624 // Allow about: URIs, and allow moz-extension ones if we're running
9625 // extension content in the parent process.
9626 if (!uri || uri->SchemeIs("about") ||
9627 (!StaticPrefs::extensions_webextensions_remote() &&
9628 uri->SchemeIs("moz-extension"))) {
9629 return true;
9631 #ifdef MOZ_THUNDERBIRD
9632 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
9633 uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
9634 uri->SchemeIs("snews")) {
9635 return true;
9637 #endif
9638 nsAutoCString scheme;
9639 uri->GetScheme(scheme);
9640 // Allow ext+foo URIs (extension-registered custom protocols). See
9641 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9642 if (StringBeginsWith(scheme, "ext+"_ns) &&
9643 !StaticPrefs::extensions_webextensions_remote()) {
9644 return true;
9646 // Final exception for some legacy automated tests:
9647 if (xpc::IsInAutomation() &&
9648 StaticPrefs::security_allow_unsafe_parent_loads()) {
9649 return true;
9651 return false;
9654 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
9655 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
9656 RefPtr<Document> document;
9657 bool inheritedFromCurrent = false;
9659 if (aConsiderCurrentDocument && mDocumentViewer) {
9660 document = mDocumentViewer->GetDocument();
9661 inheritedFromCurrent = true;
9664 if (!document) {
9665 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9666 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9667 if (parentItem) {
9668 document = parentItem->GetDocument();
9672 if (!document) {
9673 if (!aConsiderCurrentDocument) {
9674 return nullptr;
9677 // Make sure we end up with _something_ as the principal no matter
9678 // what.If this fails, we'll just get a null docViewer and bail.
9679 EnsureDocumentViewer();
9680 if (!mDocumentViewer) {
9681 return nullptr;
9683 document = mDocumentViewer->GetDocument();
9686 //-- Get the document's principal
9687 if (document) {
9688 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
9689 ? document->PartitionedPrincipal()
9690 : document->NodePrincipal();
9692 // Don't allow loads in typeContent docShells to inherit the system
9693 // principal from existing documents.
9694 if (inheritedFromCurrent && mItemType == typeContent &&
9695 docPrincipal->IsSystemPrincipal()) {
9696 return nullptr;
9699 return docPrincipal;
9702 return nullptr;
9705 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9706 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9707 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9708 const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9709 nsCOMPtr<nsIChannel> channel;
9710 if (aSrcdoc.IsVoid()) {
9711 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9712 nullptr, // PerformanceStorage
9713 nullptr, // loadGroup
9714 aCallbacks, aLoadFlags));
9716 if (aBaseURI) {
9717 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9718 if (vsc) {
9719 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9722 } else if (SchemeIsViewSource(aURI)) {
9723 // Instantiate view source handler protocol, if it doesn't exist already.
9724 nsCOMPtr<nsIIOService> io(do_GetIOService());
9725 MOZ_ASSERT(io);
9726 nsCOMPtr<nsIProtocolHandler> handler;
9727 nsresult rv =
9728 io->GetProtocolHandler("view-source", getter_AddRefs(handler));
9729 if (NS_FAILED(rv)) {
9730 return rv;
9733 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9734 if (!vsh) {
9735 return NS_ERROR_FAILURE;
9738 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9739 getter_AddRefs(channel)));
9740 } else {
9741 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
9742 aSrcdoc, "text/html"_ns, aLoadInfo,
9743 true));
9744 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9745 MOZ_ASSERT(isc);
9746 isc->SetBaseURI(aBaseURI);
9749 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9750 nsresult rv = channel->SetLoadFlags(aLoadFlags);
9751 NS_ENSURE_SUCCESS(rv, rv);
9754 channel.forget(aChannel);
9755 return NS_OK;
9758 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9759 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9760 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9761 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9762 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9763 nsIChannel** aChannel) {
9764 MOZ_ASSERT(aLoadInfo);
9766 nsString srcdoc = VoidString();
9767 bool isSrcdoc =
9768 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9769 if (isSrcdoc) {
9770 srcdoc = aLoadState->SrcdocData();
9773 aLoadInfo->SetTriggeringRemoteType(
9774 aLoadState->GetEffectiveTriggeringRemoteType());
9776 if (aLoadState->PrincipalToInherit()) {
9777 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9779 aLoadInfo->SetLoadTriggeredFromExternal(
9780 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
9781 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
9782 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9783 aLoadInfo->SetOriginalFrameSrcLoad(
9784 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9786 bool inheritAttrs = false;
9787 if (aLoadState->PrincipalToInherit()) {
9788 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9789 aLoadState->PrincipalToInherit(), aLoadState->URI(),
9790 true, // aInheritForAboutBlank
9791 isSrcdoc);
9794 // Strip the target query parameters before creating the channel.
9795 aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext);
9797 OriginAttributes attrs;
9799 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9800 // true. Otherwise we just use the origin attributes from docshell.
9801 if (inheritAttrs) {
9802 MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9803 "We should have PrincipalToInherit here.");
9804 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9805 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9806 // have the same origin attributes with docshell.
9807 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9808 attrs == aOriginAttributes);
9809 } else {
9810 attrs = aOriginAttributes;
9811 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9812 aLoadState->URI());
9815 aRv = aLoadInfo->SetOriginAttributes(attrs);
9816 if (NS_WARN_IF(NS_FAILED(aRv))) {
9817 return false;
9820 if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9821 aLoadInfo->SetIsFromProcessingFrameAttributes();
9824 // Propagate the IsFormSubmission flag to the loadInfo.
9825 if (aLoadState->IsFormSubmission()) {
9826 aLoadInfo->SetIsFormSubmission(true);
9829 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
9831 nsCOMPtr<nsIChannel> channel;
9832 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9833 aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9834 aLoadState->BaseURI());
9835 NS_ENSURE_SUCCESS(aRv, false);
9837 if (!channel) {
9838 return false;
9841 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9842 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9843 // HTTPS_ONLY_EXEMPT.
9844 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
9846 // hack
9847 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9848 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9849 do_QueryInterface(channel));
9850 nsCOMPtr<nsIURI> referrer;
9851 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9852 if (referrerInfo) {
9853 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9855 if (httpChannelInternal) {
9856 if (aLoadState->HasInternalLoadFlags(
9857 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9858 aRv = httpChannelInternal->SetThirdPartyFlags(
9859 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9860 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9862 if (aLoadState->FirstParty()) {
9863 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9864 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9865 } else {
9866 aRv = httpChannelInternal->SetDocumentURI(referrer);
9867 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9869 aRv = httpChannelInternal->SetRedirectMode(
9870 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9871 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9874 if (httpChannel) {
9875 if (aLoadState->HeadersStream()) {
9876 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9878 // Set the referrer explicitly
9879 // Referrer is currenly only set for link clicks here.
9880 if (referrerInfo) {
9881 aRv = httpChannel->SetReferrerInfo(referrerInfo);
9882 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9885 // Mark the http channel as UrgentStart for top level document loading in
9886 // active tab.
9887 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9888 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9889 if (cos) {
9890 cos->AddClassFlags(nsIClassOfService::UrgentStart);
9895 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
9896 : aLoadState->URI());
9898 const nsACString& typeHint = aLoadState->TypeHint();
9899 if (!typeHint.IsVoid()) {
9900 channel->SetContentType(typeHint);
9903 const nsAString& fileName = aLoadState->FileName();
9904 if (!fileName.IsVoid()) {
9905 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9906 NS_ENSURE_SUCCESS(aRv, false);
9907 if (!fileName.IsEmpty()) {
9908 aRv = channel->SetContentDispositionFilename(fileName);
9909 NS_ENSURE_SUCCESS(aRv, false);
9913 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
9914 nsCOMPtr<nsIURI> referrer;
9915 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9916 if (referrerInfo) {
9917 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9919 // save true referrer for those who need it (e.g. xpinstall whitelisting)
9920 // Currently only http and ftp channels support this.
9921 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
9923 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
9924 props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true);
9928 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
9929 auto loadType = aLoadState->LoadType();
9931 if (loadType == LOAD_RELOAD_NORMAL &&
9932 StaticPrefs::
9933 browser_soft_reload_only_force_validate_top_level_document()) {
9934 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
9935 if (cachingChannel) {
9936 cachingChannel->SetForceValidateCacheContent(true);
9940 // figure out if we need to set the post data stream on the channel...
9941 if (aLoadState->PostDataStream()) {
9942 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
9943 do_QueryInterface(channel)) {
9944 // XXX it's a bit of a hack to rewind the postdata stream here but
9945 // it has to be done in case the post data is being reused multiple
9946 // times.
9947 nsCOMPtr<nsISeekableStream> postDataSeekable =
9948 do_QueryInterface(aLoadState->PostDataStream());
9949 if (postDataSeekable) {
9950 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
9951 NS_ENSURE_SUCCESS(aRv, false);
9954 // we really need to have a content type associated with this stream!!
9955 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
9957 // Ownership of the stream has transferred to the channel, clear our
9958 // reference.
9959 aLoadState->SetPostDataStream(nullptr);
9962 /* If there is a valid postdata *and* it is a History Load,
9963 * set up the cache key on the channel, to retrieve the
9964 * data *only* from the cache. If it is a normal reload, the
9965 * cache is free to go to the server for updated postdata.
9967 if (cacheChannel && aCacheKey != 0) {
9968 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
9969 cacheChannel->SetCacheKey(aCacheKey);
9970 uint32_t loadFlags;
9971 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
9972 channel->SetLoadFlags(loadFlags |
9973 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
9975 } else if (loadType == LOAD_RELOAD_NORMAL) {
9976 cacheChannel->SetCacheKey(aCacheKey);
9979 } else {
9980 /* If there is no postdata, set the cache key on the channel, and
9981 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
9982 * will be free to get it from net if it is not found in cache.
9983 * New cache may use it creatively on CGI pages with GET
9984 * method and even on those that say "no-cache"
9986 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
9987 loadType == LOAD_RELOAD_CHARSET_CHANGE ||
9988 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
9989 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
9990 if (cacheChannel && aCacheKey != 0) {
9991 cacheChannel->SetCacheKey(aCacheKey);
9996 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
9997 // Allow execution against our context if the principals match
9998 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10001 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
10002 timedChannel->SetTimingEnabled(true);
10004 nsString initiatorType;
10005 switch (aLoadInfo->InternalContentPolicyType()) {
10006 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
10007 initiatorType = u"embed"_ns;
10008 break;
10009 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
10010 initiatorType = u"object"_ns;
10011 break;
10012 default: {
10013 const auto& embedderElementType =
10014 aBrowsingContext->GetEmbedderElementType();
10015 if (embedderElementType) {
10016 initiatorType = *embedderElementType;
10018 break;
10022 if (!initiatorType.IsEmpty()) {
10023 timedChannel->SetInitiatorType(initiatorType);
10027 nsCOMPtr<nsIURI> rpURI;
10028 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10029 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
10030 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
10031 if (originalResultPrincipalURI &&
10032 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
10033 // Unconditionally override, we want the replay to be equal to what has
10034 // been captured.
10035 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
10038 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
10039 // The LOAD_REPLACE flag and its handling here will be removed as part
10040 // of bug 1319110. For now preserve its restoration here to not break
10041 // any code expecting it being set specially on redirected channels.
10042 // If the flag has originally been set to change result of
10043 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10044 // any harm.
10045 uint32_t loadFlags;
10046 aRv = channel->GetLoadFlags(&loadFlags);
10047 NS_ENSURE_SUCCESS(aRv, false);
10048 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10051 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10052 if (csp) {
10053 // Navigational requests that are same origin need to be upgraded in case
10054 // upgrade-insecure-requests is present. Please note that for document
10055 // navigations that bit is re-computed in case we encounter a server
10056 // side redirect so the navigation is not same-origin anymore.
10057 bool upgradeInsecureRequests = false;
10058 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10059 if (upgradeInsecureRequests) {
10060 // only upgrade if the navigation is same origin
10061 nsCOMPtr<nsIPrincipal> resultPrincipal;
10062 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10063 channel, getter_AddRefs(resultPrincipal));
10064 NS_ENSURE_SUCCESS(aRv, false);
10065 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10066 aLoadState->TriggeringPrincipal(), resultPrincipal)) {
10067 aLoadInfo->SetUpgradeInsecureRequests(true);
10071 // For document loads we store the CSP that potentially needs to
10072 // be inherited by the new document, e.g. in case we are loading
10073 // an opaque origin like a data: URI. The actual inheritance
10074 // check happens within Document::InitCSP().
10075 // Please create an actual copy of the CSP (do not share the same
10076 // reference) otherwise a Meta CSP of an opaque origin will
10077 // incorrectly be propagated to the embedding document.
10078 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
10079 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
10080 aLoadInfo->SetCSPToInherit(cspToInherit);
10083 channel.forget(aChannel);
10084 return true;
10087 bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
10088 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
10089 return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
10090 (aPrincipalToInherit == GetInheritedPrincipal(false)) &&
10091 (!mDocumentViewer || !mDocumentViewer->GetDocument() ||
10092 mDocumentViewer->GetDocument()->IsInitialDocument());
10095 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
10096 Maybe<uint32_t> aCacheKey,
10097 nsIRequest** aRequest) {
10098 // Double-check that we're still around to load this URI.
10099 if (mIsBeingDestroyed) {
10100 // Return NS_OK despite not doing anything to avoid throwing exceptions
10101 // from nsLocation::SetHref if the unload handler of the existing page
10102 // tears us down.
10103 return NS_OK;
10106 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
10107 if (NS_WARN_IF(!uriLoader)) {
10108 return NS_ERROR_UNEXPECTED;
10111 // Persist and sync layout history state before we load a new uri, as this
10112 // might be our last chance to do so, in the content process.
10113 PersistLayoutHistoryState();
10114 SynchronizeLayoutHistoryState();
10116 nsresult rv;
10117 nsContentPolicyType contentPolicyType = DetermineContentType();
10119 if (IsSubframe()) {
10120 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10121 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10122 "DoURILoad thinks this is a frame and InternalLoad does not");
10124 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10125 // Only allow URLs able to return data in iframes.
10126 if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) {
10127 // The context to check user-interaction with for the purposes of
10128 // popup-blocking.
10130 // We generally want to check the context that initiated the navigation.
10131 WindowContext* sourceWindowContext = [&] {
10132 const MaybeDiscardedBrowsingContext& sourceBC =
10133 aLoadState->SourceBrowsingContext();
10134 if (!sourceBC.IsNullOrDiscarded()) {
10135 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
10136 return wc;
10139 return mBrowsingContext->GetParentWindowContext();
10140 }();
10142 MOZ_ASSERT(sourceWindowContext);
10143 // FIXME: We can't check user-interaction against an OOP window. This is
10144 // the next best thing we can really do. The load state keeps whether
10145 // the navigation had a user interaction in process
10146 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10147 // consume it, which we want to prevent popup-spamming from the same
10148 // click event.
10149 WindowContext* context =
10150 sourceWindowContext->IsInProcess()
10151 ? sourceWindowContext
10152 : mBrowsingContext->GetCurrentWindowContext();
10153 const bool popupBlocked = [&] {
10154 const bool active = mBrowsingContext->IsActive();
10156 // For same-origin-with-top windows, we grant a single free popup
10157 // without user activation, see bug 1680721.
10159 // We consume the flag now even if there's no user activation.
10160 const bool hasFreePass = [&] {
10161 if (!active ||
10162 !(context->IsInProcess() && context->SameOriginWithTop())) {
10163 return false;
10165 nsGlobalWindowInner* win =
10166 context->TopWindowContext()->GetInnerWindow();
10167 return win && win->TryOpenExternalProtocolIframe();
10168 }();
10170 if (context->IsInProcess() &&
10171 context->ConsumeTransientUserGestureActivation()) {
10172 // If the user has interacted with the page, consume it.
10173 return false;
10176 // TODO(emilio): Can we remove this check? It seems like what prompted
10177 // this code (bug 1514547) should be covered by transient user
10178 // activation, see bug 1514547.
10179 if (active &&
10180 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10181 return false;
10184 if (sourceWindowContext->CanShowPopup()) {
10185 return false;
10188 if (hasFreePass) {
10189 return false;
10192 return true;
10193 }();
10195 // No error must be returned when iframes are blocked.
10196 if (popupBlocked) {
10197 nsAutoString message;
10198 nsresult rv = nsContentUtils::GetLocalizedString(
10199 nsContentUtils::eDOM_PROPERTIES,
10200 "ExternalProtocolFrameBlockedNoUserActivation", message);
10201 if (NS_SUCCEEDED(rv)) {
10202 nsContentUtils::ReportToConsoleByWindowID(
10203 message, nsIScriptError::warningFlag, "DOM"_ns,
10204 context->InnerWindowId());
10206 return NS_OK;
10211 // Only allow view-source scheme in top-level docshells. view-source is
10212 // the only scheme to which this applies at the moment due to potential
10213 // timing attacks to read data from cross-origin iframes. If this widens
10214 // we should add a protocol flag for whether the scheme is allowed in
10215 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10216 nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
10217 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10218 while (nestedURI) {
10219 // view-source should always be an nsINestedURI, loop and check the
10220 // scheme on this and all inner URIs that are also nested URIs.
10221 if (SchemeIsViewSource(tempURI)) {
10222 return NS_ERROR_UNKNOWN_PROTOCOL;
10224 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10225 nestedURI = do_QueryInterface(tempURI);
10227 } else {
10228 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10229 "DoURILoad thinks this is a document and InternalLoad does not");
10232 // We want to inherit aLoadState->PrincipalToInherit() when:
10233 // 1. ChannelShouldInheritPrincipal returns true.
10234 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10235 // configured as unique opaque origin.
10236 bool inheritPrincipal = false;
10238 if (aLoadState->PrincipalToInherit()) {
10239 bool isSrcdoc =
10240 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
10241 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10242 aLoadState->PrincipalToInherit(), aLoadState->URI(),
10243 true, // aInheritForAboutBlank
10244 isSrcdoc);
10246 inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
10249 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
10250 const bool isAboutBlankLoadOntoInitialAboutBlank =
10251 IsAboutBlankLoadOntoInitialAboutBlank(aLoadState->URI(), inheritPrincipal,
10252 aLoadState->PrincipalToInherit());
10254 // FIXME We still have a ton of codepaths that don't pass through
10255 // DocumentLoadListener, so probably need to create session history info
10256 // in more places.
10257 if (aLoadState->GetLoadingSessionHistoryInfo()) {
10258 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
10259 } else if (isAboutBlankLoadOntoInitialAboutBlank &&
10260 mozilla::SessionHistoryInParent()) {
10261 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
10262 // loads have it, and later history behavior depends on it existing.
10263 UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
10264 aLoadState->URI(), aLoadState->TriggeringPrincipal(),
10265 aLoadState->PrincipalToInherit(),
10266 aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
10267 mContentTypeHint);
10268 mozilla::dom::LoadingSessionHistoryInfo info(*entry);
10269 SetLoadingSessionHistoryInfo(info, true);
10272 // open a channel for the url
10274 // If we have a pending channel, use the channel we've already created here.
10275 // We don't need to set up load flags for our channel, as it has already been
10276 // created.
10278 if (nsCOMPtr<nsIChannel> channel =
10279 aLoadState->GetPendingRedirectedChannel()) {
10280 // If we have a request outparameter, shove our channel into it.
10281 if (aRequest) {
10282 nsCOMPtr<nsIRequest> outRequest = channel;
10283 outRequest.forget(aRequest);
10286 return OpenRedirectedChannel(aLoadState);
10289 // There are two cases we care about:
10290 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10291 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10292 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10293 // element for the load. loadingPrincipal is the NodePrincipal of the
10294 // frame element.
10295 nsCOMPtr<nsINode> loadingNode;
10296 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10297 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10298 nsCOMPtr<nsISupports> topLevelLoadingContext;
10300 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10301 loadingNode = nullptr;
10302 loadingPrincipal = nullptr;
10303 loadingWindow = mScriptGlobal;
10304 if (XRE_IsContentProcess()) {
10305 // In e10s the child process doesn't have access to the element that
10306 // contains the browsing context (because that element is in the chrome
10307 // process).
10308 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
10309 topLevelLoadingContext = ToSupports(browserChild);
10310 } else {
10311 // This is for loading non-e10s tabs and toplevel windows of various
10312 // sorts.
10313 // For the toplevel window cases, requestingElement will be null.
10314 nsCOMPtr<Element> requestingElement =
10315 loadingWindow->GetFrameElementInternal();
10316 topLevelLoadingContext = requestingElement;
10318 } else {
10319 loadingWindow = nullptr;
10320 loadingNode = mScriptGlobal->GetFrameElementInternal();
10321 if (loadingNode) {
10322 // If we have a loading node, then use that as our loadingPrincipal.
10323 loadingPrincipal = loadingNode->NodePrincipal();
10324 #ifdef DEBUG
10325 // Get the docshell type for requestingElement.
10326 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
10327 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10328 // requestingElement docshell type = current docshell type.
10329 MOZ_ASSERT(
10330 mItemType == elementDocShell->ItemType(),
10331 "subframes should have the same docshell type as their parent");
10332 #endif
10333 } else {
10334 if (mIsBeingDestroyed) {
10335 // If this isn't a top-level load and mScriptGlobal's frame element is
10336 // null, then the element got removed from the DOM while we were trying
10337 // to load this resource. This docshell is scheduled for destruction
10338 // already, so bail out here.
10339 return NS_OK;
10341 // If we are not being destroyed and we do not have access to the loading
10342 // node, then we are a remote subframe. Set the loading principal
10343 // to be a null principal and then set it correctly in the parent.
10344 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
10348 if (!aLoadState->TriggeringPrincipal()) {
10349 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10350 return NS_ERROR_FAILURE;
10353 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
10354 nsSecurityFlags securityFlags =
10355 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
10357 if (mLoadType == LOAD_ERROR_PAGE) {
10358 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10361 if (inheritPrincipal) {
10362 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10365 // Must never have a parent for TYPE_DOCUMENT loads
10366 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10367 !mBrowsingContext->GetParent());
10368 // Subdocuments must have a parent
10369 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
10370 mBrowsingContext->GetParent());
10371 mBrowsingContext->SetTriggeringAndInheritPrincipals(
10372 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
10373 aLoadState->GetLoadIdentifier());
10374 RefPtr<LoadInfo> loadInfo =
10375 (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10376 ? new LoadInfo(loadingWindow, aLoadState->URI(),
10377 aLoadState->TriggeringPrincipal(),
10378 topLevelLoadingContext, securityFlags, sandboxFlags)
10379 : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
10380 loadingNode, securityFlags, contentPolicyType,
10381 Maybe<mozilla::dom::ClientInfo>(),
10382 Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
10383 sandboxFlags);
10384 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
10386 if (isAboutBlankLoadOntoInitialAboutBlank) {
10387 // Match the DocumentChannel case where the default for third-partiness
10388 // differs from the default in LoadInfo construction here.
10389 // toolkit/components/antitracking/test/browser/browser_aboutblank.js
10390 // fails without this.
10391 BrowsingContext* top = mBrowsingContext->Top();
10392 if (top == mBrowsingContext) {
10393 // If we're at the top, this must be a window.open()ed
10394 // window, and we can't be third-party relative to ourselves.
10395 loadInfo->SetIsThirdPartyContextToTopWindow(false);
10396 } else {
10397 if (Document* topDoc = top->GetDocument()) {
10398 bool thirdParty = false;
10399 mozilla::Unused << topDoc->GetPrincipal()->IsThirdPartyPrincipal(
10400 aLoadState->PrincipalToInherit(), &thirdParty);
10401 loadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
10402 } else {
10403 // If top is in a different process, we have to be third-party relative
10404 // to it.
10405 loadInfo->SetIsThirdPartyContextToTopWindow(true);
10410 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess()) {
10411 if (context->HasValidTransientUserGestureActivation()) {
10412 aLoadState->SetHasValidUserGestureActivation(true);
10414 if (!aLoadState->TriggeringWindowId()) {
10415 aLoadState->SetTriggeringWindowId(context->Id());
10417 if (!aLoadState->TriggeringStorageAccess()) {
10418 Document* contextDoc = context->GetExtantDoc();
10419 if (contextDoc) {
10420 aLoadState->SetTriggeringStorageAccess(
10421 contextDoc->UsingStorageAccess());
10426 // in case this docshell load was triggered by a valid transient user gesture,
10427 // or also the load originates from external, then we pass that information on
10428 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10429 if (aLoadState->HasValidUserGestureActivation() ||
10430 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
10431 loadInfo->SetHasValidUserGestureActivation(true);
10434 loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
10435 loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
10436 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
10437 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
10439 uint32_t cacheKey = 0;
10440 if (aCacheKey) {
10441 cacheKey = *aCacheKey;
10442 } else if (mozilla::SessionHistoryInParent()) {
10443 if (mLoadingEntry) {
10444 cacheKey = mLoadingEntry->mInfo.GetCacheKey();
10445 } else if (mActiveEntry) { // for reload cases
10446 cacheKey = mActiveEntry->GetCacheKey();
10448 } else {
10449 if (mLSHE) {
10450 cacheKey = mLSHE->GetCacheKey();
10451 } else if (mOSHE) { // for reload cases
10452 cacheKey = mOSHE->GetCacheKey();
10456 bool uriModified;
10457 if (mLSHE || mLoadingEntry) {
10458 if (mLoadingEntry) {
10459 uriModified = mLoadingEntry->mInfo.GetURIWasModified();
10460 } else {
10461 uriModified = mLSHE->GetURIWasModified();
10463 } else {
10464 uriModified = false;
10467 bool isEmbeddingBlockedError = false;
10468 if (mFailedChannel) {
10469 nsresult status;
10470 mFailedChannel->GetStatus(&status);
10471 isEmbeddingBlockedError = status == NS_ERROR_XFO_VIOLATION ||
10472 status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION;
10475 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
10476 mBrowsingContext, Some(uriModified), Some(isEmbeddingBlockedError));
10478 nsCOMPtr<nsIChannel> channel;
10479 if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
10480 !isAboutBlankLoadOntoInitialAboutBlank) {
10481 channel = DocumentChannel::CreateForDocument(
10482 aLoadState, loadInfo, loadFlags, this, cacheKey, uriModified,
10483 isEmbeddingBlockedError);
10484 MOZ_ASSERT(channel);
10486 // Disable keyword fixup when using DocumentChannel, since
10487 // DocumentLoadListener will handle this for us (in the parent process).
10488 mAllowKeywordFixup = false;
10489 } else if (!CreateAndConfigureRealChannelForLoadState(
10490 mBrowsingContext, aLoadState, loadInfo, this, this,
10491 GetOriginAttributes(), loadFlags, cacheKey, rv,
10492 getter_AddRefs(channel))) {
10493 return rv;
10496 // Make sure to give the caller a channel if we managed to create one
10497 // This is important for correct error page/session history interaction
10498 if (aRequest) {
10499 NS_ADDREF(*aRequest = channel);
10502 const nsACString& typeHint = aLoadState->TypeHint();
10503 if (!typeHint.IsVoid()) {
10504 mContentTypeHint = typeHint;
10505 } else {
10506 mContentTypeHint.Truncate();
10509 // Load attributes depend on load type...
10510 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10511 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10512 // only want to force cache load for this channel, not the whole
10513 // loadGroup.
10514 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10515 if (cachingChannel) {
10516 cachingChannel->SetAllowStaleCacheContent(true);
10520 uint32_t openFlags =
10521 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
10522 return OpenInitializedChannel(channel, uriLoader, openFlags);
10525 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10526 const char* aFromRawSegment,
10527 uint32_t aToOffset, uint32_t aCount,
10528 uint32_t* aWriteCount) {
10529 // aFromSegment now contains aCount bytes of data.
10531 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10532 buf->Append(aFromRawSegment, aCount);
10534 // Indicate that we have consumed all of aFromSegment
10535 *aWriteCount = aCount;
10536 return NS_OK;
10539 /* static */ nsresult nsDocShell::AddHeadersToChannel(
10540 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
10541 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10542 NS_ENSURE_STATE(httpChannel);
10544 uint32_t numRead;
10545 nsAutoCString headersString;
10546 nsresult rv = aHeadersData->ReadSegments(
10547 AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10548 NS_ENSURE_SUCCESS(rv, rv);
10550 // used during the manipulation of the String from the InputStream
10551 nsAutoCString headerName;
10552 nsAutoCString headerValue;
10553 int32_t crlf;
10554 int32_t colon;
10557 // Iterate over the headersString: for each "\r\n" delimited chunk,
10558 // add the value as a header to the nsIHttpChannel
10561 static const char kWhitespace[] = "\b\t\r\n ";
10562 while (true) {
10563 crlf = headersString.Find("\r\n");
10564 if (crlf == kNotFound) {
10565 return NS_OK;
10568 const nsACString& oneHeader = StringHead(headersString, crlf);
10570 colon = oneHeader.FindChar(':');
10571 if (colon == kNotFound) {
10572 return NS_ERROR_UNEXPECTED;
10575 headerName = StringHead(oneHeader, colon);
10576 headerValue = Substring(oneHeader, colon + 1);
10578 headerName.Trim(kWhitespace);
10579 headerValue.Trim(kWhitespace);
10581 headersString.Cut(0, crlf + 2);
10584 // FINALLY: we can set the header!
10587 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10588 NS_ENSURE_SUCCESS(rv, rv);
10591 MOZ_ASSERT_UNREACHABLE("oops");
10592 return NS_ERROR_UNEXPECTED;
10595 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10596 BrowsingContext* aBrowsingContext, uint32_t aLoadType,
10597 bool aIsDocumentLoad) {
10598 MOZ_ASSERT(aBrowsingContext);
10600 uint32_t openFlags = 0;
10601 if (aLoadType == LOAD_LINK) {
10602 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10604 if (!aBrowsingContext->GetAllowContentRetargeting()) {
10605 openFlags |= nsIURILoader::DONT_RETARGET;
10608 // Unless the pref is set, object/embed loads always specify DONT_RETARGET.
10609 // See bug 1868001 for details.
10610 if (!aIsDocumentLoad &&
10611 !StaticPrefs::dom_navigation_object_embed_allow_retargeting()) {
10612 openFlags |= nsIURILoader::DONT_RETARGET;
10615 return openFlags;
10618 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
10619 nsIURILoader* aURILoader,
10620 uint32_t aOpenFlags) {
10621 nsresult rv = NS_OK;
10623 // If anything fails here, make sure to clear our initial ClientSource.
10624 auto cleanupInitialClient =
10625 MakeScopeExit([&] { mInitialClientSource.reset(); });
10627 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10628 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10630 MaybeCreateInitialClientSource();
10632 // Let the client channel helper know if we are using DocumentChannel,
10633 // since redirects get handled in the parent process in that case.
10634 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
10635 if (docChannel && XRE_IsContentProcess()) {
10636 // Tell the content process nsDocumentOpenInfo to not try to do
10637 // any sort of targeting.
10638 aOpenFlags |= nsIURILoader::DONT_RETARGET;
10641 // Since we are loading a document we need to make sure the proper reserved
10642 // and initial client data is stored on the nsILoadInfo. The
10643 // ClientChannelHelper does this and ensures that it is propagated properly
10644 // on redirects. We pass no reserved client here so that the helper will
10645 // create the reserved ClientSource if necessary.
10646 Maybe<ClientInfo> noReservedClient;
10647 if (docChannel) {
10648 // When using DocumentChannel, all redirect handling is done in the parent,
10649 // so we just need the child variant to watch for the internal redirect
10650 // to the final channel.
10651 rv = AddClientChannelHelperInChild(aChannel,
10652 GetMainThreadSerialEventTarget());
10653 docChannel->SetInitialClientInfo(GetInitialClientInfo());
10654 } else {
10655 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
10656 GetInitialClientInfo(),
10657 GetMainThreadSerialEventTarget());
10659 NS_ENSURE_SUCCESS(rv, rv);
10661 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
10662 NS_ENSURE_SUCCESS(rv, rv);
10664 // We're about to load a new page and it may take time before necko
10665 // gives back any data, so main thread might have a chance to process a
10666 // collector slice
10667 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
10669 // Success. Keep the initial ClientSource if it exists.
10670 cleanupInitialClient.release();
10672 return NS_OK;
10675 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
10676 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
10677 MOZ_ASSERT(channel);
10679 // If anything fails here, make sure to clear our initial ClientSource.
10680 auto cleanupInitialClient =
10681 MakeScopeExit([&] { mInitialClientSource.reset(); });
10683 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10684 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10686 MaybeCreateInitialClientSource();
10688 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
10690 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
10691 if (loadInfo->GetExternalContentPolicyType() ==
10692 ExtContentPolicy::TYPE_DOCUMENT) {
10693 li->UpdateBrowsingContextID(mBrowsingContext->Id());
10694 } else if (loadInfo->GetExternalContentPolicyType() ==
10695 ExtContentPolicy::TYPE_SUBDOCUMENT) {
10696 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
10699 // If we did a process switch, then we should have an existing allocated
10700 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10701 CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget());
10703 RefPtr<nsDocumentOpenInfo> loader =
10704 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10705 channel->SetLoadGroup(mLoadGroup);
10707 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10709 nsresult rv = NS_OK;
10710 if (XRE_IsParentProcess()) {
10711 // If we're in the parent, the we don't have an nsIChildChannel, just
10712 // the original channel, which is already open in this process.
10714 // DocumentLoadListener expects to get an nsIParentChannel, so
10715 // we create a wrapper around the channel and nsIStreamListener
10716 // that forwards functionality as needed, and then we register
10717 // it under the provided identifier.
10718 RefPtr<ParentChannelWrapper> wrapper =
10719 new ParentChannelWrapper(channel, loader);
10720 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10722 mLoadGroup->AddRequest(channel, nullptr);
10723 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10724 do_QueryInterface(channel)) {
10725 // Our channel was redirected from another process, so doesn't need to
10726 // be opened again. However, it does need its listener hooked up
10727 // correctly.
10728 rv = childChannel->CompleteRedirectSetup(loader);
10729 } else {
10730 // It's possible for the redirected channel to not implement
10731 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10732 // can just open the local instance and it will work.
10733 rv = channel->AsyncOpen(loader);
10735 if (rv == NS_ERROR_NO_CONTENT) {
10736 return NS_OK;
10738 NS_ENSURE_SUCCESS(rv, rv);
10740 // Success. Keep the initial ClientSource if it exists.
10741 cleanupInitialClient.release();
10742 return NS_OK;
10745 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
10746 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10747 nsACString& aNewHash, uint32_t aLoadType) {
10748 if (!mCurrentURI) {
10749 return NS_OK;
10752 RefPtr<PresShell> presShell = GetPresShell();
10753 if (!presShell) {
10754 // If we failed to get the shell, or if there is no shell,
10755 // nothing left to do here.
10756 return NS_OK;
10759 nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
10760 if (rootScroll) {
10761 rootScroll->ClearDidHistoryRestore();
10764 // If we have no new anchor, we do not want to scroll, unless there is a
10765 // current anchor and we are doing a history load. So return if we have no
10766 // new anchor, and there is no current anchor or the load is not a history
10767 // load.
10768 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10769 return NS_OK;
10772 // Both the new and current URIs refer to the same page. We can now
10773 // browse to the hash stored in the new URI.
10775 // If it's a load from history, we don't have any anchor jumping to do.
10776 // Scrollbar position will be restored by the caller based on positions stored
10777 // in session history.
10778 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10780 if (aNewHash.IsEmpty()) {
10781 // 2. If fragment is the empty string, then return the special value top of
10782 // the document.
10784 // Tell the shell it's at an anchor without scrolling.
10785 presShell->GoToAnchor(u""_ns, false);
10787 if (scroll) {
10788 // Scroll to the top of the page. Ignore the return value; failure to
10789 // scroll here (e.g. if there is no root scrollframe) is not grounds for
10790 // canceling the load!
10791 SetCurScrollPosEx(0, 0);
10794 return NS_OK;
10797 // 3. Let potentialIndicatedElement be the result of finding a potential
10798 // indicated element given document and fragment.
10799 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10800 auto rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
10802 // 4. If potentialIndicatedElement is not null, then return
10803 // potentialIndicatedElement.
10804 if (NS_SUCCEEDED(rv)) {
10805 return NS_OK;
10808 // 5. Let fragmentBytes be the result of percent-decoding fragment.
10809 nsAutoCString fragmentBytes;
10810 const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(),
10811 /* aFlags = */ 0, fragmentBytes);
10813 if (!unescaped) {
10814 // Another attempt is only necessary if characters were unescaped.
10815 return NS_OK;
10818 if (fragmentBytes.IsEmpty()) {
10819 // When aNewHash contains "%00", the unescaped string may be empty, and
10820 // GoToAnchor asserts if we ask it to scroll to an empty ref.
10821 presShell->GoToAnchor(u""_ns, false);
10822 return NS_OK;
10825 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
10826 // fragmentBytes.
10827 nsAutoString decodedFragment;
10828 rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
10829 NS_ENSURE_SUCCESS(rv, rv);
10831 // 7. Set potentialIndicatedElement to the result of finding a potential
10832 // indicated element given document and decodedFragment.
10834 // Ignore the return value of GoToAnchor, since it will return an error if
10835 // there is no such anchor in the document, which is actually a success
10836 // condition for us (we want to update the session history with the new URI no
10837 // matter whether we actually scrolled somewhere).
10838 presShell->GoToAnchor(decodedFragment, scroll, ScrollFlags::ScrollSmoothAuto);
10840 return NS_OK;
10843 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10844 nsIPrincipal* aTriggeringPrincipal,
10845 nsIPrincipal* aPrincipalToInherit,
10846 nsIPrincipal* aPartitionedPrincipalToInherit,
10847 nsIContentSecurityPolicy* aCsp,
10848 bool aAddToGlobalHistory, bool aCloneSHChildren) {
10849 MOZ_ASSERT(aURI, "uri is null");
10850 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10852 MOZ_ASSERT(!aPrincipalToInherit ||
10853 (aPrincipalToInherit && aTriggeringPrincipal));
10855 #if defined(DEBUG)
10856 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10857 nsAutoCString chanName;
10858 if (aChannel) {
10859 aChannel->GetName(chanName);
10860 } else {
10861 chanName.AssignLiteral("<no channel>");
10864 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10865 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10866 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10868 #endif
10870 bool equalUri = false;
10872 // Get the post data and the HTTP response code from the channel.
10873 uint32_t responseStatus = 0;
10874 nsCOMPtr<nsIInputStream> inputStream;
10875 if (aChannel) {
10876 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10878 // Check if the HTTPChannel is hiding under a multiPartChannel
10879 if (!httpChannel) {
10880 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10883 if (httpChannel) {
10884 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10885 if (uploadChannel) {
10886 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10889 // If the response status indicates an error, unlink this session
10890 // history entry from any entries sharing its document.
10891 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10892 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10893 mLSHE->AbandonBFCacheEntry();
10894 // FIXME Do the same for mLoadingEntry
10899 // Determine if this type of load should update history.
10900 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
10902 // We don't update session history on reload unless we're loading
10903 // an iframe in shift-reload case.
10904 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
10906 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10907 // root browsing context.
10908 // FIXME If session history in the parent is enabled then we only do this if
10909 // the session history object is in process, otherwise we can't really
10910 // use the mLSHE anyway. Once session history is only stored in the
10911 // parent then this code will probably be removed anyway.
10912 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
10913 if (!rootSH) {
10914 updateSHistory = false;
10915 updateGHistory = false; // XXX Why global history too?
10918 // Check if the url to be loaded is the same as the one already loaded.
10919 if (mCurrentURI) {
10920 aURI->Equals(mCurrentURI, &equalUri);
10923 #ifdef DEBUG
10924 bool shAvailable = (rootSH != nullptr);
10926 // XXX This log message is almost useless because |updateSHistory|
10927 // and |updateGHistory| are not correct at this point.
10929 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10930 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10931 " equalURI=%i\n",
10932 shAvailable, updateSHistory, updateGHistory, equalUri));
10933 #endif
10935 /* If the url to be loaded is the same as the one already there,
10936 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10937 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10938 * AddToSessionHistory() won't mess with the current SHEntry and
10939 * if this page has any frame children, it also will be handled
10940 * properly. see bug 83684
10942 * NB: If mOSHE is null but we have a current URI, then it probably
10943 * means that we must be at the transient about:blank content viewer;
10944 * we should let the normal load continue, since there's nothing to
10945 * replace. Sometimes this happens after a session restore (eg process
10946 * switch) and mCurrentURI is not about:blank; we assume we can let the load
10947 * continue (Bug 1301399).
10949 * XXX Hopefully changing the loadType at this time will not hurt
10950 * anywhere. The other way to take care of sequentially repeating
10951 * frameset pages is to add new methods to nsIDocShellTreeItem.
10952 * Hopefully I don't have to do that.
10954 if (equalUri &&
10955 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
10956 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
10957 mLoadType == LOAD_STOP_CONTENT) &&
10958 !inputStream) {
10959 mLoadType = LOAD_NORMAL_REPLACE;
10962 // If this is a refresh to the currently loaded url, we don't
10963 // have to update session or global history.
10964 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10965 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
10968 /* If the user pressed shift-reload, cache will create a new cache key
10969 * for the page. Save the new cacheKey in Session History.
10970 * see bug 90098
10972 if (aChannel && IsForceReloadType(mLoadType)) {
10973 MOZ_ASSERT(!updateSHistory || IsSubframe(),
10974 "We shouldn't be updating session history for forced"
10975 " reloads unless we're in a newly created iframe!");
10977 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
10978 uint32_t cacheKey = 0;
10979 // Get the Cache Key and store it in SH.
10980 if (cacheChannel) {
10981 cacheChannel->GetCacheKey(&cacheKey);
10983 // If we already have a loading history entry, store the new cache key
10984 // in it. Otherwise, since we're doing a reload and won't be updating
10985 // our history entry, store the cache key in our current history entry.
10986 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
10988 if (!mozilla::SessionHistoryInParent()) {
10989 // Since we're force-reloading, clear all the sub frame history.
10990 ClearFrameHistory(mLSHE);
10991 ClearFrameHistory(mOSHE);
10995 if (!mozilla::SessionHistoryInParent()) {
10996 // Clear subframe history on refresh.
10997 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
10998 // this case. One should re-validate after bug 1331865 fixed.
10999 if (mLoadType == LOAD_REFRESH) {
11000 ClearFrameHistory(mLSHE);
11001 ClearFrameHistory(mOSHE);
11004 if (updateSHistory) {
11005 // Update session history if necessary...
11006 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11007 /* This is a fresh page getting loaded for the first time
11008 *.Create a Entry for it and add it to SH, if this is the
11009 * rootDocShell
11011 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11012 aPrincipalToInherit,
11013 aPartitionedPrincipalToInherit, aCsp,
11014 aCloneSHChildren, getter_AddRefs(mLSHE));
11016 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11017 // Even if we don't add anything to SHistory, ensure the current index
11018 // points to the same SHEntry as our mLSHE.
11020 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11021 mLSHE);
11025 // If this is a POST request, we do not want to include this in global
11026 // history.
11027 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11028 !net::ChannelIsPost(aChannel)) {
11029 nsCOMPtr<nsIURI> previousURI;
11030 uint32_t previousFlags = 0;
11032 if (mLoadType & LOAD_CMD_RELOAD) {
11033 // On a reload request, we don't set redirecting flags.
11034 previousURI = aURI;
11035 } else {
11036 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11039 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11042 // If this was a history load or a refresh, or it was a history load but
11043 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11044 // in session history.
11045 if (!mozilla::SessionHistoryInParent() && rootSH &&
11046 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11047 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11048 mPreviousEntryIndex = rootSH->Index();
11049 if (!mozilla::SessionHistoryInParent()) {
11050 rootSH->LegacySHistory()->UpdateIndex();
11052 mLoadedEntryIndex = rootSH->Index();
11053 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11054 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11055 mLoadedEntryIndex));
11058 // aCloneSHChildren exactly means "we are not loading a new document".
11059 uint32_t locationFlags =
11060 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11062 bool onLocationChangeNeeded =
11063 SetCurrentURI(aURI, aChannel, false,
11064 /* aIsInitialAboutBlank */ false, locationFlags);
11065 // Make sure to store the referrer from the channel, if any
11066 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11067 if (httpChannel) {
11068 mReferrerInfo = httpChannel->GetReferrerInfo();
11070 return onLocationChangeNeeded;
11073 Maybe<Wireframe> nsDocShell::GetWireframe() {
11074 const bool collectWireFrame =
11075 mozilla::SessionHistoryInParent() &&
11076 StaticPrefs::browser_history_collectWireframes() &&
11077 mBrowsingContext->IsTopContent() && mActiveEntry;
11079 if (!collectWireFrame) {
11080 return Nothing();
11083 RefPtr<Document> doc = mDocumentViewer->GetDocument();
11084 Nullable<Wireframe> wireframe;
11085 doc->GetWireframeWithoutFlushing(false, wireframe);
11086 if (wireframe.IsNull()) {
11087 return Nothing();
11089 return Some(wireframe.Value());
11092 bool nsDocShell::CollectWireframe() {
11093 Maybe<Wireframe> wireframe = GetWireframe();
11094 if (wireframe.isNothing()) {
11095 return false;
11098 if (XRE_IsParentProcess()) {
11099 SessionHistoryEntry* entry =
11100 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11101 if (entry) {
11102 entry->SetWireframe(wireframe);
11104 } else {
11105 mozilla::Unused
11106 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11107 mBrowsingContext, wireframe.ref());
11110 return true;
11113 //*****************************************************************************
11114 // nsDocShell: Session History
11115 //*****************************************************************************
11117 NS_IMETHODIMP
11118 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11119 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11120 MOZ_LOG(gSHLog, LogLevel::Debug,
11121 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11122 NS_ConvertUTF16toUTF8(aTitle).get(),
11123 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11124 // Implements History.pushState and History.replaceState
11126 // Here's what we do, roughly in the order specified by HTML5. The specific
11127 // steps we are executing are at
11128 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11129 // and
11130 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11131 // This function basically implements #dom-history-pushstate and
11132 // UpdateURLAndHistory implements #url-and-history-update-steps.
11134 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11135 // step 5.
11136 // B. If the third argument is present, #dom-history-pushstate step 7.
11137 // 7.1. Resolve the url, relative to our document.
11138 // 7.2. If (a) fails, raise a SECURITY_ERR
11139 // 7.4. Compare the resulting absolute URL to the document's address. If
11140 // any part of the URLs difer other than the <path>, <query>, and
11141 // <fragment> components, raise a SECURITY_ERR and abort.
11142 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11143 // Remove from the session history all entries after the current entry,
11144 // as we would after a regular navigation, and save the current
11145 // entry's scroll position (bug 590573).
11146 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11147 // either add a state object entry to the session history after the
11148 // current entry with the following properties, or modify the current
11149 // session history entry to set
11150 // a. cloned data as the state object,
11151 // b. if the third argument was present, the absolute URL found in
11152 // step 2
11153 // Also clear the new history entry's POST data (see bug 580069).
11154 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11155 // replaceState), notify bfcache that we've navigated to a new page.
11156 // F. If the third argument is present, set the document's current address
11157 // to the absolute URL found in step B. This is
11158 // #url-and-history-update-steps step 4.
11160 // It's important that this function not run arbitrary scripts after step A
11161 // and before completing step E. For example, if a script called
11162 // history.back() before we completed step E, bfcache might destroy an
11163 // active content viewer. Since EvictOutOfRangeDocumentViewers at the end of
11164 // step E might run script, we can't just put a script blocker around the
11165 // critical section.
11167 // Note that we completely ignore the aTitle parameter.
11169 nsresult rv;
11171 // Don't clobber the load type of an existing network load.
11172 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11174 // pushState effectively becomes replaceState when we've started a network
11175 // load but haven't adopted its document yet. This mirrors what we do with
11176 // changes to the hash at this stage of the game.
11177 if (JustStartedNetworkLoad()) {
11178 aReplace = true;
11181 RefPtr<Document> document = GetDocument();
11182 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11184 // Step A: Serialize aData using structured clone.
11185 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11186 // step 5.
11187 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11189 // scContainer->Init might cause arbitrary JS to run, and this code might
11190 // navigate the page we're on, potentially to a different origin! (bug
11191 // 634834) To protect against this, we abort if our principal changes due
11192 // to the InitFromJSVal() call.
11194 RefPtr<Document> origDocument = GetDocument();
11195 if (!origDocument) {
11196 return NS_ERROR_DOM_SECURITY_ERR;
11198 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11200 scContainer = new nsStructuredCloneContainer();
11201 rv = scContainer->InitFromJSVal(aData, aCx);
11202 NS_ENSURE_SUCCESS(rv, rv);
11204 RefPtr<Document> newDocument = GetDocument();
11205 if (!newDocument) {
11206 return NS_ERROR_DOM_SECURITY_ERR;
11208 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11210 bool principalsEqual = false;
11211 origPrincipal->Equals(newPrincipal, &principalsEqual);
11212 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11215 // Check that the state object isn't too long.
11216 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11217 if (maxStateObjSize < 0) {
11218 maxStateObjSize = 0;
11221 uint64_t scSize;
11222 rv = scContainer->GetSerializedNBytes(&scSize);
11223 NS_ENSURE_SUCCESS(rv, rv);
11225 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11227 // Step B: Resolve aURL.
11228 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11229 // step 7.
11230 bool equalURIs = true;
11231 nsCOMPtr<nsIURI> currentURI;
11232 if (mCurrentURI) {
11233 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11234 } else {
11235 currentURI = mCurrentURI;
11237 nsCOMPtr<nsIURI> newURI;
11238 if (aURL.Length() == 0) {
11239 newURI = currentURI;
11240 } else {
11241 // 7.1: Resolve aURL relative to mURI
11243 nsIURI* docBaseURI = document->GetDocBaseURI();
11244 if (!docBaseURI) {
11245 return NS_ERROR_FAILURE;
11248 nsAutoCString spec;
11249 docBaseURI->GetSpec(spec);
11251 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11252 document->GetDocumentCharacterSet(), docBaseURI);
11254 // 7.2: If 2a fails, raise a SECURITY_ERR
11255 if (NS_FAILED(rv)) {
11256 return NS_ERROR_DOM_SECURITY_ERR;
11259 // 7.4 and 7.5: Same-origin check.
11260 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11261 // In addition to checking that the security manager says that
11262 // the new URI has the same origin as our current URI, we also
11263 // check that the two URIs have the same userpass. (The
11264 // security manager says that |http://foo.com| and
11265 // |http://me@foo.com| have the same origin.) currentURI
11266 // won't contain the password part of the userpass, so this
11267 // means that it's never valid to specify a password in a
11268 // pushState or replaceState URI.
11270 nsCOMPtr<nsIScriptSecurityManager> secMan =
11271 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11272 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11274 // It's very important that we check that newURI is of the same
11275 // origin as currentURI, not docBaseURI, because a page can
11276 // set docBaseURI arbitrarily to any domain.
11277 nsAutoCString currentUserPass, newUserPass;
11278 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11279 NS_ERROR_FAILURE);
11280 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11281 bool isPrivateWin =
11282 document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
11284 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11285 isPrivateWin)) ||
11286 !currentUserPass.Equals(newUserPass)) {
11287 return NS_ERROR_DOM_SECURITY_ERR;
11289 } else {
11290 // It's a file:// URI
11291 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11293 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11294 newURI, false, document->InnerWindowID()))) {
11295 return NS_ERROR_DOM_SECURITY_ERR;
11299 if (currentURI) {
11300 currentURI->Equals(newURI, &equalURIs);
11301 } else {
11302 equalURIs = false;
11305 } // end of same-origin check
11307 // Step 8: call "URL and history update steps"
11308 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11309 currentURI, equalURIs);
11310 NS_ENSURE_SUCCESS(rv, rv);
11312 return NS_OK;
11315 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11316 nsIStructuredCloneContainer* aData,
11317 const nsAString& aTitle, bool aReplace,
11318 nsIURI* aCurrentURI, bool aEqualURIs) {
11319 // Implements
11320 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11322 // If we have a pending title change, handle it before creating a new entry.
11323 aDocument->DoNotifyPossibleTitleChange();
11325 // Step 2, if aReplace is false: Create a new entry in the session
11326 // history. This will erase all SHEntries after the new entry and make this
11327 // entry the current one. This operation may modify mOSHE, which we need
11328 // later, so we keep a reference here.
11329 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11330 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11332 // If this push/replaceState changed the document's current URI and the new
11333 // URI differs from the old URI in more than the hash, or if the old
11334 // SHEntry's URI was modified in this way by a push/replaceState call
11335 // set URIWasModified to true for the current SHEntry (bug 669671).
11336 bool sameExceptHashes = true;
11337 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11338 bool uriWasModified;
11339 if (sameExceptHashes) {
11340 if (mozilla::SessionHistoryInParent()) {
11341 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11342 } else {
11343 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11345 } else {
11346 uriWasModified = true;
11349 mLoadType = LOAD_PUSHSTATE;
11351 nsCOMPtr<nsISHEntry> newSHEntry;
11352 if (!aReplace) {
11353 // Step 2.
11355 // Step 2.2, "Remove any tasks queued by the history traversal task
11356 // source that are associated with any Document objects in the
11357 // top-level browsing context's document family." This is very hard in
11358 // SessionHistoryInParent since we can't synchronously access the
11359 // pending navigations that are already sent to the parent. We can
11360 // abort any AsyncGo navigations that are waiting to be sent. If we
11361 // send a message to the parent, it would be processed after any
11362 // navigations previously sent. So long as we consider the "history
11363 // traversal task source" to be the list in this process we match the
11364 // spec. If we move the entire list to the parent, we can handle the
11365 // aborting of loads there, but we don't have a way to synchronously
11366 // remove entries as we do here for non-SHIP.
11367 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11368 if (shistory) {
11369 shistory->RemovePendingHistoryNavigations();
11372 nsPoint scrollPos = GetCurScrollPos();
11374 bool scrollRestorationIsManual;
11375 if (mozilla::SessionHistoryInParent()) {
11376 // FIXME Need to save the current scroll position on mActiveEntry.
11377 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11378 } else {
11379 // Save the current scroll position (bug 590573). Step 2.3.
11380 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11382 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11385 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11387 if (mozilla::SessionHistoryInParent()) {
11388 MOZ_LOG(gSHLog, LogLevel::Debug,
11389 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11391 nsString title(mActiveEntry->GetTitle());
11392 nsCOMPtr<nsIReferrerInfo> referrerInfo = mActiveEntry->GetReferrerInfo();
11394 UpdateActiveEntry(false,
11395 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11396 /* aOriginalURI = */ nullptr,
11397 /* aReferrerInfo = */ referrerInfo,
11398 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11399 csp, title, scrollRestorationIsManual, aData,
11400 uriWasModified);
11401 } else {
11402 // Since we're not changing which page we have loaded, pass
11403 // true for aCloneChildren.
11404 nsresult rv = AddToSessionHistory(
11405 aNewURI, nullptr,
11406 aDocument->NodePrincipal(), // triggeringPrincipal
11407 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11408 NS_ENSURE_SUCCESS(rv, rv);
11410 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11412 // Session history entries created by pushState inherit scroll restoration
11413 // mode from the current entry.
11414 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11416 // Set the new SHEntry's title (bug 655273).
11417 nsString title;
11418 mOSHE->GetTitle(title);
11419 newSHEntry->SetTitle(title);
11421 nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo();
11422 newSHEntry->SetReferrerInfo(referrerInfo);
11424 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11425 // two entries correspond to the same document.
11426 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11427 NS_ERROR_FAILURE);
11429 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11430 // we'll just set mOSHE here.
11431 mOSHE = newSHEntry;
11433 } else if (mozilla::SessionHistoryInParent()) {
11434 MOZ_LOG(gSHLog, LogLevel::Debug,
11435 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11436 this, mActiveEntry.get()));
11437 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11438 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11439 // in our case. We could also set it to aNewURI, with the same result.
11440 // We don't use aTitle here, see bug 544535.
11441 nsString title;
11442 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11443 if (mActiveEntry) {
11444 title = mActiveEntry->GetTitle();
11445 referrerInfo = mActiveEntry->GetReferrerInfo();
11446 } else {
11447 referrerInfo = nullptr;
11449 UpdateActiveEntry(
11450 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11451 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
11452 aDocument->GetCsp(), title,
11453 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11454 uriWasModified);
11455 } else {
11456 // Step 3.
11457 newSHEntry = mOSHE;
11459 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11460 // Since we're not changing which page we have loaded, pass
11461 // true for aCloneChildren.
11462 if (!newSHEntry) {
11463 nsresult rv = AddToSessionHistory(
11464 aNewURI, nullptr,
11465 aDocument->NodePrincipal(), // triggeringPrincipal
11466 nullptr, nullptr, aDocument->GetCsp(), true,
11467 getter_AddRefs(newSHEntry));
11468 NS_ENSURE_SUCCESS(rv, rv);
11469 mOSHE = newSHEntry;
11472 nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo();
11474 newSHEntry->SetURI(aNewURI);
11475 newSHEntry->SetOriginalURI(aNewURI);
11476 // We replaced the URI of the entry, clear the unstripped URI as it
11477 // shouldn't be used for reloads anymore.
11478 newSHEntry->SetUnstrippedURI(nullptr);
11479 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11480 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11481 // in our case. We could also set it to aNewURI, with the same result.
11482 newSHEntry->SetResultPrincipalURI(nullptr);
11483 newSHEntry->SetLoadReplace(false);
11484 newSHEntry->SetReferrerInfo(referrerInfo);
11487 if (!mozilla::SessionHistoryInParent()) {
11488 // Step 2.4 and 3: Modify new/original session history entry and clear its
11489 // POST data, if there is any.
11490 newSHEntry->SetStateData(aData);
11491 newSHEntry->SetPostData(nullptr);
11493 newSHEntry->SetURIWasModified(uriWasModified);
11495 // Step E as described at the top of AddState: If aReplace is false,
11496 // indicating that we're doing a pushState rather than a replaceState,
11497 // notify bfcache that we've added a page to the history so it can evict
11498 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11499 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11500 // root session history if this call is coming from a document.open() in a
11501 // docshell subtree that disables session history.
11502 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11503 if (rootSH) {
11504 rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry,
11505 aReplace);
11509 // Step 4: If the document's URI changed, update document's URI and update
11510 // global history.
11512 // We need to call FireOnLocationChange so that the browser's address bar
11513 // gets updated and the back button is enabled, but we only need to
11514 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11515 // since SetCurrentURI will call FireOnLocationChange for us.
11517 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11518 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11519 // notification is allowed only when we know docshell is not loading a new
11520 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11521 // FireOnLocationChange(...) breaks security UI.
11523 // If the docshell is shutting down, don't update the document URI, as we
11524 // can't load into a docshell that is being destroyed.
11525 if (!aEqualURIs && !mIsBeingDestroyed) {
11526 aDocument->SetDocumentURI(aNewURI);
11527 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11528 /* aIsInitialAboutBlank */ false,
11529 GetSameDocumentNavigationFlags(aNewURI));
11531 AddURIVisit(aNewURI, aCurrentURI, 0);
11533 // AddURIVisit doesn't set the title for the new URI in global history,
11534 // so do that here.
11535 UpdateGlobalHistoryTitle(aNewURI);
11537 // Inform the favicon service that our old favicon applies to this new
11538 // URI.
11539 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11540 } else {
11541 FireDummyOnLocationChange();
11543 aDocument->SetStateObject(aData);
11545 return NS_OK;
11548 NS_IMETHODIMP
11549 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11550 if (mozilla::SessionHistoryInParent()) {
11551 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11552 return NS_OK;
11555 *aIsManual = false;
11556 if (mOSHE) {
11557 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11560 return NS_OK;
11563 NS_IMETHODIMP
11564 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11565 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11567 return NS_OK;
11570 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11571 nsISHEntry* aSHEntry, bool aIsManual) {
11572 if (aSHEntry) {
11573 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11576 if (mActiveEntry && mBrowsingContext) {
11577 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11578 if (XRE_IsParentProcess()) {
11579 SessionHistoryEntry* entry =
11580 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11581 if (entry) {
11582 entry->SetScrollRestorationIsManual(aIsManual);
11584 } else {
11585 mozilla::Unused << ContentChild::GetSingleton()
11586 ->SendSessionHistoryEntryScrollRestorationIsManual(
11587 mBrowsingContext, aIsManual);
11592 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11593 uint32_t aCacheKey) {
11594 if (aSHEntry) {
11595 aSHEntry->SetCacheKey(aCacheKey);
11598 if (mActiveEntry && mBrowsingContext) {
11599 mActiveEntry->SetCacheKey(aCacheKey);
11600 if (XRE_IsParentProcess()) {
11601 SessionHistoryEntry* entry =
11602 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11603 if (entry) {
11604 entry->SetCacheKey(aCacheKey);
11606 } else {
11607 mozilla::Unused
11608 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11609 mBrowsingContext, aCacheKey);
11614 /* static */
11615 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11616 // I believe none of the about: urls should go in the history. But then
11617 // that could just be me... If the intent is only deny about:blank then we
11618 // should just do a spec compare, rather than two gets of the scheme and
11619 // then the path. -Gagan
11620 nsresult rv;
11621 nsAutoCString buf;
11623 rv = aURI->GetScheme(buf);
11624 if (NS_FAILED(rv)) {
11625 return false;
11628 if (buf.EqualsLiteral("about")) {
11629 rv = aURI->GetPathQueryRef(buf);
11630 if (NS_FAILED(rv)) {
11631 return false;
11634 if (buf.EqualsLiteral("blank")) {
11635 return false;
11637 // We only want to add about:newtab if it's not privileged, and
11638 // if it is not configured to show the blank page.
11639 if (buf.EqualsLiteral("newtab")) {
11640 if (!StaticPrefs::browser_newtabpage_enabled()) {
11641 return false;
11644 NS_ENSURE_TRUE(aChannel, false);
11645 nsCOMPtr<nsIPrincipal> resultPrincipal;
11646 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11647 aChannel, getter_AddRefs(resultPrincipal));
11648 NS_ENSURE_SUCCESS(rv, false);
11649 return !resultPrincipal->IsSystemPrincipal();
11653 return true;
11656 nsresult nsDocShell::AddToSessionHistory(
11657 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11658 nsIPrincipal* aPrincipalToInherit,
11659 nsIPrincipal* aPartitionedPrincipalToInherit,
11660 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11661 nsISHEntry** aNewEntry) {
11662 MOZ_ASSERT(aURI, "uri is null");
11663 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11664 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11666 #if defined(DEBUG)
11667 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11668 nsAutoCString chanName;
11669 if (aChannel) {
11670 aChannel->GetName(chanName);
11671 } else {
11672 chanName.AssignLiteral("<no channel>");
11675 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11676 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11677 aURI->GetSpecOrDefault().get(), chanName.get()));
11679 #endif
11681 nsresult rv = NS_OK;
11682 nsCOMPtr<nsISHEntry> entry;
11685 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11686 * the existing SH entry in the page and replace the url and
11687 * other vitalities.
11689 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11690 !mBrowsingContext->IsTop()) {
11691 // This is a subframe
11692 entry = mOSHE;
11693 if (entry) {
11694 entry->ClearEntry();
11698 // Create a new entry if necessary.
11699 if (!entry) {
11700 entry = new nsSHEntry();
11703 // Get the post data & referrer
11704 nsCOMPtr<nsIInputStream> inputStream;
11705 nsCOMPtr<nsIURI> originalURI;
11706 nsCOMPtr<nsIURI> resultPrincipalURI;
11707 nsCOMPtr<nsIURI> unstrippedURI;
11708 bool loadReplace = false;
11709 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11710 uint32_t cacheKey = 0;
11711 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11712 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11713 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11714 aPartitionedPrincipalToInherit;
11715 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11716 bool expired = false; // by default the page is not expired
11717 bool discardLayoutState = false;
11718 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11719 bool userActivation = false;
11721 if (aChannel) {
11722 cacheChannel = do_QueryInterface(aChannel);
11724 /* If there is a caching channel, get the Cache Key and store it
11725 * in SH.
11727 if (cacheChannel) {
11728 cacheChannel->GetCacheKey(&cacheKey);
11730 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11732 // Check if the httpChannel is hiding under a multipartChannel
11733 if (!httpChannel) {
11734 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11736 if (httpChannel) {
11737 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11738 if (uploadChannel) {
11739 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11741 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11742 uint32_t loadFlags;
11743 aChannel->GetLoadFlags(&loadFlags);
11744 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11745 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11746 MOZ_ASSERT(NS_SUCCEEDED(rv));
11748 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11751 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11752 if (!triggeringPrincipal) {
11753 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11755 if (!csp) {
11756 csp = loadInfo->GetCspToInherit();
11759 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11761 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
11763 userActivation = loadInfo->GetHasValidUserGestureActivation();
11765 // For now keep storing just the principal in the SHEntry.
11766 if (!principalToInherit) {
11767 if (loadInfo->GetLoadingSandboxed()) {
11768 if (loadInfo->GetLoadingPrincipal()) {
11769 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11770 loadInfo->GetLoadingPrincipal());
11771 } else {
11772 // get the OriginAttributes
11773 OriginAttributes attrs;
11774 loadInfo->GetOriginAttributes(&attrs);
11775 principalToInherit = NullPrincipal::Create(attrs);
11777 } else {
11778 principalToInherit = loadInfo->PrincipalToInherit();
11782 if (!partitionedPrincipalToInherit) {
11783 // XXXehsan is it correct to fall back to the principal to inherit in all
11784 // cases? For example, what about the cases where we are using the load
11785 // info's principal to inherit? Do we need to add a similar concept to
11786 // load info for partitioned principal?
11787 partitionedPrincipalToInherit = principalToInherit;
11791 nsAutoString srcdoc;
11792 bool srcdocEntry = false;
11793 nsCOMPtr<nsIURI> baseURI;
11795 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11796 if (inStrmChan) {
11797 bool isSrcdocChannel;
11798 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11799 if (isSrcdocChannel) {
11800 inStrmChan->GetSrcdocData(srcdoc);
11801 srcdocEntry = true;
11802 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11803 } else {
11804 srcdoc.SetIsVoid(true);
11807 /* If cache got a 'no-store', ask SH not to store
11808 * HistoryLayoutState. By default, SH will set this
11809 * flag to true and save HistoryLayoutState.
11811 bool saveLayoutState = !discardLayoutState;
11813 if (cacheChannel) {
11814 // Check if the page has expired from cache
11815 uint32_t expTime = 0;
11816 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11817 uint32_t now = PRTimeToSeconds(PR_Now());
11818 if (expTime <= now) {
11819 expired = true;
11823 // Title is set in nsDocShell::SetTitle()
11824 entry->Create(aURI, // uri
11825 u""_ns, // Title
11826 inputStream, // Post data stream
11827 cacheKey, // CacheKey
11828 mContentTypeHint, // Content-type
11829 triggeringPrincipal, // Channel or provided principal
11830 principalToInherit, partitionedPrincipalToInherit, csp,
11831 HistoryID(), GetCreatedDynamically(), originalURI,
11832 resultPrincipalURI, unstrippedURI, loadReplace, referrerInfo,
11833 srcdoc, srcdocEntry, baseURI, saveLayoutState, expired,
11834 userActivation);
11836 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11837 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11838 Maybe<int32_t> previousEntryIndex;
11839 Maybe<int32_t> loadedEntryIndex;
11840 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11841 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11842 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11844 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11845 if (previousEntryIndex.isSome()) {
11846 mPreviousEntryIndex = previousEntryIndex.value();
11848 if (loadedEntryIndex.isSome()) {
11849 mLoadedEntryIndex = loadedEntryIndex.value();
11852 // aCloneChildren implies that we are retaining the same document, thus we
11853 // need to signal to the top WC that the new SHEntry may receive a fresh
11854 // user interaction flag.
11855 if (aCloneChildren) {
11856 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11857 if (topWc && !topWc->IsDiscarded()) {
11858 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11861 } else {
11862 // This is a subframe, make sure that this new SHEntry will be
11863 // marked with user interaction.
11864 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11865 if (topWc && !topWc->IsDiscarded()) {
11866 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11868 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11869 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11870 aCloneChildren);
11874 // Return the new SH entry...
11875 if (aNewEntry) {
11876 *aNewEntry = nullptr;
11877 if (NS_SUCCEEDED(rv)) {
11878 entry.forget(aNewEntry);
11882 return rv;
11885 void nsDocShell::UpdateActiveEntry(
11886 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11887 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
11888 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
11889 const nsAString& aTitle, bool aScrollRestorationIsManual,
11890 nsIStructuredCloneContainer* aData, bool aURIWasModified) {
11891 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11892 MOZ_ASSERT(aURI, "uri is null");
11893 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
11894 "This code only deals with pushState");
11895 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
11897 MOZ_LOG(gSHLog, LogLevel::Debug,
11898 ("Creating an active entry on nsDocShell %p to %s", this,
11899 aURI->GetSpecOrDefault().get()));
11901 // Even if we're replacing an existing entry we create new a
11902 // SessionHistoryInfo. In the parent process we'll keep the existing
11903 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11904 // entry keeps identity but its data is replaced.
11905 bool replace = aReplace && mActiveEntry;
11907 if (!replace) {
11908 CollectWireframe();
11911 if (mActiveEntry) {
11912 // Link this entry to the previous active entry.
11913 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
11914 } else {
11915 mActiveEntry = MakeUnique<SessionHistoryInfo>(
11916 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
11918 mActiveEntry->SetOriginalURI(aOriginalURI);
11919 mActiveEntry->SetUnstrippedURI(nullptr);
11920 mActiveEntry->SetReferrerInfo(aReferrerInfo);
11921 mActiveEntry->SetTitle(aTitle);
11922 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
11923 mActiveEntry->SetURIWasModified(aURIWasModified);
11924 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
11926 if (replace) {
11927 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
11928 } else {
11929 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
11930 // FIXME We should probably just compute mChildOffset in the parent
11931 // instead of passing it over IPC here.
11932 mBrowsingContext->SetActiveSessionHistoryEntry(
11933 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
11934 /* aCacheKey = */ 0);
11935 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
11939 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
11940 bool aUserActivation) {
11941 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11943 nsresult rv;
11944 RefPtr<nsDocShellLoadState> loadState;
11945 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
11946 NS_ENSURE_SUCCESS(rv, rv);
11948 // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if
11949 // that's the only thing holding a ref to aEntry that will cause aEntry to
11950 // die while we're loading it. So hold a strong ref to aEntry here, just
11951 // in case.
11952 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11954 loadState->SetHasValidUserGestureActivation(
11955 loadState->HasValidUserGestureActivation() || aUserActivation);
11957 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
11960 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
11961 uint32_t aLoadType,
11962 bool aUserActivation) {
11963 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
11964 loadState->SetHasValidUserGestureActivation(
11965 loadState->HasValidUserGestureActivation() || aUserActivation);
11967 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
11970 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
11971 uint32_t aLoadType,
11972 bool aLoadingCurrentEntry) {
11973 if (!IsNavigationAllowed()) {
11974 return NS_OK;
11977 // We are setting load type afterwards so we don't have to
11978 // send it in an IPC message
11979 aLoadState->SetLoadType(aLoadType);
11981 nsresult rv;
11982 if (SchemeIsJavascript(aLoadState->URI())) {
11983 // We're loading a URL that will execute script from inside asyncOpen.
11984 // Replace the current document with about:blank now to prevent
11985 // anything from the current document from leaking into any JavaScript
11986 // code in the URL.
11987 // Don't cache the presentation if we're going to just reload the
11988 // current entry. Caching would lead to trying to save the different
11989 // content viewers in the same nsISHEntry object.
11990 rv = CreateAboutBlankDocumentViewer(
11991 aLoadState->PrincipalToInherit(),
11992 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
11993 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
11995 if (NS_FAILED(rv)) {
11996 // The creation of the intermittent about:blank content
11997 // viewer failed for some reason (potentially because the
11998 // user prevented it). Interrupt the history load.
11999 return NS_OK;
12002 if (!aLoadState->TriggeringPrincipal()) {
12003 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
12004 // URIs will pick it up from the about:blank page we just loaded,
12005 // and we don't really want even that in this case.
12006 nsCOMPtr<nsIPrincipal> principal =
12007 NullPrincipal::Create(GetOriginAttributes());
12008 aLoadState->SetTriggeringPrincipal(principal);
12012 /* If there is a valid postdata *and* the user pressed
12013 * reload or shift-reload, take user's permission before we
12014 * repost the data to the server.
12016 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
12017 bool repost;
12018 rv = ConfirmRepost(&repost);
12019 if (NS_FAILED(rv)) {
12020 return rv;
12023 // If the user pressed cancel in the dialog, return. We're done here.
12024 if (!repost) {
12025 return NS_BINDING_ABORTED;
12029 // If there is no valid triggeringPrincipal, we deny the load
12030 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
12031 "need a valid triggeringPrincipal to load from history");
12032 if (!aLoadState->TriggeringPrincipal()) {
12033 return NS_ERROR_FAILURE;
12036 return InternalLoad(aLoadState); // No nsIRequest
12039 NS_IMETHODIMP
12040 nsDocShell::PersistLayoutHistoryState() {
12041 nsresult rv = NS_OK;
12043 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12044 bool scrollRestorationIsManual;
12045 if (mozilla::SessionHistoryInParent()) {
12046 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12047 } else {
12048 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12050 nsCOMPtr<nsILayoutHistoryState> layoutState;
12051 if (RefPtr<PresShell> presShell = GetPresShell()) {
12052 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12053 } else if (scrollRestorationIsManual) {
12054 // Even if we don't have layout anymore, we may want to reset the
12055 // current scroll state in layout history.
12056 GetLayoutHistoryState(getter_AddRefs(layoutState));
12059 if (scrollRestorationIsManual && layoutState) {
12060 layoutState->ResetScrollState();
12064 return rv;
12067 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12068 nsISHEntry* aNewEntry) {
12069 if (aOldEntry == mOSHE) {
12070 mOSHE = aNewEntry;
12073 if (aOldEntry == mLSHE) {
12074 mLSHE = aNewEntry;
12078 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12079 const Maybe<nsISHEntry*>& aOSHE) {
12080 // We want to hold on to the reference in mLSHE before we update it.
12081 // Otherwise, SetHistoryEntry could release the last reference to
12082 // the entry while aOSHE is pointing to it.
12083 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12084 if (aLSHE.isSome()) {
12085 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12086 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12088 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12089 if (aOSHE.isSome()) {
12090 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12091 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12095 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12096 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12097 // We need to sync up the docshell and session history trees for
12098 // subframe navigation. If the load was in a subframe, we forward up to
12099 // the root docshell, which will then recursively sync up all docshells
12100 // to their corresponding entries in the new session history tree.
12101 // If we don't do this, then we can cache a content viewer on the wrong
12102 // cloned entry, and subsequently restore it at the wrong time.
12103 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12104 if (topBC->IsDiscarded()) {
12105 topBC = nullptr;
12107 RefPtr<BrowsingContext> currBC =
12108 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12109 if (topBC && *aPtr) {
12110 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12112 nsCOMPtr<nsISHEntry> entry(aEntry);
12113 entry.swap(*aPtr);
12114 return entry.forget();
12117 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12118 RefPtr<ChildSHistory> childSHistory =
12119 mBrowsingContext->Top()->GetChildSessionHistory();
12120 return childSHistory.forget();
12123 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12124 nsIHttpChannel** aReturn) {
12125 NS_ENSURE_ARG_POINTER(aReturn);
12126 if (!aChannel) {
12127 return NS_ERROR_FAILURE;
12130 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12131 if (multiPartChannel) {
12132 nsCOMPtr<nsIChannel> baseChannel;
12133 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12134 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12135 *aReturn = httpChannel;
12136 NS_IF_ADDREF(*aReturn);
12138 return NS_OK;
12141 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12142 // By default layout State will be saved.
12143 if (!aChannel) {
12144 return false;
12147 // figure out if SH should be saving layout state
12148 bool noStore = false;
12149 Unused << aChannel->IsNoStoreResponse(&noStore);
12150 return noStore;
12153 NS_IMETHODIMP
12154 nsDocShell::GetEditor(nsIEditor** aEditor) {
12155 NS_ENSURE_ARG_POINTER(aEditor);
12156 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12157 htmlEditor.forget(aEditor);
12158 return NS_OK;
12161 NS_IMETHODIMP
12162 nsDocShell::SetEditor(nsIEditor* aEditor) {
12163 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12164 // If TextEditor comes, throw an error.
12165 if (aEditor && !htmlEditor) {
12166 return NS_ERROR_INVALID_ARG;
12168 return SetHTMLEditorInternal(htmlEditor);
12171 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12172 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12175 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12176 if (!aHTMLEditor && !mEditorData) {
12177 return NS_OK;
12180 nsresult rv = EnsureEditorData();
12181 if (NS_FAILED(rv)) {
12182 return rv;
12185 return mEditorData->SetHTMLEditor(aHTMLEditor);
12188 NS_IMETHODIMP
12189 nsDocShell::GetEditable(bool* aEditable) {
12190 NS_ENSURE_ARG_POINTER(aEditable);
12191 *aEditable = mEditorData && mEditorData->GetEditable();
12192 return NS_OK;
12195 NS_IMETHODIMP
12196 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12197 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12199 if (mEditorData) {
12200 *aHasEditingSession = !!mEditorData->GetEditingSession();
12201 } else {
12202 *aHasEditingSession = false;
12205 return NS_OK;
12208 NS_IMETHODIMP
12209 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12210 nsresult rv = EnsureEditorData();
12211 if (NS_FAILED(rv)) {
12212 return rv;
12215 return mEditorData->MakeEditable(aInWaitForUriLoad);
12218 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12219 bool needToAddURIVisit = true;
12220 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12221 if (props) {
12222 mozilla::Unused << props->GetPropertyAsBool(
12223 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12226 return needToAddURIVisit;
12229 /* static */ void nsDocShell::ExtractLastVisit(
12230 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12231 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12232 if (!props) {
12233 return;
12236 nsresult rv;
12237 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12238 if (NS_SUCCEEDED(rv)) {
12239 uri.forget(aURI);
12241 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12242 aChannelRedirectFlags);
12244 NS_WARNING_ASSERTION(
12245 NS_SUCCEEDED(rv),
12246 "Could not fetch previous flags, URI will be treated like referrer");
12248 } else {
12249 // There is no last visit for this channel, so this must be the first
12250 // link. Link the visit to the referrer of this request, if any.
12251 // Treat referrer as null if there is an error getting it.
12252 NS_GetReferrerFromChannel(aChannel, aURI);
12256 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12257 uint32_t aChannelRedirectFlags) {
12258 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12259 if (!props || !aURI) {
12260 return;
12263 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12264 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12265 aChannelRedirectFlags);
12268 /* static */ void nsDocShell::InternalAddURIVisit(
12269 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12270 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12271 nsIWidget* aWidget, uint32_t aLoadType, bool aWasUpgraded) {
12272 MOZ_ASSERT(aURI, "Visited URI is null!");
12273 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12274 "Do not add error or bypass pages to global history");
12276 bool usePrivateBrowsing = false;
12277 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12279 // Only content-type docshells save URI visits. Also don't do
12280 // anything here if we're not supposed to use global history.
12281 if (!aBrowsingContext->IsContent() ||
12282 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12283 return;
12286 nsCOMPtr<IHistory> history = components::History::Service();
12288 if (history) {
12289 uint32_t visitURIFlags = 0;
12291 if (aBrowsingContext->IsTop()) {
12292 visitURIFlags |= IHistory::TOP_LEVEL;
12295 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12296 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12297 } else if (aChannelRedirectFlags &
12298 nsIChannelEventSink::REDIRECT_PERMANENT) {
12299 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12300 } else {
12301 MOZ_ASSERT(!aChannelRedirectFlags,
12302 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12303 "if any flags in aChannelRedirectFlags is set.");
12306 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12307 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12308 if (aResponseStatus == 301 || aResponseStatus == 308) {
12309 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12312 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12313 // simple retry attempt by the user is unlikely to solve them.
12314 // 408 is special cased, since may actually indicate a temporary
12315 // connection problem.
12316 else if (aResponseStatus != 408 &&
12317 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12318 aResponseStatus == 505)) {
12319 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12322 if (aWasUpgraded) {
12323 visitURIFlags |=
12324 IHistory::REDIRECT_SOURCE | IHistory::REDIRECT_SOURCE_UPGRADED;
12327 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12328 visitURIFlags,
12329 aBrowsingContext->BrowserId());
12333 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12334 uint32_t aChannelRedirectFlags,
12335 uint32_t aResponseStatus) {
12336 nsPIDOMWindowOuter* outer = GetWindow();
12337 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12339 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12340 aResponseStatus, mBrowsingContext, widget, mLoadType,
12341 false);
12344 //*****************************************************************************
12345 // nsDocShell: Helper Routines
12346 //*****************************************************************************
12348 NS_IMETHODIMP
12349 nsDocShell::SetLoadType(uint32_t aLoadType) {
12350 mLoadType = aLoadType;
12351 return NS_OK;
12354 NS_IMETHODIMP
12355 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12356 *aLoadType = mLoadType;
12357 return NS_OK;
12360 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12361 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12362 *aRepost = true;
12363 return NS_OK;
12366 nsCOMPtr<nsIPromptCollection> prompter =
12367 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12368 if (!prompter) {
12369 return NS_ERROR_NOT_AVAILABLE;
12372 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12375 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12376 nsIStringBundle** aStringBundle) {
12377 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12378 NS_ERROR_FAILURE);
12380 nsCOMPtr<nsIStringBundleService> stringBundleService =
12381 mozilla::components::StringBundle::Service();
12382 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12384 NS_ENSURE_SUCCESS(
12385 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12386 NS_ERROR_FAILURE);
12388 return NS_OK;
12391 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12392 PresShell* presShell = GetPresShell();
12393 NS_ENSURE_TRUE(presShell, nullptr);
12395 return presShell->GetRootScrollFrameAsScrollable();
12398 nsresult nsDocShell::EnsureScriptEnvironment() {
12399 if (mScriptGlobal) {
12400 return NS_OK;
12403 if (mIsBeingDestroyed) {
12404 return NS_ERROR_NOT_AVAILABLE;
12407 #ifdef DEBUG
12408 NS_ASSERTION(!mInEnsureScriptEnv,
12409 "Infinite loop! Calling EnsureScriptEnvironment() from "
12410 "within EnsureScriptEnvironment()!");
12412 // Yeah, this isn't re-entrant safe, but that's ok since if we
12413 // re-enter this method, we'll infinitely loop...
12414 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12415 mInEnsureScriptEnv = true;
12416 #endif
12418 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12419 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12421 uint32_t chromeFlags;
12422 browserChrome->GetChromeFlags(&chromeFlags);
12424 // If our window is modal and we're not opened as chrome, make
12425 // this window a modal content window.
12426 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12427 MOZ_ASSERT(mScriptGlobal);
12429 // Ensure the script object is set up to run script.
12430 return mScriptGlobal->EnsureScriptEnvironment();
12433 nsresult nsDocShell::EnsureEditorData() {
12434 MOZ_ASSERT(!mIsBeingDestroyed);
12436 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12437 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12438 // We shouldn't recreate the editor data if it already exists, or
12439 // we're shutting down, or we already have a detached editor data
12440 // stored in the session history. We should only have one editordata
12441 // per docshell.
12442 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12445 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12448 nsresult nsDocShell::EnsureFind() {
12449 if (!mFind) {
12450 mFind = new nsWebBrowserFind();
12453 // we promise that the nsIWebBrowserFind that we return has been set
12454 // up to point to the focused, or content window, so we have to
12455 // set that up each time.
12457 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12458 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12460 // default to our window
12461 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12462 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12463 nsFocusManager::GetFocusedDescendant(ourWindow,
12464 nsFocusManager::eIncludeAllDescendants,
12465 getter_AddRefs(windowToSearch));
12467 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12468 if (!findInFrames) {
12469 return NS_ERROR_NO_INTERFACE;
12472 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12473 if (NS_FAILED(rv)) {
12474 return rv;
12476 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12477 if (NS_FAILED(rv)) {
12478 return rv;
12481 return NS_OK;
12484 NS_IMETHODIMP
12485 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12486 NS_ENSURE_ARG(aDoomed);
12487 *aDoomed = mIsBeingDestroyed;
12488 return NS_OK;
12491 NS_IMETHODIMP
12492 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12493 NS_ENSURE_ARG(aResult);
12494 *aResult = mIsExecutingOnLoadHandler;
12495 return NS_OK;
12498 NS_IMETHODIMP
12499 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12500 nsCOMPtr<nsILayoutHistoryState> state;
12501 if (mozilla::SessionHistoryInParent()) {
12502 if (mActiveEntry) {
12503 state = mActiveEntry->GetLayoutHistoryState();
12505 } else {
12506 if (mOSHE) {
12507 state = mOSHE->GetLayoutHistoryState();
12510 state.forget(aLayoutHistoryState);
12511 return NS_OK;
12514 NS_IMETHODIMP
12515 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12516 if (mOSHE) {
12517 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12519 if (mActiveEntry) {
12520 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12522 return NS_OK;
12525 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12526 nsIInterfaceRequestor* aRequestor) {
12527 if (aRequestor) {
12528 mWeakPtr = do_GetWeakReference(aRequestor);
12532 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12533 mWeakPtr = nullptr;
12536 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12538 NS_IMETHODIMP
12539 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12540 void** aSink) {
12541 NS_ENSURE_ARG_POINTER(aSink);
12542 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12543 if (ifReq) {
12544 return ifReq->GetInterface(aIID, aSink);
12546 *aSink = nullptr;
12547 return NS_NOINTERFACE;
12550 //*****************************************************************************
12551 // nsDocShell::nsIAuthPromptProvider
12552 //*****************************************************************************
12554 NS_IMETHODIMP
12555 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12556 void** aResult) {
12557 // a priority prompt request will override a false mAllowAuth setting
12558 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12560 if (!mAllowAuth && !priorityPrompt) {
12561 return NS_ERROR_NOT_AVAILABLE;
12564 // we're either allowing auth, or it's a proxy request
12565 nsresult rv;
12566 nsCOMPtr<nsIPromptFactory> wwatch =
12567 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12568 NS_ENSURE_SUCCESS(rv, rv);
12570 rv = EnsureScriptEnvironment();
12571 NS_ENSURE_SUCCESS(rv, rv);
12573 // Get the an auth prompter for our window so that the parenting
12574 // of the dialogs works as it should when using tabs.
12576 return wwatch->GetPrompt(mScriptGlobal, aIID,
12577 reinterpret_cast<void**>(aResult));
12580 //*****************************************************************************
12581 // nsDocShell::nsILoadContext
12582 //*****************************************************************************
12584 NS_IMETHODIMP
12585 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12586 CallGetInterface(this, aWindow);
12587 return NS_OK;
12590 NS_IMETHODIMP
12591 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12592 return mBrowsingContext->GetTopWindow(aWindow);
12595 NS_IMETHODIMP
12596 nsDocShell::GetTopFrameElement(Element** aElement) {
12597 return mBrowsingContext->GetTopFrameElement(aElement);
12600 NS_IMETHODIMP
12601 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12602 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12605 NS_IMETHODIMP
12606 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12607 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12610 NS_IMETHODIMP
12611 nsDocShell::GetIsContent(bool* aIsContent) {
12612 *aIsContent = (mItemType == typeContent);
12613 return NS_OK;
12616 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12617 MOZ_ASSERT(aURI, "Must have a URI!");
12619 if (!mFiredUnloadEvent) {
12620 return true;
12623 if (!mLoadingURI) {
12624 return false;
12627 bool isPrivateWin = false;
12628 Document* doc = GetDocument();
12629 if (doc) {
12630 isPrivateWin =
12631 doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
12634 nsCOMPtr<nsIScriptSecurityManager> secMan =
12635 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12636 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12637 aURI, mLoadingURI, false, isPrivateWin));
12641 // Routines for selection and clipboard
12643 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12644 nsIController** aResult) {
12645 NS_ENSURE_ARG_POINTER(aResult);
12646 *aResult = nullptr;
12648 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12650 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12651 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12653 return root->GetControllerForCommand(aCommand, false /* for any window */,
12654 aResult);
12657 NS_IMETHODIMP
12658 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12659 NS_ENSURE_ARG_POINTER(aResult);
12660 *aResult = false;
12662 nsresult rv = NS_ERROR_FAILURE;
12664 nsCOMPtr<nsIController> controller;
12665 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12666 if (controller) {
12667 rv = controller->IsCommandEnabled(aCommand, aResult);
12670 return rv;
12673 NS_IMETHODIMP
12674 nsDocShell::DoCommand(const char* aCommand) {
12675 nsresult rv = NS_ERROR_FAILURE;
12677 nsCOMPtr<nsIController> controller;
12678 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12679 if (controller) {
12680 rv = controller->DoCommand(aCommand);
12683 return rv;
12686 NS_IMETHODIMP
12687 nsDocShell::DoCommandWithParams(const char* aCommand,
12688 nsICommandParams* aParams) {
12689 nsCOMPtr<nsIController> controller;
12690 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12691 if (NS_WARN_IF(NS_FAILED(rv))) {
12692 return rv;
12695 nsCOMPtr<nsICommandController> commandController =
12696 do_QueryInterface(controller, &rv);
12697 if (NS_WARN_IF(NS_FAILED(rv))) {
12698 return rv;
12701 return commandController->DoCommandWithParams(aCommand, aParams);
12704 nsresult nsDocShell::EnsureCommandHandler() {
12705 if (!mCommandManager) {
12706 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12707 mCommandManager = new nsCommandManager(domWindow);
12710 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12713 // link handling
12715 class OnLinkClickEvent : public Runnable {
12716 public:
12717 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12718 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12719 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12721 NS_IMETHOD Run() override {
12722 AutoPopupStatePusher popupStatePusher(mPopupState);
12724 // We need to set up an AutoJSAPI here for the following reason: When we
12725 // do OnLinkClickSync we'll eventually end up in
12726 // nsGlobalWindow::OpenInternal which only does popup blocking if
12727 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12728 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12729 // concerned.
12730 AutoJSAPI jsapi;
12731 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12732 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12733 mTriggeringPrincipal);
12735 return NS_OK;
12738 private:
12739 RefPtr<nsDocShell> mHandler;
12740 nsCOMPtr<nsIContent> mContent;
12741 RefPtr<nsDocShellLoadState> mLoadState;
12742 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12743 PopupBlocker::PopupControlState mPopupState;
12744 bool mNoOpenerImplied;
12745 bool mIsTrusted;
12748 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12749 nsDocShellLoadState* aLoadState,
12750 bool aNoOpenerImplied, bool aIsTrusted,
12751 nsIPrincipal* aTriggeringPrincipal)
12752 : mozilla::Runnable("OnLinkClickEvent"),
12753 mHandler(aHandler),
12754 mContent(aContent),
12755 mLoadState(aLoadState),
12756 mTriggeringPrincipal(aTriggeringPrincipal),
12757 mPopupState(PopupBlocker::GetPopupControlState()),
12758 mNoOpenerImplied(aNoOpenerImplied),
12759 mIsTrusted(aIsTrusted) {}
12761 nsresult nsDocShell::OnLinkClick(
12762 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12763 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12764 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12765 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12766 #ifndef ANDROID
12767 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12768 #endif
12769 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12771 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12772 return NS_OK;
12775 // On history navigation through Back/Forward buttons, don't execute
12776 // automatic JavaScript redirection such as |anchorElement.click()| or
12777 // |formElement.submit()|.
12779 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12780 // nsDocShell::OnLinkClickSync(...) instead.
12781 if (ShouldBlockLoadingForBackButton()) {
12782 return NS_OK;
12785 if (aContent->IsEditable()) {
12786 return NS_OK;
12789 Document* ownerDoc = aContent->OwnerDoc();
12790 if (nsContentUtils::IsExternalProtocol(aURI)) {
12791 ownerDoc->EnsureNotEnteringAndExitFullscreen();
12794 bool noOpenerImplied = false;
12795 nsAutoString target(aTargetSpec);
12796 if (aFileName.IsVoid() &&
12797 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
12798 target = u"_blank";
12799 if (!aTargetSpec.Equals(target)) {
12800 noOpenerImplied = true;
12804 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12805 loadState->SetTarget(target);
12806 loadState->SetFileName(aFileName);
12807 loadState->SetPostDataStream(aPostDataStream);
12808 loadState->SetHeadersStream(aHeadersDataStream);
12809 loadState->SetFirstParty(true);
12810 loadState->SetTriggeringPrincipal(
12811 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12812 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12813 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12814 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12816 nsCOMPtr<nsIRunnable> ev =
12817 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12818 aIsTrusted, aTriggeringPrincipal);
12819 return Dispatch(ev.forget());
12822 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12823 nsIURI* aLinkURI, nsIContent* aContent,
12824 bool aIsUserTriggered) {
12825 if (net::SchemeIsJavascript(aLinkURI)) {
12826 return false;
12829 // External links from within app tabs should always open in new tabs
12830 // instead of replacing the app tab's page (Bug 575561)
12831 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12832 // get either host, just return false to use the original target.
12833 nsAutoCString linkHost;
12834 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12835 return false;
12838 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12839 // privileged code to change the default targeting behaviour. In particular,
12840 // if a user-initiated link click for the (or targetting the) top-level frame
12841 // is detected, we default the target to "_blank" to give it a new
12842 // top-level BrowsingContext.
12843 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
12844 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
12845 aOriginalTarget == u"_top"_ns)) {
12846 return true;
12849 // Don't modify non-default targets.
12850 if (!aOriginalTarget.IsEmpty()) {
12851 return false;
12854 // Only check targets that are in extension panels or app tabs.
12855 // (isAppTab will be false for app tab subframes).
12856 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12857 if (!mmGroup.EqualsLiteral("webext-browsers") &&
12858 !mBrowsingContext->IsAppTab()) {
12859 return false;
12862 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12863 if (!docURI) {
12864 return false;
12867 nsAutoCString docHost;
12868 if (NS_FAILED(docURI->GetHost(docHost))) {
12869 return false;
12872 if (linkHost.Equals(docHost)) {
12873 return false;
12876 // Special case: ignore "www" prefix if it is part of host string
12877 return linkHost.Length() < docHost.Length()
12878 ? !docHost.Equals("www."_ns + linkHost)
12879 : !linkHost.Equals("www."_ns + docHost);
12882 static bool ElementCanHaveNoopener(nsIContent* aContent) {
12883 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12884 // the HTML, XHTML, or SVG namespace.
12885 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
12886 nsGkAtoms::form) ||
12887 aContent->IsSVGElement(nsGkAtoms::a);
12890 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12891 nsDocShellLoadState* aLoadState,
12892 bool aNoOpenerImplied,
12893 nsIPrincipal* aTriggeringPrincipal) {
12894 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12895 return NS_OK;
12898 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12899 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12900 // but |HTMLFormElement::SubmitSubmission(...)|.
12901 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
12902 ShouldBlockLoadingForBackButton()) {
12903 return NS_OK;
12906 if (aContent->IsEditable()) {
12907 return NS_OK;
12910 // if the triggeringPrincipal is not passed explicitly, then we
12911 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12912 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
12913 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
12916 // defer to an external protocol handler if necessary...
12917 nsCOMPtr<nsIExternalProtocolService> extProtService =
12918 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12919 if (extProtService) {
12920 nsAutoCString scheme;
12921 aLoadState->URI()->GetScheme(scheme);
12922 if (!scheme.IsEmpty()) {
12923 // if the URL scheme does not correspond to an exposed protocol, then
12924 // we need to hand this link click over to the external protocol
12925 // handler.
12926 bool isExposed;
12927 nsresult rv =
12928 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12929 if (NS_SUCCEEDED(rv) && !isExposed) {
12930 return extProtService->LoadURI(
12931 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
12932 /* aTriggeredExternally */
12933 false,
12934 /* aHasValidUserGestureActivation */
12935 aContent->OwnerDoc()->HasValidTransientUserGestureActivation());
12940 uint32_t triggeringSandboxFlags = 0;
12941 uint64_t triggeringWindowId = 0;
12942 bool triggeringStorageAccess = false;
12943 if (mBrowsingContext) {
12944 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
12945 triggeringWindowId = aContent->OwnerDoc()->InnerWindowID();
12946 triggeringStorageAccess = aContent->OwnerDoc()->UsingStorageAccess();
12949 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12950 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
12951 bool triggeringPrincipalIsSystemPrincipal =
12952 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
12953 if (elementCanHaveNoopener) {
12954 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
12955 nsAutoString relString;
12956 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
12957 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
12958 relString);
12960 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
12961 bool explicitOpenerSet = false;
12963 // The opener behaviour follows a hierarchy, such that if a higher
12964 // priority behaviour is specified, it always takes priority. That
12965 // priority is currently: norefrerer > noopener > opener > default
12967 while (tok.hasMoreTokens()) {
12968 const nsAString& token = tok.nextToken();
12969 if (token.LowerCaseEqualsLiteral("noreferrer")) {
12970 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
12971 INTERNAL_LOAD_FLAGS_NO_OPENER;
12972 // noreferrer cannot be overwritten by a 'rel=opener'.
12973 explicitOpenerSet = true;
12974 break;
12977 if (token.LowerCaseEqualsLiteral("noopener")) {
12978 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12979 explicitOpenerSet = true;
12982 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12983 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
12984 explicitOpenerSet = true;
12988 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
12989 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
12990 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12993 if (aNoOpenerImplied) {
12994 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
12998 // Get the owner document of the link that was clicked, this will be
12999 // the document that the link is in, or the last document that the
13000 // link was in. From that document, we'll get the URI to use as the
13001 // referrer, since the current URI in this docshell may be a
13002 // new document that we're in the process of loading.
13003 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
13005 // Now check that the referrerDoc's inner window is the current inner
13006 // window for mScriptGlobal. If it's not, then we don't want to
13007 // follow this link.
13008 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
13009 if (!mScriptGlobal || !referrerInner ||
13010 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
13011 // We're no longer the current inner window
13012 return NS_OK;
13015 // referrer could be null here in some odd cases, but that's ok,
13016 // we'll just load the link w/o sending a referrer in those cases.
13018 // If this is an anchor element, grab its type property to use as a hint
13019 nsAutoString typeHint;
13020 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13021 if (anchor) {
13022 anchor->GetType(typeHint);
13023 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13024 nsAutoCString type, dummy;
13025 NS_ParseRequestContentType(utf8Hint, type, dummy);
13026 CopyUTF8toUTF16(type, typeHint);
13029 uint32_t loadType = LOAD_LINK;
13030 if (aLoadState->IsFormSubmission()) {
13031 if (aLoadState->Target().IsEmpty()) {
13032 // We set the right load type here for form submissions with an empty
13033 // target. Form submission with a non-empty target are handled in
13034 // nsDocShell::PerformRetargeting after we've selected the correct target
13035 // BC.
13036 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
13038 } else {
13039 // Link click can be triggered inside an onload handler, and we don't want
13040 // to add history entry in this case.
13041 bool inOnLoadHandler = false;
13042 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13043 if (inOnLoadHandler) {
13044 loadType = LOAD_NORMAL_REPLACE;
13048 nsCOMPtr<nsIReferrerInfo> referrerInfo =
13049 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
13050 : new ReferrerInfo(*referrerDoc);
13051 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
13053 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
13054 aLoadState->SetTriggeringWindowId(triggeringWindowId);
13055 aLoadState->SetTriggeringStorageAccess(triggeringStorageAccess);
13056 aLoadState->SetReferrerInfo(referrerInfo);
13057 aLoadState->SetInternalLoadFlags(flags);
13058 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
13059 aLoadState->SetLoadType(loadType);
13060 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
13061 aLoadState->SetHasValidUserGestureActivation(
13062 context && context->HasValidTransientUserGestureActivation());
13064 nsresult rv = InternalLoad(aLoadState);
13066 if (NS_SUCCEEDED(rv)) {
13067 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
13068 referrerInfo);
13071 return rv;
13074 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13075 const nsAString& aTargetSpec) {
13076 if (aContent->IsEditable()) {
13077 return NS_OK;
13080 nsresult rv = NS_ERROR_FAILURE;
13082 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13083 if (!browserChrome) {
13084 return rv;
13087 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13088 nsAutoCString spec;
13089 rv = exposableURI->GetDisplaySpec(spec);
13090 NS_ENSURE_SUCCESS(rv, rv);
13092 NS_ConvertUTF8toUTF16 uStr(spec);
13094 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13095 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13097 rv = browserChrome->SetLinkStatus(uStr);
13098 return rv;
13101 nsresult nsDocShell::OnLeaveLink() {
13102 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13103 nsresult rv = NS_ERROR_FAILURE;
13105 if (browserChrome) {
13106 rv = browserChrome->SetLinkStatus(u""_ns);
13108 return rv;
13111 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13112 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13113 UserActivation::IsHandlingUserInput() ||
13114 !Preferences::GetBool("accessibility.blockjsredirection")) {
13115 return false;
13118 bool canGoForward = false;
13119 GetCanGoForward(&canGoForward);
13120 return canGoForward;
13123 //----------------------------------------------------------------------
13124 // Web Shell Services API
13126 // This functions is only called when a new charset is detected in loading a
13127 // document.
13128 nsresult nsDocShell::CharsetChangeReloadDocument(
13129 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13130 // XXX hack. keep the aCharset and aSource wait to pick it up
13131 nsCOMPtr<nsIDocumentViewer> viewer;
13132 NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer)), NS_ERROR_FAILURE);
13133 if (viewer) {
13134 int32_t source;
13135 Unused << viewer->GetReloadEncodingAndSource(&source);
13136 if (aSource > source) {
13137 viewer->SetReloadEncodingAndSource(aEncoding, aSource);
13138 if (eCharsetReloadRequested != mCharsetReloadState) {
13139 mCharsetReloadState = eCharsetReloadRequested;
13140 switch (mLoadType) {
13141 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13142 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13143 LOAD_FLAGS_BYPASS_PROXY);
13144 case LOAD_RELOAD_BYPASS_CACHE:
13145 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13146 default:
13147 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13152 // return failure if this request is not accepted due to mCharsetReloadState
13153 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13156 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13157 if (eCharsetReloadRequested != mCharsetReloadState) {
13158 Stop(nsIWebNavigation::STOP_ALL);
13159 return NS_OK;
13161 // return failer if this request is not accepted due to mCharsetReloadState
13162 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13165 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13166 #if NS_PRINT_PREVIEW
13167 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mDocumentViewer);
13168 return viewer->ExitPrintPreview();
13169 #else
13170 return NS_OK;
13171 #endif
13174 /* [infallible] */
13175 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13176 bool* aIsTopLevelContentDocShell) {
13177 *aIsTopLevelContentDocShell = false;
13179 if (mItemType == typeContent) {
13180 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13183 return NS_OK;
13186 // Implements nsILoadContext.originAttributes
13187 NS_IMETHODIMP
13188 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13189 JS::MutableHandle<JS::Value> aVal) {
13190 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13193 // Implements nsIDocShell.GetOriginAttributes()
13194 NS_IMETHODIMP
13195 nsDocShell::GetOriginAttributes(JSContext* aCx,
13196 JS::MutableHandle<JS::Value> aVal) {
13197 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13200 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13201 nsIURI* aURI) {
13202 MOZ_ASSERT(aPrincipal);
13203 MOZ_ASSERT(aURI);
13205 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13206 return false;
13209 nsCOMPtr<nsIDocShellTreeItem> parent;
13210 GetInProcessSameTypeParent(getter_AddRefs(parent));
13211 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13212 nsPIDOMWindowInner* parentInner =
13213 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13215 StorageAccess storage =
13216 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13218 // If the partitioned service worker is enabled, service worker is allowed to
13219 // control the window if partition is enabled.
13220 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13221 RefPtr<Document> doc = parentInner->GetExtantDoc();
13223 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13224 return true;
13228 return storage == StorageAccess::eAllow;
13231 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13232 MOZ_ASSERT(!mIsBeingDestroyed);
13233 return mBrowsingContext->SetOriginAttributes(aAttrs);
13236 NS_IMETHODIMP
13237 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13238 RefPtr<nsDocShell> self = this;
13239 RefPtr<ChildProcessChannelListener> cpcl =
13240 ChildProcessChannelListener::GetSingleton();
13242 // Call into InternalLoad with the pending channel when it is received.
13243 cpcl->RegisterCallback(
13244 aIdentifier, [self, aHistoryIndex](
13245 nsDocShellLoadState* aLoadState,
13246 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13247 aStreamFilterEndpoints,
13248 nsDOMNavigationTiming* aTiming) {
13249 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13250 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13251 aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
13252 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
13253 return NS_BINDING_ABORTED;
13256 self->mLoadType = aLoadState->LoadType();
13257 nsCOMPtr<nsIURI> previousURI;
13258 uint32_t previousFlags = 0;
13259 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13260 getter_AddRefs(previousURI), &previousFlags);
13261 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13262 previousURI, previousFlags);
13264 if (aTiming) {
13265 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13266 self->mBlankTiming = false;
13269 // If we're performing a history load, locate the correct history entry,
13270 // and set the relevant bits on our loadState.
13271 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13272 !mozilla::SessionHistoryInParent()) {
13273 nsCOMPtr<nsISHistory> legacySHistory =
13274 self->GetSessionHistory()->LegacySHistory();
13276 nsCOMPtr<nsISHEntry> entry;
13277 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13278 getter_AddRefs(entry));
13279 if (NS_SUCCEEDED(rv)) {
13280 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13281 aLoadState->SetLoadType(LOAD_HISTORY);
13282 aLoadState->SetSHEntry(entry);
13286 self->InternalLoad(aLoadState);
13288 if (aLoadState->GetOriginalURIString().isSome()) {
13289 // Save URI string in case it's needed later when
13290 // sending to search engine service in EndPageLoad()
13291 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13294 for (auto& endpoint : aStreamFilterEndpoints) {
13295 extensions::StreamFilterParent::Attach(
13296 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13299 // If the channel isn't pending, then it means that InternalLoad
13300 // never connected it, and we shouldn't try to continue. This
13301 // can happen even if InternalLoad returned NS_OK.
13302 bool pending = false;
13303 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13304 NS_ASSERTION(pending, "We should have connected the pending channel!");
13305 if (!pending) {
13306 return NS_BINDING_ABORTED;
13308 return NS_OK;
13310 return NS_OK;
13313 NS_IMETHODIMP
13314 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13315 JSContext* aCx) {
13316 OriginAttributes attrs;
13317 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13318 return NS_ERROR_INVALID_ARG;
13321 return SetOriginAttributes(attrs);
13324 NS_IMETHODIMP
13325 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13326 if (PresShell* presShell = GetPresShell()) {
13327 *aOut = presShell->AsyncPanZoomEnabled();
13328 return NS_OK;
13331 // If we don't have a presShell, fall back to the default platform value of
13332 // whether or not APZ is enabled.
13333 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13334 return NS_OK;
13337 bool nsDocShell::HasUnloadedParent() {
13338 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13339 wc = wc->GetParentWindowContext()) {
13340 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13341 wc->GetBrowsingContext()->IsDiscarded()) {
13342 // If a parent is OOP and the parent WindowContext is no
13343 // longer current, we can assume the parent was unloaded.
13344 return true;
13347 if (wc->GetBrowsingContext()->IsInProcess() &&
13348 (!wc->GetBrowsingContext()->GetDocShell() ||
13349 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13350 return true;
13353 return false;
13356 /* static */
13357 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13358 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13359 aLoadType & LOAD_CMD_HISTORY);
13362 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13363 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13364 return;
13367 // Global history is interested into sub-frame visits only for link-coloring
13368 // purposes, thus title updates are skipped for those.
13370 // Moreover, some iframe documents (such as the ones created via
13371 // document.open()) inherit the document uri of the caller, which would cause
13372 // us to override a previously set page title with one from the subframe.
13373 if (IsSubframe()) {
13374 return;
13377 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13378 history->SetURITitle(aURI, mTitle);
13382 bool nsDocShell::IsInvisible() { return mInvisible; }
13384 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13386 /* static */
13387 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13388 const nsString& aKeyword) {
13389 if (aProvider.IsEmpty()) {
13390 return;
13392 nsresult rv;
13393 nsCOMPtr<nsISupportsString> isupportsString =
13394 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13395 NS_ENSURE_SUCCESS_VOID(rv);
13397 rv = isupportsString->SetData(aProvider);
13398 NS_ENSURE_SUCCESS_VOID(rv);
13400 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13401 if (obsSvc) {
13402 // Note that "keyword-search" refers to a search via the url
13403 // bar, not a bookmarks keyword search.
13404 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13408 NS_IMETHODIMP
13409 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13410 bool* aShouldIntercept) {
13411 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13412 aShouldIntercept);
13415 NS_IMETHODIMP
13416 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13417 return mInterceptController->ChannelIntercepted(aChannel);
13420 bool nsDocShell::InFrameSwap() {
13421 RefPtr<nsDocShell> shell = this;
13422 do {
13423 if (shell->mInFrameSwap) {
13424 return true;
13426 shell = shell->GetInProcessParentDocshell();
13427 } while (shell);
13428 return false;
13431 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13432 return std::move(mInitialClientSource);
13435 NS_IMETHODIMP
13436 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13437 if (!NS_SUCCEEDED(EnsureEditorData())) {
13438 return NS_ERROR_FAILURE;
13441 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13442 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13445 NS_IMETHODIMP
13446 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13447 *aBrowserChild = GetBrowserChild().take();
13448 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13451 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13452 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13453 return tc.forget();
13456 nsCommandManager* nsDocShell::GetCommandManager() {
13457 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13458 return mCommandManager;
13461 NS_IMETHODIMP_(void)
13462 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13463 mBrowsingContext->GetOriginAttributes(aAttrs);
13466 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13467 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13468 return docShell->GetHTMLEditorInternal();
13471 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13472 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13473 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13476 #define MATRIX_LENGTH 20
13478 NS_IMETHODIMP
13479 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13480 if (aMatrix.Length() == MATRIX_LENGTH) {
13481 mColorMatrix.reset(new gfx::Matrix5x4());
13482 static_assert(
13483 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13484 "Size mismatch for our memcpy");
13485 memcpy(mColorMatrix->components, aMatrix.Elements(),
13486 sizeof(mColorMatrix->components));
13487 } else if (aMatrix.Length() == 0) {
13488 mColorMatrix.reset();
13489 } else {
13490 return NS_ERROR_INVALID_ARG;
13493 PresShell* presShell = GetPresShell();
13494 if (!presShell) {
13495 return NS_ERROR_FAILURE;
13498 nsIFrame* frame = presShell->GetRootFrame();
13499 if (!frame) {
13500 return NS_ERROR_FAILURE;
13503 frame->SchedulePaint();
13505 return NS_OK;
13508 NS_IMETHODIMP
13509 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13510 if (mColorMatrix) {
13511 aMatrix.SetLength(MATRIX_LENGTH);
13512 static_assert(
13513 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13514 "Size mismatch for our memcpy");
13515 memcpy(aMatrix.Elements(), mColorMatrix->components,
13516 MATRIX_LENGTH * sizeof(float));
13519 return NS_OK;
13522 #undef MATRIX_LENGTH
13524 NS_IMETHODIMP
13525 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13526 *aForceReload = IsForceReloading();
13527 return NS_OK;
13530 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13532 NS_IMETHODIMP
13533 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13534 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13535 return NS_OK;
13538 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13540 bool nsDocShell::GetIsAttemptingToNavigate() {
13541 // XXXbz the document.open spec says to abort even if there's just a
13542 // queued navigation task, sort of. It's not clear whether browsers
13543 // actually do that, and we didn't use to do it, so for now let's
13544 // not do that.
13545 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13546 if (mDocumentRequest) {
13547 // There's definitely a navigation in progress.
13548 return true;
13551 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13552 // until the script runs, which means they're not sending loadgroup
13553 // notifications and hence not getting set as mDocumentRequest. Look through
13554 // our loadgroup for document-level javascript: loads.
13555 if (!mLoadGroup) {
13556 return false;
13559 nsCOMPtr<nsISimpleEnumerator> requests;
13560 mLoadGroup->GetRequests(getter_AddRefs(requests));
13561 bool hasMore = false;
13562 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13563 nsCOMPtr<nsISupports> elem;
13564 requests->GetNext(getter_AddRefs(elem));
13565 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13566 if (!scriptChannel) {
13567 continue;
13570 if (scriptChannel->GetIsDocumentLoad()) {
13571 // This is a javascript: load that might lead to a new document,
13572 // hence a navigation.
13573 return true;
13577 return mCheckingSessionHistory;
13580 void nsDocShell::SetLoadingSessionHistoryInfo(
13581 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
13582 bool aNeedToReportActiveAfterLoadingBecomesActive) {
13583 // FIXME Would like to assert this, but can't yet.
13584 // MOZ_ASSERT(!mLoadingEntry);
13585 MOZ_LOG(gSHLog, LogLevel::Debug,
13586 ("Setting the loading entry on nsDocShell %p to %s", this,
13587 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13588 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13589 mNeedToReportActiveAfterLoadingBecomesActive =
13590 aNeedToReportActiveAfterLoadingBecomesActive;
13593 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13594 uint32_t aCacheKey,
13595 nsIURI* aPreviousURI) {
13596 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13598 MOZ_LOG(gSHLog, LogLevel::Debug,
13599 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13601 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
13602 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13603 mActiveEntryIsLoadingFromSessionHistory =
13604 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13605 if (mLoadingEntry) {
13606 MOZ_LOG(gSHLog, LogLevel::Debug,
13607 ("Moving the loading entry to the active entry on nsDocShell %p "
13608 "to %s",
13609 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13610 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13611 mLoadingEntry.swap(loadingEntry);
13612 if (!mActiveEntryIsLoadingFromSessionHistory) {
13613 if (mNeedToReportActiveAfterLoadingBecomesActive) {
13614 // Needed to pass various history length WPTs.
13615 mBrowsingContext->SetActiveSessionHistoryEntry(
13616 mozilla::Nothing(), mActiveEntry.get(), mLoadType,
13617 /* aUpdatedCacheKey = */ 0, false);
13619 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13622 mNeedToReportActiveAfterLoadingBecomesActive = false;
13624 if (mActiveEntry) {
13625 if (aCacheKey != 0) {
13626 mActiveEntry->SetCacheKey(aCacheKey);
13628 MOZ_ASSERT(loadingEntry);
13629 uint32_t loadType =
13630 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13632 if (loadingEntry->mLoadId != UINT64_MAX) {
13633 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13634 // does require a non-null uri if this is for a refresh load of the same
13635 // URI, but in that case mCurrentURI won't be null here.
13636 mBrowsingContext->SessionHistoryCommit(
13637 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
13638 aPersist, false, aExpired, aCacheKey);
13643 static bool IsFaviconLoad(nsIRequest* aRequest) {
13644 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13645 if (!channel) {
13646 return false;
13649 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13650 return li && li->InternalContentPolicyType() ==
13651 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13654 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13655 nsIRequest* aRequest) {
13656 // Ignore favicon loads, they don't need to block caching.
13657 if (IsFaviconLoad(aRequest)) {
13658 return;
13661 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13663 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13665 if (mBrowsingContext->GetCurrentWindowContext()) {
13666 // We have three states: no request, one request with an id and
13667 // eiher one request without an id or multiple requests. Nothing() is no
13668 // request, Some(non-zero) is one request with an id and Some(0) is one
13669 // request without an id or multiple requests.
13670 Maybe<uint64_t> singleChannelId;
13671 if (mRequestForBlockingFromBFCacheCount > 1) {
13672 singleChannelId = Some(0);
13673 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13674 nsCOMPtr<nsIIdentChannel> identChannel;
13675 if (aStartRequest) {
13676 identChannel = do_QueryInterface(aRequest);
13677 } else {
13678 // aChannel is the channel that's being removed, but we need to check if
13679 // the remaining channel in the loadgroup has an id.
13680 nsCOMPtr<nsISimpleEnumerator> requests;
13681 mLoadGroup->GetRequests(getter_AddRefs(requests));
13682 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13683 if (!IsFaviconLoad(request) &&
13684 !!(identChannel = do_QueryInterface(request))) {
13685 break;
13690 if (identChannel) {
13691 singleChannelId = Some(identChannel->ChannelId());
13692 } else {
13693 singleChannelId = Some(0);
13695 } else {
13696 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13697 singleChannelId = Nothing();
13700 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13701 nsAutoCString uri("[no uri]");
13702 if (mCurrentURI) {
13703 uri = mCurrentURI->GetSpecOrDefault();
13705 if (singleChannelId.isNothing()) {
13706 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13707 ("Loadgroup for %s doesn't have any requests relevant for "
13708 "blocking BFCache",
13709 uri.get()));
13710 } else if (singleChannelId.value() == 0) {
13711 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13712 ("Loadgroup for %s has multiple requests relevant for blocking "
13713 "BFCache",
13714 uri.get()));
13715 } else {
13716 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13717 ("Loadgroup for %s has one request with id %" PRIu64
13718 " relevant for blocking BFCache",
13719 uri.get(), singleChannelId.value()));
13723 if (mSingleChannelId != singleChannelId) {
13724 mSingleChannelId = singleChannelId;
13725 WindowGlobalChild* wgc =
13726 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13727 if (wgc) {
13728 wgc->SendSetSingleChannelId(singleChannelId);
13734 NS_IMETHODIMP
13735 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13736 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13737 nsAutoCString uri("[no uri]");
13738 if (mCurrentURI) {
13739 uri = mCurrentURI->GetSpecOrDefault();
13741 nsAutoCString name;
13742 aRequest->GetName(name);
13743 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13744 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13746 RecordSingleChannelId(true, aRequest);
13747 return nsDocLoader::OnStartRequest(aRequest);
13750 NS_IMETHODIMP
13751 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13752 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13753 nsAutoCString uri("[no uri]");
13754 if (mCurrentURI) {
13755 uri = mCurrentURI->GetSpecOrDefault();
13757 nsAutoCString name;
13758 aRequest->GetName(name);
13759 MOZ_LOG(
13760 gSHIPBFCacheLog, LogLevel::Verbose,
13761 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13763 RecordSingleChannelId(false, aRequest);
13764 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13767 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13768 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13770 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13771 nsCOMPtr<nsISimpleEnumerator> requests;
13772 mLoadGroup->GetRequests(getter_AddRefs(requests));
13773 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13774 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13775 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13776 static_cast<DocumentChannelChild*>(channel.get())
13777 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13780 mChannelToDisconnectOnPageHide = 0;