Bug 1839316: part 5) Guard the "fetchpriority" attribute behind a pref. r=kershaw...
[gecko.git] / docshell / base / nsDocShell.cpp
blob555fb05473fd446e5b681ade53a298a58aeea1dd
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/ObservedDocShell.h"
34 #include "mozilla/Preferences.h"
35 #include "mozilla/PresShell.h"
36 #include "mozilla/ResultExtensions.h"
37 #include "mozilla/SchedulerGroup.h"
38 #include "mozilla/ScopeExit.h"
39 #include "mozilla/ScrollTypes.h"
40 #include "mozilla/SimpleEnumerator.h"
41 #include "mozilla/StaticPrefs_browser.h"
42 #include "mozilla/StaticPrefs_docshell.h"
43 #include "mozilla/StaticPrefs_dom.h"
44 #include "mozilla/StaticPrefs_extensions.h"
45 #include "mozilla/StaticPrefs_privacy.h"
46 #include "mozilla/StaticPrefs_security.h"
47 #include "mozilla/StaticPrefs_ui.h"
48 #include "mozilla/StaticPrefs_fission.h"
49 #include "mozilla/StartupTimeline.h"
50 #include "mozilla/StorageAccess.h"
51 #include "mozilla/StoragePrincipalHelper.h"
52 #include "mozilla/Telemetry.h"
54 #include "mozilla/Unused.h"
55 #include "mozilla/WidgetUtils.h"
57 #include "mozilla/dom/AutoEntryScript.h"
58 #include "mozilla/dom/ChildProcessChannelListener.h"
59 #include "mozilla/dom/ClientChannelHelper.h"
60 #include "mozilla/dom/ClientHandle.h"
61 #include "mozilla/dom/ClientInfo.h"
62 #include "mozilla/dom/ClientManager.h"
63 #include "mozilla/dom/ClientSource.h"
64 #include "mozilla/dom/ContentChild.h"
65 #include "mozilla/dom/ContentFrameMessageManager.h"
66 #include "mozilla/dom/DocGroup.h"
67 #include "mozilla/dom/Element.h"
68 #include "mozilla/dom/HTMLAnchorElement.h"
69 #include "mozilla/dom/HTMLIFrameElement.h"
70 #include "mozilla/dom/PerformanceNavigation.h"
71 #include "mozilla/dom/PermissionMessageUtils.h"
72 #include "mozilla/dom/PopupBlocker.h"
73 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
74 #include "mozilla/dom/ScreenOrientation.h"
75 #include "mozilla/dom/ScriptSettings.h"
76 #include "mozilla/dom/ServiceWorkerInterceptController.h"
77 #include "mozilla/dom/ServiceWorkerUtils.h"
78 #include "mozilla/dom/SessionHistoryEntry.h"
79 #include "mozilla/dom/SessionStorageManager.h"
80 #include "mozilla/dom/SessionStoreChangeListener.h"
81 #include "mozilla/dom/SessionStoreChild.h"
82 #include "mozilla/dom/SessionStoreUtils.h"
83 #include "mozilla/dom/BrowserChild.h"
84 #include "mozilla/dom/ToJSValue.h"
85 #include "mozilla/dom/UserActivation.h"
86 #include "mozilla/dom/ChildSHistory.h"
87 #include "mozilla/dom/nsCSPContext.h"
88 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
89 #include "mozilla/dom/LoadURIOptionsBinding.h"
90 #include "mozilla/dom/JSWindowActorChild.h"
91 #include "mozilla/dom/DocumentBinding.h"
92 #include "mozilla/ipc/ProtocolUtils.h"
93 #include "mozilla/net/DocumentChannel.h"
94 #include "mozilla/net/DocumentChannelChild.h"
95 #include "mozilla/net/ParentChannelWrapper.h"
96 #include "mozilla/net/UrlClassifierFeatureFactory.h"
97 #include "ReferrerInfo.h"
99 #include "nsIAuthPrompt.h"
100 #include "nsIAuthPrompt2.h"
101 #include "nsICachingChannel.h"
102 #include "nsICaptivePortalService.h"
103 #include "nsIChannel.h"
104 #include "nsIChannelEventSink.h"
105 #include "nsIClassOfService.h"
106 #include "nsIConsoleReportCollector.h"
107 #include "nsIContent.h"
108 #include "nsIContentInlines.h"
109 #include "nsIContentSecurityPolicy.h"
110 #include "nsIContentViewer.h"
111 #include "nsIController.h"
112 #include "nsIDocShellTreeItem.h"
113 #include "nsIDocShellTreeOwner.h"
114 #include "mozilla/dom/Document.h"
115 #include "nsHTMLDocument.h"
116 #include "nsIDocumentLoaderFactory.h"
117 #include "nsIDOMWindow.h"
118 #include "nsIEditingSession.h"
119 #include "nsIEffectiveTLDService.h"
120 #include "nsIExternalProtocolService.h"
121 #include "nsIFormPOSTActionChannel.h"
122 #include "nsIFrame.h"
123 #include "nsIGlobalObject.h"
124 #include "nsIHttpChannel.h"
125 #include "nsIHttpChannelInternal.h"
126 #include "nsIIDNService.h"
127 #include "nsIInputStreamChannel.h"
128 #include "nsIInterfaceRequestorUtils.h"
129 #include "nsILayoutHistoryState.h"
130 #include "nsILoadInfo.h"
131 #include "nsILoadURIDelegate.h"
132 #include "nsIMultiPartChannel.h"
133 #include "nsINestedURI.h"
134 #include "nsINetworkPredictor.h"
135 #include "nsINode.h"
136 #include "nsINSSErrorsService.h"
137 #include "nsIObserverService.h"
138 #include "nsIOService.h"
139 #include "nsIPrincipal.h"
140 #include "nsIPrivacyTransitionObserver.h"
141 #include "nsIPrompt.h"
142 #include "nsIPromptCollection.h"
143 #include "nsIPromptFactory.h"
144 #include "nsIPublicKeyPinningService.h"
145 #include "nsIReflowObserver.h"
146 #include "nsIScriptChannel.h"
147 #include "nsIScriptObjectPrincipal.h"
148 #include "nsIScriptSecurityManager.h"
149 #include "nsIScrollableFrame.h"
150 #include "nsIScrollObserver.h"
151 #include "nsISupportsPrimitives.h"
152 #include "nsISecureBrowserUI.h"
153 #include "nsISeekableStream.h"
154 #include "nsISelectionDisplay.h"
155 #include "nsISHEntry.h"
156 #include "nsISiteSecurityService.h"
157 #include "nsISocketProvider.h"
158 #include "nsIStringBundle.h"
159 #include "nsIStructuredCloneContainer.h"
160 #include "nsIBrowserChild.h"
161 #include "nsITextToSubURI.h"
162 #include "nsITimedChannel.h"
163 #include "nsITimer.h"
164 #include "nsITransportSecurityInfo.h"
165 #include "nsIUploadChannel.h"
166 #include "nsIURIFixup.h"
167 #include "nsIURIMutator.h"
168 #include "nsIURILoader.h"
169 #include "nsIViewSourceChannel.h"
170 #include "nsIWebBrowserChrome.h"
171 #include "nsIWebBrowserChromeFocus.h"
172 #include "nsIWebBrowserFind.h"
173 #include "nsIWebProgress.h"
174 #include "nsIWidget.h"
175 #include "nsIWindowWatcher.h"
176 #include "nsIWritablePropertyBag2.h"
177 #include "nsIX509Cert.h"
178 #include "nsIXULRuntime.h"
180 #include "nsCommandManager.h"
181 #include "nsPIDOMWindow.h"
182 #include "nsPIWindowRoot.h"
184 #include "IHistory.h"
185 #include "IUrlClassifierUITelemetry.h"
187 #include "nsArray.h"
188 #include "nsArrayUtils.h"
189 #include "nsCExternalHandlerService.h"
190 #include "nsContentDLF.h"
191 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
192 #include "nsContentSecurityManager.h"
193 #include "nsContentSecurityUtils.h"
194 #include "nsContentUtils.h"
195 #include "nsCURILoader.h"
196 #include "nsDocShellCID.h"
197 #include "nsDocShellEditorData.h"
198 #include "nsDocShellEnumerator.h"
199 #include "nsDocShellLoadState.h"
200 #include "nsDocShellLoadTypes.h"
201 #include "nsDOMCID.h"
202 #include "nsDOMNavigationTiming.h"
203 #include "nsDSURIContentListener.h"
204 #include "nsEditingSession.h"
205 #include "nsError.h"
206 #include "nsEscape.h"
207 #include "nsFocusManager.h"
208 #include "nsGlobalWindowInner.h"
209 #include "nsGlobalWindowOuter.h"
210 #include "nsJSEnvironment.h"
211 #include "nsNetCID.h"
212 #include "nsNetUtil.h"
213 #include "nsObjectLoadingContent.h"
214 #include "nsPingListener.h"
215 #include "nsPoint.h"
216 #include "nsQueryObject.h"
217 #include "nsQueryActor.h"
218 #include "nsRect.h"
219 #include "nsRefreshTimer.h"
220 #include "nsSandboxFlags.h"
221 #include "nsSHEntry.h"
222 #include "nsSHistory.h"
223 #include "nsSHEntry.h"
224 #include "nsStructuredCloneContainer.h"
225 #include "nsSubDocumentFrame.h"
226 #include "nsURILoader.h"
227 #include "nsURLHelper.h"
228 #include "nsView.h"
229 #include "nsViewManager.h"
230 #include "nsViewSourceHandler.h"
231 #include "nsWebBrowserFind.h"
232 #include "nsWhitespaceTokenizer.h"
233 #include "nsWidgetsCID.h"
234 #include "nsXULAppAPI.h"
236 #include "ThirdPartyUtil.h"
237 #include "GeckoProfiler.h"
238 #include "mozilla/NullPrincipal.h"
239 #include "Navigator.h"
240 #include "prenv.h"
241 #include "mozilla/ipc/URIUtils.h"
242 #include "sslerr.h"
243 #include "mozpkix/pkix.h"
244 #include "NSSErrorsService.h"
246 #include "timeline/JavascriptTimelineMarker.h"
247 #include "nsDocShellTelemetryUtils.h"
249 #ifdef MOZ_PLACES
250 # include "nsIFaviconService.h"
251 # include "mozIPlacesPendingOperation.h"
252 #endif
254 #if NS_PRINT_PREVIEW
255 # include "nsIDocumentViewerPrint.h"
256 # include "nsIWebBrowserPrint.h"
257 #endif
259 using namespace mozilla;
260 using namespace mozilla::dom;
261 using namespace mozilla::net;
263 using mozilla::ipc::Endpoint;
265 // Threshold value in ms for META refresh based redirects
266 #define REFRESH_REDIRECT_TIMER 15000
268 static mozilla::LazyLogModule gCharsetMenuLog("CharsetMenu");
270 #define LOGCHARSETMENU(args) \
271 MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args)
273 #ifdef DEBUG
274 unsigned long nsDocShell::gNumberOfDocShells = 0;
275 static uint64_t gDocshellIDCounter = 0;
277 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
278 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
279 "DocShellAndDOMWindowLeak");
280 #endif
281 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
282 extern mozilla::LazyLogModule gPageCacheLog;
283 mozilla::LazyLogModule gSHLog("SessionHistory");
284 extern mozilla::LazyLogModule gSHIPBFCacheLog;
286 const char kAppstringsBundleURL[] =
287 "chrome://global/locale/appstrings.properties";
289 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext,
290 nsILoadInfo* aLoadInfo) {
291 MOZ_ASSERT(aBrowsingContext);
292 MOZ_ASSERT(aLoadInfo);
294 if (aLoadInfo->GetExternalContentPolicyType() !=
295 ExtContentPolicy::TYPE_DOCUMENT) {
296 return false;
299 return aBrowsingContext->IsTopContent();
302 // True if loading for top level document loading in active tab.
303 static bool IsUrgentStart(BrowsingContext* aBrowsingContext,
304 nsILoadInfo* aLoadInfo, uint32_t aLoadType) {
305 MOZ_ASSERT(aBrowsingContext);
306 MOZ_ASSERT(aLoadInfo);
308 if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) {
309 return false;
312 if (aLoadType &
313 (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) {
314 return true;
317 return aBrowsingContext->IsActive();
320 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
321 uint64_t aContentWindowID)
322 : nsDocLoader(true),
323 mContentWindowID(aContentWindowID),
324 mBrowsingContext(aBrowsingContext),
325 mParentCharset(nullptr),
326 mTreeOwner(nullptr),
327 mScrollbarPref(ScrollbarPreference::Auto),
328 mCharsetReloadState(eCharsetReloadInit),
329 mParentCharsetSource(0),
330 mFrameMargins(-1, -1),
331 mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome),
332 mPreviousEntryIndex(-1),
333 mLoadedEntryIndex(-1),
334 mBusyFlags(BUSY_FLAGS_NONE),
335 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
336 mLoadType(0),
337 mFailedLoadType(0),
338 mJSRunToCompletionDepth(0),
339 mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
340 mChannelToDisconnectOnPageHide(0),
341 mCreatingDocument(false),
342 #ifdef DEBUG
343 mInEnsureScriptEnv(false),
344 #endif
345 mInitialized(false),
346 mAllowSubframes(true),
347 mAllowMetaRedirects(true),
348 mAllowImages(true),
349 mAllowMedia(true),
350 mAllowDNSPrefetch(true),
351 mAllowWindowControl(true),
352 mCSSErrorReportingEnabled(false),
353 mAllowAuth(mItemType == typeContent),
354 mAllowKeywordFixup(false),
355 mDisableMetaRefreshWhenInactive(false),
356 mWindowDraggingAllowed(false),
357 mInFrameSwap(false),
358 mFiredUnloadEvent(false),
359 mEODForCurrentDocument(false),
360 mURIResultedInDocument(false),
361 mIsBeingDestroyed(false),
362 mIsExecutingOnLoadHandler(false),
363 mSavingOldViewer(false),
364 mInvisible(false),
365 mHasLoadedNonBlankURI(false),
366 mBlankTiming(false),
367 mTitleValidForCurrentURI(false),
368 mWillChangeProcess(false),
369 mIsNavigating(false),
370 mForcedAutodetection(false),
371 mCheckingSessionHistory(false),
372 mNeedToReportActiveAfterLoadingBecomesActive(false) {
373 // If no outer window ID was provided, generate a new one.
374 if (aContentWindowID == 0) {
375 mContentWindowID = nsContentUtils::GenerateWindowId();
378 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
380 #ifdef DEBUG
381 mDocShellID = gDocshellIDCounter++;
382 // We're counting the number of |nsDocShells| to help find leaks
383 ++gNumberOfDocShells;
384 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
385 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this,
386 gNumberOfDocShells, getpid(), mDocShellID));
387 #endif
390 nsDocShell::~nsDocShell() {
391 MOZ_ASSERT(!mObserved);
393 // Avoid notifying observers while we're in the dtor.
394 mIsBeingDestroyed = true;
396 Destroy();
398 if (mContentViewer) {
399 mContentViewer->Close(nullptr);
400 mContentViewer->Destroy();
401 mContentViewer = nullptr;
404 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
406 #ifdef DEBUG
407 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
408 nsAutoCString url;
409 if (mLastOpenedURI) {
410 url = mLastOpenedURI->GetSpecOrDefault();
412 // Data URLs can be very long, so truncate to avoid flooding the log.
413 const uint32_t maxURLLength = 1000;
414 if (url.Length() > maxURLLength) {
415 url.Truncate(maxURLLength);
419 // We're counting the number of |nsDocShells| to help find leaks
420 --gNumberOfDocShells;
421 MOZ_LOG(
422 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
423 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n",
424 (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get()));
426 #endif
429 bool nsDocShell::Initialize() {
430 if (mInitialized) {
431 // We've already been initialized.
432 return true;
435 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
436 "Unexpected item type in docshell");
438 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
439 mInitialized = true;
441 mDisableMetaRefreshWhenInactive =
442 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
443 mDisableMetaRefreshWhenInactive);
445 if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) {
446 const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
447 : NS_CHROME_WEBNAVIGATION_CREATE;
448 serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr);
451 return true;
454 /* static */
455 already_AddRefed<nsDocShell> nsDocShell::Create(
456 BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
457 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
459 nsresult rv;
460 RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
462 // Initialize the underlying nsDocLoader.
463 rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
464 if (NS_WARN_IF(NS_FAILED(rv))) {
465 return nullptr;
468 // Create our ContentListener
469 ds->mContentListener = new nsDSURIContentListener(ds);
471 // We enable if we're in the parent process in order to support non-e10s
472 // configurations.
473 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
474 // constructor.
475 if (XRE_IsParentProcess()) {
476 ds->mInterceptController = new ServiceWorkerInterceptController();
479 // We want to hold a strong ref to the loadgroup, so it better hold a weak
480 // ref to us... use an InterfaceRequestorProxy to do this.
481 nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
482 ds->mLoadGroup->SetNotificationCallbacks(proxy);
484 // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
485 // It could be nice to directly set up our DocLoader tree?
486 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
487 if (NS_WARN_IF(NS_FAILED(rv))) {
488 return nullptr;
491 // Add |ds| as a progress listener to itself. A little weird, but simpler
492 // than reproducing all the listener-notification logic in overrides of the
493 // various methods via which nsDocLoader can be notified. Note that this
494 // holds an nsWeakPtr to |ds|, so it's ok.
495 rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
496 nsIWebProgress::NOTIFY_STATE_NETWORK |
497 nsIWebProgress::NOTIFY_LOCATION);
498 if (NS_WARN_IF(NS_FAILED(rv))) {
499 return nullptr;
502 // If our BrowsingContext has private browsing enabled, update the number of
503 // private browsing docshells.
504 if (aBrowsingContext->UsePrivateBrowsing()) {
505 ds->NotifyPrivateBrowsingChanged();
508 // If our parent window is present in this process, set up our parent now.
509 RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext();
510 if (parentWC && parentWC->IsInProcess()) {
511 // If we don't have a parent element anymore, we can't finish this load!
512 // How'd we get here?
513 RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement();
514 if (!parentElement) {
515 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
516 return nullptr;
519 // We have an in-process parent window, but don't have a parent nsDocShell?
520 // How'd we get here!
521 nsCOMPtr<nsIDocShell> parentShell =
522 parentElement->OwnerDoc()->GetDocShell();
523 if (!parentShell) {
524 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
525 return nullptr;
527 parentShell->AddChild(ds);
530 // Make |ds| the primary DocShell for the given context.
531 aBrowsingContext->SetDocShell(ds);
533 // Set |ds| default load flags on load group.
534 ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
536 if (XRE_IsParentProcess()) {
537 aBrowsingContext->Canonical()->MaybeAddAsProgressListener(ds);
540 return ds.forget();
543 void nsDocShell::DestroyChildren() {
544 for (auto* child : mChildList.ForwardRange()) {
545 nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child);
546 NS_ASSERTION(shell, "docshell has null child");
548 if (shell) {
549 shell->SetTreeOwner(nullptr);
553 nsDocLoader::DestroyChildren();
556 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
557 mScriptGlobal, mInitialClientSource,
558 mBrowsingContext,
559 mChromeEventHandler)
561 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
562 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
564 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
565 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
566 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
567 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
568 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
569 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
570 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
571 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
572 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
573 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
574 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
575 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
576 mInterceptController)
577 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
579 NS_IMETHODIMP
580 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
581 MOZ_ASSERT(aSink, "null out param");
583 *aSink = nullptr;
585 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
586 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
587 *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
588 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
589 *aSink = mContentListener;
590 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
591 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
592 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
593 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
594 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
595 NS_SUCCEEDED(EnsureScriptEnvironment())) {
596 return mScriptGlobal->QueryInterface(aIID, aSink);
597 } else if (aIID.Equals(NS_GET_IID(Document)) &&
598 NS_SUCCEEDED(EnsureContentViewer())) {
599 RefPtr<Document> doc = mContentViewer->GetDocument();
600 doc.forget(aSink);
601 return *aSink ? NS_OK : NS_NOINTERFACE;
602 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
603 NS_SUCCEEDED(EnsureScriptEnvironment())) {
604 nsresult rv;
605 nsCOMPtr<nsIWindowWatcher> wwatch =
606 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
607 NS_ENSURE_SUCCESS(rv, rv);
609 // Get the an auth prompter for our window so that the parenting
610 // of the dialogs works as it should when using tabs.
611 nsIPrompt* prompt;
612 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
613 NS_ENSURE_SUCCESS(rv, rv);
615 *aSink = prompt;
616 return NS_OK;
617 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
618 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
619 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
620 ? NS_OK
621 : NS_NOINTERFACE;
622 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
623 // This is deprecated, you should instead directly get
624 // ChildSHistory from the browsing context.
625 MOZ_DIAGNOSTIC_ASSERT(
626 false, "Do not try to get a nsISHistory interface from nsIDocShell");
627 return NS_NOINTERFACE;
628 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
629 nsresult rv = EnsureFind();
630 if (NS_FAILED(rv)) {
631 return rv;
634 *aSink = mFind;
635 NS_ADDREF((nsISupports*)*aSink);
636 return NS_OK;
637 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
638 if (PresShell* presShell = GetPresShell()) {
639 return presShell->QueryInterface(aIID, aSink);
641 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
642 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
643 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
644 if (NS_SUCCEEDED(rv) && treeOwner) {
645 return treeOwner->QueryInterface(aIID, aSink);
647 } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
648 *aSink = GetBrowserChild().take();
649 return *aSink ? NS_OK : NS_ERROR_FAILURE;
650 } else {
651 return nsDocLoader::GetInterface(aIID, aSink);
654 NS_IF_ADDREF(((nsISupports*)*aSink));
655 return *aSink ? NS_OK : NS_NOINTERFACE;
658 NS_IMETHODIMP
659 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
660 // Note: this gets called fairly early (before a pageload actually starts).
661 // We could probably defer this even longer.
662 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
663 static_cast<BrowserChild*>(browserChild.get())
664 ->SetCancelContentJSEpoch(aEpoch);
665 return NS_OK;
668 nsresult nsDocShell::CheckDisallowedJavascriptLoad(
669 nsDocShellLoadState* aLoadState) {
670 if (!net::SchemeIsJavascript(aLoadState->URI())) {
671 return NS_OK;
674 if (nsCOMPtr<nsIPrincipal> targetPrincipal =
675 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
676 if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) {
677 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
679 return NS_OK;
681 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
684 NS_IMETHODIMP
685 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
686 return LoadURI(aLoadState, aSetNavigating, false);
689 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
690 bool aSetNavigating,
691 bool aContinueHandlingSubframeHistory) {
692 MOZ_ASSERT(aLoadState, "Must have a valid load state!");
693 // NOTE: This comparison between what appears to be internal/external load
694 // flags is intentional, as it's ensuring that the caller isn't using any of
695 // the flags reserved for implementations by the `nsIWebNavigation` interface.
696 // In the future, this check may be dropped.
697 MOZ_ASSERT(
698 (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
699 "Should not have these flags set");
700 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
701 "Targeting doesn't occur until InternalLoad");
703 if (!aLoadState->TriggeringPrincipal()) {
704 MOZ_ASSERT(false, "LoadURI must have a triggering principal");
705 return NS_ERROR_FAILURE;
708 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
710 bool oldIsNavigating = mIsNavigating;
711 auto cleanupIsNavigating =
712 MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
713 if (aSetNavigating) {
714 mIsNavigating = true;
717 PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden;
718 if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) {
719 popupState = PopupBlocker::openAllowed;
720 // If we allow popups as part of the navigation, ensure we fake a user
721 // interaction, so that popups can, in fact, be allowed to open.
722 if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) {
723 wc->NotifyUserGestureActivation();
727 AutoPopupStatePusher statePusher(popupState);
729 if (aLoadState->GetCancelContentJSEpoch().isSome()) {
730 SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
733 // Note: we allow loads to get through here even if mFiredUnloadEvent is
734 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
735 // so we pass false as the second parameter to IsNavigationAllowed.
736 // However, we don't allow the page to change location *in the middle of*
737 // firing beforeunload, so we do need to check if *beforeunload* is currently
738 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
739 if (!IsNavigationAllowed(true, false)) {
740 return NS_OK; // JS may not handle returning of an error code
743 nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
744 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) {
745 defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
746 } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) {
747 defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
750 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags));
752 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
753 mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
754 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
757 // LoadType used to be set to a default value here, if no LoadInfo/LoadState
758 // object was passed in. That functionality has been removed as of bug
759 // 1492648. LoadType should now be set up by the caller at the time they
760 // create their nsDocShellLoadState object to pass into LoadURI.
762 MOZ_LOG(
763 gDocShellLeakLog, LogLevel::Debug,
764 ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
765 aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
767 if ((!aLoadState->LoadIsFromSessionHistory() &&
768 !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
769 LOAD_FLAGS_REPLACE_HISTORY)) ||
770 aContinueHandlingSubframeHistory) {
771 // This is possibly a subframe, so handle it accordingly.
773 // If history exists, it will be loaded into the aLoadState object, and the
774 // LoadType will be changed.
775 if (MaybeHandleSubframeHistory(aLoadState,
776 aContinueHandlingSubframeHistory)) {
777 // MaybeHandleSubframeHistory returns true if we need to continue loading
778 // asynchronously.
779 return NS_OK;
783 if (aLoadState->LoadIsFromSessionHistory()) {
784 MOZ_LOG(gSHLog, LogLevel::Debug,
785 ("nsDocShell[%p]: loading from session history", this));
787 if (!mozilla::SessionHistoryInParent()) {
788 nsCOMPtr<nsISHEntry> entry = aLoadState->SHEntry();
789 return LoadHistoryEntry(entry, aLoadState->LoadType(),
790 aLoadState->HasValidUserGestureActivation());
793 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
794 return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(),
795 aLoadState->LoadType(),
796 aLoadState->HasValidUserGestureActivation());
799 // On history navigation via Back/Forward buttons, don't execute
800 // automatic JavaScript redirection such as |location.href = ...| or
801 // |window.open()|
803 // LOAD_NORMAL: window.open(...) etc.
804 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
805 if ((aLoadState->LoadType() == LOAD_NORMAL ||
806 aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
807 ShouldBlockLoadingForBackButton()) {
808 return NS_OK;
811 BrowsingContext::Type bcType = mBrowsingContext->GetType();
813 // Set up the inheriting principal in LoadState.
814 nsresult rv = aLoadState->SetupInheritingPrincipal(
815 bcType, mBrowsingContext->OriginAttributesRef());
816 NS_ENSURE_SUCCESS(rv, rv);
818 rv = aLoadState->SetupTriggeringPrincipal(
819 mBrowsingContext->OriginAttributesRef());
820 NS_ENSURE_SUCCESS(rv, rv);
822 aLoadState->CalculateLoadURIFlags();
824 MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
825 "Typehint should be null when calling InternalLoad from LoadURI");
826 MOZ_ASSERT(aLoadState->FileName().IsVoid(),
827 "FileName should be null when calling InternalLoad from LoadURI");
828 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(),
829 "Shouldn't be loading from an entry when calling InternalLoad "
830 "from LoadURI");
832 // If we have a system triggering principal, we can assume that this load was
833 // triggered by some UI in the browser chrome, such as the URL bar or
834 // bookmark bar. This should count as a user interaction for the current sh
835 // entry, so that the user may navigate back to the current entry, from the
836 // entry that is going to be added as part of this load.
837 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
838 aLoadState->TriggeringPrincipal();
839 if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) {
840 if (mozilla::SessionHistoryInParent()) {
841 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
842 if (topWc && !topWc->IsDiscarded()) {
843 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
845 } else {
846 bool oshe = false;
847 nsCOMPtr<nsISHEntry> currentSHEntry;
848 GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe);
849 if (currentSHEntry) {
850 currentSHEntry->SetHasUserInteraction(true);
855 rv = InternalLoad(aLoadState);
856 NS_ENSURE_SUCCESS(rv, rv);
858 if (aLoadState->GetOriginalURIString().isSome()) {
859 // Save URI string in case it's needed later when
860 // sending to search engine service in EndPageLoad()
861 mOriginalUriString = *aLoadState->GetOriginalURIString();
864 return NS_OK;
867 bool nsDocShell::IsLoadingFromSessionHistory() {
868 return mActiveEntryIsLoadingFromSessionHistory;
871 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
872 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
873 // detect Cancel calls.
874 class StopDetector final : public nsIRequest {
875 public:
876 StopDetector() = default;
878 NS_DECL_ISUPPORTS
879 NS_DECL_NSIREQUEST
881 bool Canceled() { return mCanceled; }
883 private:
884 ~StopDetector() = default;
886 bool mCanceled = false;
889 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
891 NS_IMETHODIMP
892 StopDetector::GetName(nsACString& aResult) {
893 aResult.AssignLiteral("about:stop-detector");
894 return NS_OK;
897 NS_IMETHODIMP
898 StopDetector::IsPending(bool* aRetVal) {
899 *aRetVal = true;
900 return NS_OK;
903 NS_IMETHODIMP
904 StopDetector::GetStatus(nsresult* aStatus) {
905 *aStatus = NS_OK;
906 return NS_OK;
909 NS_IMETHODIMP StopDetector::SetCanceledReason(const nsACString& aReason) {
910 return SetCanceledReasonImpl(aReason);
913 NS_IMETHODIMP StopDetector::GetCanceledReason(nsACString& aReason) {
914 return GetCanceledReasonImpl(aReason);
917 NS_IMETHODIMP StopDetector::CancelWithReason(nsresult aStatus,
918 const nsACString& aReason) {
919 return CancelWithReasonImpl(aStatus, aReason);
922 NS_IMETHODIMP
923 StopDetector::Cancel(nsresult aStatus) {
924 mCanceled = true;
925 return NS_OK;
928 NS_IMETHODIMP
929 StopDetector::Suspend(void) { return NS_OK; }
930 NS_IMETHODIMP
931 StopDetector::Resume(void) { return NS_OK; }
933 NS_IMETHODIMP
934 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
935 *aLoadGroup = nullptr;
936 return NS_OK;
939 NS_IMETHODIMP
940 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
942 NS_IMETHODIMP
943 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
944 *aLoadFlags = nsIRequest::LOAD_NORMAL;
945 return NS_OK;
948 NS_IMETHODIMP
949 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
950 return GetTRRModeImpl(aTRRMode);
953 NS_IMETHODIMP
954 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
955 return SetTRRModeImpl(aTRRMode);
958 NS_IMETHODIMP
959 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
961 bool nsDocShell::MaybeHandleSubframeHistory(
962 nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
963 // First, verify if this is a subframe.
964 // Note, it is ok to rely on docshell here and not browsing context since when
965 // an iframe is created, it has first in-process docshell.
966 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
967 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
968 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
970 if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
971 if (mBrowsingContext && mBrowsingContext->IsTop()) {
972 // This is the root docshell. If we got here while
973 // executing an onLoad Handler,this load will not go
974 // into session history.
975 // XXX Why is this code in a method which deals with iframes!
976 if (aLoadState->IsFormSubmission()) {
977 #ifdef DEBUG
978 if (!mEODForCurrentDocument) {
979 const MaybeDiscarded<BrowsingContext>& targetBC =
980 aLoadState->TargetBrowsingContext();
981 MOZ_ASSERT_IF(GetBrowsingContext() == targetBC.get(),
982 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
984 #endif
985 } else {
986 bool inOnLoadHandler = false;
987 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
988 if (inOnLoadHandler) {
989 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
993 return false;
996 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
997 * loaded through a history mechanism, then get the SH entry for the child
998 * from the parent. This is done to restore frameset navigation while going
999 * back/forward. If the parent was loaded through any other loadType, set the
1000 * child's loadType too accordingly, so that session history does not get
1001 * confused.
1004 // Get the parent's load type
1005 uint32_t parentLoadType;
1006 parentDS->GetLoadType(&parentLoadType);
1008 if (!aContinueHandlingSubframeHistory) {
1009 if (mozilla::SessionHistoryInParent()) {
1010 if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
1011 !GetCreatedDynamically()) {
1012 if (XRE_IsContentProcess()) {
1013 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
1014 nsCOMPtr<nsILoadGroup> loadGroup;
1015 GetLoadGroup(getter_AddRefs(loadGroup));
1016 if (contentChild && loadGroup && !mCheckingSessionHistory) {
1017 RefPtr<Document> parentDoc = parentDS->GetDocument();
1018 parentDoc->BlockOnload();
1019 RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
1020 Maybe<uint64_t> currentLoadIdentifier =
1021 mBrowsingContext->GetCurrentLoadIdentifier();
1022 RefPtr<nsDocShellLoadState> loadState = aLoadState;
1023 bool isNavigating = mIsNavigating;
1024 RefPtr<StopDetector> stopDetector = new StopDetector();
1025 loadGroup->AddRequest(stopDetector, nullptr);
1026 // Need to set mCheckingSessionHistory so that
1027 // GetIsAttemptingToNavigate() returns true.
1028 mCheckingSessionHistory = true;
1030 auto resolve =
1031 [currentLoadIdentifier, browsingContext, parentDoc, loadState,
1032 isNavigating, loadGroup, stopDetector](
1033 mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
1034 RefPtr<nsDocShell> docShell =
1035 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1036 auto unblockParent = MakeScopeExit(
1037 [loadGroup, stopDetector, parentDoc, docShell]() {
1038 if (docShell) {
1039 docShell->mCheckingSessionHistory = false;
1041 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1042 parentDoc->UnblockOnload(false);
1045 if (!docShell || !docShell->mCheckingSessionHistory) {
1046 return;
1049 if (stopDetector->Canceled()) {
1050 return;
1052 if (currentLoadIdentifier ==
1053 browsingContext->GetCurrentLoadIdentifier() &&
1054 aResult.isSome()) {
1055 loadState->SetLoadingSessionHistoryInfo(aResult.value());
1056 // This is an initial subframe load from the session
1057 // history, index doesn't need to be updated.
1058 loadState->SetLoadIsFromSessionHistory(0, false);
1061 // We got the results back from the parent process, call
1062 // LoadURI again with the possibly updated data.
1063 docShell->LoadURI(loadState, isNavigating, true);
1065 auto reject = [loadGroup, stopDetector, browsingContext,
1066 parentDoc](mozilla::ipc::ResponseRejectReason) {
1067 RefPtr<nsDocShell> docShell =
1068 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1069 if (docShell) {
1070 docShell->mCheckingSessionHistory = false;
1072 // In practise reject shouldn't be called ever.
1073 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1074 parentDoc->UnblockOnload(false);
1076 contentChild->SendGetLoadingSessionHistoryInfoFromParent(
1077 mBrowsingContext, std::move(resolve), std::move(reject));
1078 return true;
1080 } else {
1081 Maybe<LoadingSessionHistoryInfo> info;
1082 mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
1083 info);
1084 if (info.isSome()) {
1085 aLoadState->SetLoadingSessionHistoryInfo(info.value());
1086 // This is an initial subframe load from the session
1087 // history, index doesn't need to be updated.
1088 aLoadState->SetLoadIsFromSessionHistory(0, false);
1092 } else {
1093 // Get the ShEntry for the child from the parent
1094 nsCOMPtr<nsISHEntry> currentSH;
1095 bool oshe = false;
1096 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1097 bool dynamicallyAddedChild = GetCreatedDynamically();
1099 if (!dynamicallyAddedChild && !oshe && currentSH) {
1100 // Only use the old SHEntry, if we're sure enough that
1101 // it wasn't originally for some other frame.
1102 nsCOMPtr<nsISHEntry> shEntry;
1103 currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
1104 mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry));
1105 if (shEntry) {
1106 aLoadState->SetSHEntry(shEntry);
1112 // Make some decisions on the child frame's loadType based on the
1113 // parent's loadType, if the subframe hasn't loaded anything into it.
1115 // In some cases privileged scripts may try to get the DOMWindow
1116 // reference of this docshell before the loading starts, causing the
1117 // initial about:blank content viewer being created and mCurrentURI being
1118 // set. To handle this case we check if mCurrentURI is about:blank and
1119 // currentSHEntry is null.
1120 bool oshe = false;
1121 nsCOMPtr<nsISHEntry> currentChildEntry;
1122 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
1124 if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry ||
1125 mLoadingEntry || mActiveEntry)) {
1126 // This is a pre-existing subframe. If
1127 // 1. The load of this frame was not originally initiated by session
1128 // history directly (i.e. (!shEntry) condition succeeded, but it can
1129 // still be a history load on parent which causes this frame being
1130 // loaded), which we checked with the above assert, and
1131 // 2. mCurrentURI is not null, nor the initial about:blank,
1132 // it is possible that a parent's onLoadHandler or even self's
1133 // onLoadHandler is loading a new page in this child. Check parent's and
1134 // self's busy flag and if it is set, we don't want this onLoadHandler
1135 // load to get in to session history.
1136 BusyFlags parentBusy = parentDS->GetBusyFlags();
1137 BusyFlags selfBusy = GetBusyFlags();
1139 if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
1140 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1141 aLoadState->ClearLoadIsFromSessionHistory();
1143 return false;
1146 // This is a newly created frame. Check for exception cases first.
1147 // By default the subframe will inherit the parent's loadType.
1148 if (aLoadState->LoadIsFromSessionHistory() &&
1149 (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) {
1150 // The parent was loaded normally. In this case, this *brand new*
1151 // child really shouldn't have a SHEntry. If it does, it could be
1152 // because the parent is replacing an existing frame with a new frame,
1153 // in the onLoadHandler. We don't want this url to get into session
1154 // history. Clear off shEntry, and set load type to
1155 // LOAD_BYPASS_HISTORY.
1156 bool inOnLoadHandler = false;
1157 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1158 if (inOnLoadHandler) {
1159 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1160 aLoadState->ClearLoadIsFromSessionHistory();
1162 } else if (parentLoadType == LOAD_REFRESH) {
1163 // Clear shEntry. For refresh loads, we have to load
1164 // what comes through the pipe, not what's in history.
1165 aLoadState->ClearLoadIsFromSessionHistory();
1166 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1167 (aLoadState->LoadIsFromSessionHistory() &&
1168 ((parentLoadType & LOAD_CMD_HISTORY) ||
1169 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1170 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
1171 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
1172 (parentLoadType ==
1173 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
1174 // If the parent url, bypassed history or was loaded from
1175 // history, pass on the parent's loadType to the new child
1176 // frame too, so that the child frame will also
1177 // avoid getting into history.
1178 aLoadState->SetLoadType(parentLoadType);
1179 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1180 // If the parent document is an error page, we don't
1181 // want to update global/session history. However,
1182 // this child frame is not an error page.
1183 aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
1184 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1185 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1186 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1187 // the new frame should inherit the parent's load type so that it also
1188 // bypasses the cache and/or proxy
1189 aLoadState->SetLoadType(parentLoadType);
1192 return false;
1196 * Reset state to a new content model within the current document and the
1197 * document viewer. Called by the document before initiating an out of band
1198 * document.write().
1200 NS_IMETHODIMP
1201 nsDocShell::PrepareForNewContentModel() {
1202 // Clear out our form control state, because the state of controls
1203 // in the pre-open() document should not affect the state of
1204 // controls that are now going to be written.
1205 SetLayoutHistoryState(nullptr);
1206 mEODForCurrentDocument = false;
1207 return NS_OK;
1210 NS_IMETHODIMP
1211 nsDocShell::FirePageHideNotification(bool aIsUnload) {
1212 FirePageHideNotificationInternal(aIsUnload, false);
1213 return NS_OK;
1216 void nsDocShell::FirePageHideNotificationInternal(
1217 bool aIsUnload, bool aSkipCheckingDynEntries) {
1218 if (mContentViewer && !mFiredUnloadEvent) {
1219 // Keep an explicit reference since calling PageHide could release
1220 // mContentViewer
1221 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1222 mFiredUnloadEvent = true;
1224 if (mTiming) {
1225 mTiming->NotifyUnloadEventStart();
1228 contentViewer->PageHide(aIsUnload);
1230 if (mTiming) {
1231 mTiming->NotifyUnloadEventEnd();
1234 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1235 uint32_t n = mChildList.Length();
1236 kids.SetCapacity(n);
1237 for (uint32_t i = 0; i < n; i++) {
1238 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1241 n = kids.Length();
1242 for (uint32_t i = 0; i < n; ++i) {
1243 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1244 if (child) {
1245 // Skip checking dynamic subframe entries in our children.
1246 child->FirePageHideNotificationInternal(aIsUnload, true);
1250 // If the document is unloading, remove all dynamic subframe entries.
1251 if (aIsUnload && !aSkipCheckingDynEntries) {
1252 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
1253 if (rootSH) {
1254 MOZ_LOG(
1255 gSHLog, LogLevel::Debug,
1256 ("nsDocShell %p unloading, remove dynamic subframe entries", this));
1257 if (mozilla::SessionHistoryInParent()) {
1258 if (mActiveEntry) {
1259 mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry();
1261 MOZ_LOG(gSHLog, LogLevel::Debug,
1262 ("nsDocShell %p unloading, no active entries", this));
1263 } else if (mOSHE) {
1264 int32_t index = rootSH->Index();
1265 rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
1270 // Now make sure our editor, if any, is detached before we go
1271 // any farther.
1272 DetachEditorFromWindow();
1276 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) {
1277 MOZ_ASSERT(mozilla::BFCacheInParent());
1279 if (!mScriptGlobal) {
1280 return;
1283 if (RefPtr<nsGlobalWindowInner> inner =
1284 nsGlobalWindowInner::Cast(mScriptGlobal->GetCurrentInnerWindow())) {
1285 if (aThaw) {
1286 inner->Thaw(false);
1287 } else {
1288 inner->Freeze(false);
1293 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
1294 MOZ_ASSERT(mozilla::BFCacheInParent());
1296 if (!mContentViewer) {
1297 return;
1300 // Emulate what non-SHIP BFCache does too. In pageshow case
1301 // add and remove a request and before that call SetCurrentURI to get
1302 // the location change notification.
1303 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
1304 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1305 if (aShow) {
1306 contentViewer->SetIsHidden(false);
1307 mRefreshURIList = std::move(mBFCachedRefreshURIList);
1308 RefreshURIFromQueue();
1309 mFiredUnloadEvent = false;
1310 RefPtr<Document> doc = contentViewer->GetDocument();
1311 if (doc) {
1312 doc->NotifyActivityChanged();
1313 nsCOMPtr<nsPIDOMWindowInner> inner =
1314 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
1315 if (mBrowsingContext->IsTop()) {
1316 doc->NotifyPossibleTitleChange(false);
1317 if (inner) {
1318 // Now that we have found the inner window of the page restored
1319 // from the history, we have to make sure that
1320 // performance.navigation.type is 2.
1321 // Traditionally this type change has been done to the top level page
1322 // only.
1323 Performance* performance = inner->GetPerformance();
1324 if (performance) {
1325 performance->GetDOMTiming()->NotifyRestoreStart();
1330 nsCOMPtr<nsIChannel> channel = doc->GetChannel();
1331 if (channel) {
1332 SetLoadType(LOAD_HISTORY);
1333 mEODForCurrentDocument = false;
1334 mIsRestoringDocument = true;
1335 mLoadGroup->AddRequest(channel, nullptr);
1336 SetCurrentURI(doc->GetDocumentURI(), channel,
1337 /* aFireOnLocationChange */ true,
1338 /* aIsInitialAboutBlank */ false,
1339 /* aLocationFlags */ 0);
1340 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
1341 mIsRestoringDocument = false;
1343 RefPtr<PresShell> presShell = GetPresShell();
1344 if (presShell) {
1345 presShell->Thaw(false);
1348 if (inner) {
1349 inner->FireDelayedDOMEvents(false);
1352 } else if (!mFiredUnloadEvent) {
1353 // XXXBFCache check again that the page can enter bfcache.
1354 // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
1356 if (mRefreshURIList) {
1357 RefreshURIToQueue();
1358 mBFCachedRefreshURIList = std::move(mRefreshURIList);
1359 } else {
1360 // If Stop was called, the list was moved to mSavedRefreshURIList after
1361 // calling SuspendRefreshURIs, which calls RefreshURIToQueue.
1362 mBFCachedRefreshURIList = std::move(mSavedRefreshURIList);
1365 mFiredUnloadEvent = true;
1366 contentViewer->PageHide(false);
1368 RefPtr<PresShell> presShell = GetPresShell();
1369 if (presShell) {
1370 presShell->Freeze(false);
1375 nsresult nsDocShell::Dispatch(TaskCategory aCategory,
1376 already_AddRefed<nsIRunnable>&& aRunnable) {
1377 nsCOMPtr<nsIRunnable> runnable(aRunnable);
1378 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
1379 if (NS_WARN_IF(!win)) {
1380 // Window should only be unavailable after destroyed.
1381 MOZ_ASSERT(mIsBeingDestroyed);
1382 return NS_ERROR_FAILURE;
1385 if (win->GetDocGroup()) {
1386 return win->GetDocGroup()->Dispatch(aCategory, runnable.forget());
1389 return SchedulerGroup::Dispatch(aCategory, runnable.forget());
1392 NS_IMETHODIMP
1393 nsDocShell::DispatchLocationChangeEvent() {
1394 return Dispatch(
1395 TaskCategory::Other,
1396 NewRunnableMethod("nsDocShell::FireDummyOnLocationChange", this,
1397 &nsDocShell::FireDummyOnLocationChange));
1400 NS_IMETHODIMP
1401 nsDocShell::StartDelayedAutoplayMediaComponents() {
1402 RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow();
1403 if (outerWindow) {
1404 outerWindow->ActivateMediaComponents();
1406 return NS_OK;
1409 bool nsDocShell::MaybeInitTiming() {
1410 if (mTiming && !mBlankTiming) {
1411 return false;
1414 bool canBeReset = false;
1416 if (mScriptGlobal && mBlankTiming) {
1417 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
1418 if (innerWin && innerWin->GetPerformance()) {
1419 mTiming = innerWin->GetPerformance()->GetDOMTiming();
1420 mBlankTiming = false;
1424 if (!mTiming) {
1425 mTiming = new nsDOMNavigationTiming(this);
1426 canBeReset = true;
1429 mTiming->NotifyNavigationStart(
1430 mBrowsingContext->IsActive()
1431 ? nsDOMNavigationTiming::DocShellState::eActive
1432 : nsDOMNavigationTiming::DocShellState::eInactive);
1434 return canBeReset;
1437 void nsDocShell::MaybeResetInitTiming(bool aReset) {
1438 if (aReset) {
1439 mTiming = nullptr;
1443 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
1444 return mTiming;
1447 nsPresContext* nsDocShell::GetEldestPresContext() {
1448 nsIContentViewer* viewer = mContentViewer;
1449 while (viewer) {
1450 nsIContentViewer* prevViewer = viewer->GetPreviousViewer();
1451 if (!prevViewer) {
1452 return viewer->GetPresContext();
1454 viewer = prevViewer;
1457 return nullptr;
1460 nsPresContext* nsDocShell::GetPresContext() {
1461 if (!mContentViewer) {
1462 return nullptr;
1465 return mContentViewer->GetPresContext();
1468 PresShell* nsDocShell::GetPresShell() {
1469 nsPresContext* presContext = GetPresContext();
1470 return presContext ? presContext->GetPresShell() : nullptr;
1473 PresShell* nsDocShell::GetEldestPresShell() {
1474 nsPresContext* presContext = GetEldestPresContext();
1476 if (presContext) {
1477 return presContext->GetPresShell();
1480 return nullptr;
1483 NS_IMETHODIMP
1484 nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer) {
1485 NS_ENSURE_ARG_POINTER(aContentViewer);
1487 *aContentViewer = mContentViewer;
1488 NS_IF_ADDREF(*aContentViewer);
1489 return NS_OK;
1492 NS_IMETHODIMP
1493 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) {
1494 *aWindowID = mContentWindowID;
1495 return NS_OK;
1498 NS_IMETHODIMP
1499 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) {
1500 mChromeEventHandler = aChromeEventHandler;
1502 if (mScriptGlobal) {
1503 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1506 return NS_OK;
1509 NS_IMETHODIMP
1510 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) {
1511 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1512 RefPtr<EventTarget> handler = mChromeEventHandler;
1513 handler.forget(aChromeEventHandler);
1514 return NS_OK;
1517 NS_IMETHODIMP
1518 nsDocShell::SetCurrentURIForSessionStore(nsIURI* aURI) {
1519 // Note that securityUI will set STATE_IS_INSECURE, even if
1520 // the scheme of |aURI| is "https".
1521 SetCurrentURI(aURI, nullptr,
1522 /* aFireOnLocationChange */
1523 true,
1524 /* aIsInitialAboutBlank */
1525 false,
1526 /* aLocationFlags */
1527 nsIWebProgressListener::LOCATION_CHANGE_SESSION_STORE);
1528 return NS_OK;
1531 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1532 bool aFireOnLocationChange,
1533 bool aIsInitialAboutBlank,
1534 uint32_t aLocationFlags) {
1535 MOZ_ASSERT(!mIsBeingDestroyed);
1537 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1538 ("DOCSHELL %p SetCurrentURI %s\n", this,
1539 aURI ? aURI->GetSpecOrDefault().get() : ""));
1541 // We don't want to send a location change when we're displaying an error
1542 // page, and we don't want to change our idea of "current URI" either
1543 if (mLoadType == LOAD_ERROR_PAGE) {
1544 return false;
1547 bool uriIsEqual = false;
1548 if (!mCurrentURI || !aURI ||
1549 NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
1550 mTitleValidForCurrentURI = false;
1553 SetCurrentURIInternal(aURI);
1555 #ifdef DEBUG
1556 mLastOpenedURI = aURI;
1557 #endif
1559 if (!NS_IsAboutBlank(mCurrentURI)) {
1560 mHasLoadedNonBlankURI = true;
1563 // Don't fire onLocationChange when creating a subframe's initial about:blank
1564 // document, as this can happen when it's not safe for us to run script.
1565 if (aIsInitialAboutBlank && !mHasLoadedNonBlankURI &&
1566 !mBrowsingContext->IsTop()) {
1567 MOZ_ASSERT(!aRequest && aLocationFlags == 0);
1568 return false;
1571 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1573 if (aFireOnLocationChange) {
1574 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1576 return !aFireOnLocationChange;
1579 void nsDocShell::SetCurrentURIInternal(nsIURI* aURI) {
1580 mCurrentURI = aURI;
1581 if (mBrowsingContext) {
1582 mBrowsingContext->ClearCachedValuesOfLocations();
1586 NS_IMETHODIMP
1587 nsDocShell::GetCharset(nsACString& aCharset) {
1588 aCharset.Truncate();
1590 PresShell* presShell = GetPresShell();
1591 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1592 Document* doc = presShell->GetDocument();
1593 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1594 doc->GetDocumentCharacterSet()->Name(aCharset);
1595 return NS_OK;
1598 NS_IMETHODIMP
1599 nsDocShell::ForceEncodingDetection() {
1600 nsCOMPtr<nsIContentViewer> viewer;
1601 GetContentViewer(getter_AddRefs(viewer));
1602 if (!viewer) {
1603 return NS_OK;
1606 Document* doc = viewer->GetDocument();
1607 if (!doc || doc->WillIgnoreCharsetOverride()) {
1608 return NS_OK;
1611 mForcedAutodetection = true;
1613 nsIURI* url = doc->GetOriginalURI();
1614 bool isFileURL = url && SchemeIsFile(url);
1616 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1617 auto encoding = doc->GetDocumentCharacterSet();
1618 // AsHTMLDocument is valid, because we called
1619 // WillIgnoreCharsetOverride() above.
1620 if (doc->AsHTMLDocument()->IsPlainText()) {
1621 switch (charsetSource) {
1622 case kCharsetFromInitialAutoDetectionASCII:
1623 // Deliberately no final version
1624 LOGCHARSETMENU(("TEXT:UnlabeledAscii"));
1625 Telemetry::AccumulateCategorical(
1626 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledAscii);
1627 break;
1628 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1629 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1630 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1631 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1632 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1633 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1634 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8"));
1635 Telemetry::AccumulateCategorical(
1636 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1637 UnlabeledNonUtf8);
1638 break;
1639 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1640 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1641 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1642 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD"));
1643 Telemetry::AccumulateCategorical(
1644 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1645 UnlabeledNonUtf8TLD);
1646 break;
1647 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1648 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1649 LOGCHARSETMENU(("TEXT:UnlabeledUtf8"));
1650 Telemetry::AccumulateCategorical(
1651 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledUtf8);
1652 break;
1653 case kCharsetFromChannel:
1654 if (encoding == UTF_8_ENCODING) {
1655 LOGCHARSETMENU(("TEXT:ChannelUtf8"));
1656 Telemetry::AccumulateCategorical(
1657 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::ChannelUtf8);
1658 } else {
1659 LOGCHARSETMENU(("TEXT:ChannelNonUtf8"));
1660 Telemetry::AccumulateCategorical(
1661 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1662 ChannelNonUtf8);
1664 break;
1665 default:
1666 LOGCHARSETMENU(("TEXT:Bug"));
1667 Telemetry::AccumulateCategorical(
1668 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::Bug);
1669 break;
1671 } else {
1672 switch (charsetSource) {
1673 case kCharsetFromInitialAutoDetectionASCII:
1674 // Deliberately no final version
1675 LOGCHARSETMENU(("HTML:UnlabeledAscii"));
1676 Telemetry::AccumulateCategorical(
1677 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledAscii);
1678 break;
1679 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1680 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1681 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1682 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1683 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1684 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1685 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8"));
1686 Telemetry::AccumulateCategorical(
1687 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1688 UnlabeledNonUtf8);
1689 break;
1690 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1691 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1692 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1693 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD"));
1694 Telemetry::AccumulateCategorical(
1695 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1696 UnlabeledNonUtf8TLD);
1697 break;
1698 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1699 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1700 LOGCHARSETMENU(("HTML:UnlabeledUtf8"));
1701 Telemetry::AccumulateCategorical(
1702 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledUtf8);
1703 break;
1704 case kCharsetFromChannel:
1705 if (encoding == UTF_8_ENCODING) {
1706 LOGCHARSETMENU(("HTML:ChannelUtf8"));
1707 Telemetry::AccumulateCategorical(
1708 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::ChannelUtf8);
1709 } else {
1710 LOGCHARSETMENU(("HTML:ChannelNonUtf8"));
1711 Telemetry::AccumulateCategorical(
1712 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1713 ChannelNonUtf8);
1715 break;
1716 case kCharsetFromXmlDeclaration:
1717 case kCharsetFromMetaTag:
1718 if (isFileURL) {
1719 LOGCHARSETMENU(("HTML:LocalLabeled"));
1720 Telemetry::AccumulateCategorical(
1721 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::LocalLabeled);
1722 } else if (encoding == UTF_8_ENCODING) {
1723 LOGCHARSETMENU(("HTML:MetaUtf8"));
1724 Telemetry::AccumulateCategorical(
1725 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::InternalUtf8);
1726 } else {
1727 LOGCHARSETMENU(("HTML:MetaNonUtf8"));
1728 Telemetry::AccumulateCategorical(
1729 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1730 InternalNonUtf8);
1732 break;
1733 default:
1734 LOGCHARSETMENU(("HTML:Bug"));
1735 Telemetry::AccumulateCategorical(
1736 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::Bug);
1737 break;
1740 return NS_OK;
1743 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1744 int32_t aCharsetSource,
1745 nsIPrincipal* aPrincipal) {
1746 mParentCharset = aCharset;
1747 mParentCharsetSource = aCharsetSource;
1748 mParentCharsetPrincipal = aPrincipal;
1751 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1752 int32_t* aCharsetSource,
1753 nsIPrincipal** aPrincipal) {
1754 aCharset = mParentCharset;
1755 *aCharsetSource = mParentCharsetSource;
1756 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1759 NS_IMETHODIMP
1760 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
1761 MOZ_ASSERT(aPromise);
1763 ErrorResult rv;
1764 RefPtr<Document> doc(GetDocument());
1765 RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
1766 if (NS_WARN_IF(rv.Failed())) {
1767 return rv.StealNSResult();
1770 // Retrieve the document's content blocking events from the parent process.
1771 RefPtr<Document::GetContentBlockingEventsPromise> promise =
1772 doc->GetContentBlockingEvents();
1773 if (promise) {
1774 promise->Then(
1775 GetCurrentSerialEventTarget(), __func__,
1776 [retPromise](const Document::GetContentBlockingEventsPromise::
1777 ResolveOrRejectValue& aValue) {
1778 if (aValue.IsResolve()) {
1779 bool has = aValue.ResolveValue() &
1780 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
1781 retPromise->MaybeResolve(has);
1782 } else {
1783 retPromise->MaybeResolve(false);
1786 } else {
1787 retPromise->MaybeResolve(false);
1790 retPromise.forget(aPromise);
1791 return NS_OK;
1794 NS_IMETHODIMP
1795 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
1796 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1798 *aAllowPlugins = mBrowsingContext->GetAllowPlugins();
1799 return NS_OK;
1802 NS_IMETHODIMP
1803 nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
1804 // XXX should enable or disable a plugin host
1805 return mBrowsingContext->SetAllowPlugins(aAllowPlugins);
1808 NS_IMETHODIMP
1809 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
1810 MOZ_ASSERT(aEnabled);
1811 *aEnabled = mCSSErrorReportingEnabled;
1812 return NS_OK;
1815 NS_IMETHODIMP
1816 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1817 mCSSErrorReportingEnabled = aEnabled;
1818 return NS_OK;
1821 NS_IMETHODIMP
1822 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1823 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1824 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1827 void nsDocShell::NotifyPrivateBrowsingChanged() {
1828 MOZ_ASSERT(!mIsBeingDestroyed);
1830 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1831 while (iter.HasMore()) {
1832 nsWeakPtr ref = iter.GetNext();
1833 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1834 if (!obs) {
1835 iter.Remove();
1836 } else {
1837 obs->PrivateModeChanged(UsePrivateBrowsing());
1842 NS_IMETHODIMP
1843 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1844 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1847 NS_IMETHODIMP
1848 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1849 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1852 NS_IMETHODIMP
1853 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1854 NS_ENSURE_ARG_POINTER(aResult);
1856 *aResult = mHasLoadedNonBlankURI;
1857 return NS_OK;
1860 NS_IMETHODIMP
1861 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1862 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1863 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1866 NS_IMETHODIMP
1867 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1868 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1871 NS_IMETHODIMP
1872 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1873 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1874 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1877 NS_IMETHODIMP
1878 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1879 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1882 NS_IMETHODIMP
1883 nsDocShell::AddWeakPrivacyTransitionObserver(
1884 nsIPrivacyTransitionObserver* aObserver) {
1885 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1886 if (!weakObs) {
1887 return NS_ERROR_NOT_AVAILABLE;
1889 mPrivacyObservers.AppendElement(weakObs);
1890 return NS_OK;
1893 NS_IMETHODIMP
1894 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1895 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1896 if (!weakObs) {
1897 return NS_ERROR_FAILURE;
1899 mReflowObservers.AppendElement(weakObs);
1900 return NS_OK;
1903 NS_IMETHODIMP
1904 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1905 nsWeakPtr obs = do_GetWeakReference(aObserver);
1906 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1909 NS_IMETHODIMP
1910 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1911 DOMHighResTimeStamp aStart,
1912 DOMHighResTimeStamp aEnd) {
1913 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1914 while (iter.HasMore()) {
1915 nsWeakPtr ref = iter.GetNext();
1916 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1917 if (!obs) {
1918 iter.Remove();
1919 } else if (aInterruptible) {
1920 obs->ReflowInterruptible(aStart, aEnd);
1921 } else {
1922 obs->Reflow(aStart, aEnd);
1925 return NS_OK;
1928 NS_IMETHODIMP
1929 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1930 NS_ENSURE_ARG_POINTER(aReturn);
1932 *aReturn = mAllowMetaRedirects;
1933 return NS_OK;
1936 NS_IMETHODIMP
1937 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1938 mAllowMetaRedirects = aValue;
1939 return NS_OK;
1942 NS_IMETHODIMP
1943 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1944 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1946 *aAllowSubframes = mAllowSubframes;
1947 return NS_OK;
1950 NS_IMETHODIMP
1951 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1952 mAllowSubframes = aAllowSubframes;
1953 return NS_OK;
1956 NS_IMETHODIMP
1957 nsDocShell::GetAllowImages(bool* aAllowImages) {
1958 NS_ENSURE_ARG_POINTER(aAllowImages);
1960 *aAllowImages = mAllowImages;
1961 return NS_OK;
1964 NS_IMETHODIMP
1965 nsDocShell::SetAllowImages(bool aAllowImages) {
1966 mAllowImages = aAllowImages;
1967 return NS_OK;
1970 NS_IMETHODIMP
1971 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1972 *aAllowMedia = mAllowMedia;
1973 return NS_OK;
1976 NS_IMETHODIMP
1977 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1978 mAllowMedia = aAllowMedia;
1980 // Mute or unmute audio contexts attached to the inner window.
1981 if (mScriptGlobal) {
1982 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1983 if (aAllowMedia) {
1984 innerWin->UnmuteAudioContexts();
1985 } else {
1986 innerWin->MuteAudioContexts();
1991 return NS_OK;
1994 NS_IMETHODIMP
1995 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1996 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1997 return NS_OK;
2000 NS_IMETHODIMP
2001 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
2002 mAllowDNSPrefetch = aAllowDNSPrefetch;
2003 return NS_OK;
2006 NS_IMETHODIMP
2007 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
2008 *aAllowWindowControl = mAllowWindowControl;
2009 return NS_OK;
2012 NS_IMETHODIMP
2013 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
2014 mAllowWindowControl = aAllowWindowControl;
2015 return NS_OK;
2018 NS_IMETHODIMP
2019 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
2020 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
2021 return NS_OK;
2024 NS_IMETHODIMP
2025 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
2026 BrowsingContext::Transaction txn;
2027 txn.SetAllowContentRetargeting(aAllowContentRetargeting);
2028 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
2029 return txn.Commit(mBrowsingContext);
2032 NS_IMETHODIMP
2033 nsDocShell::GetAllowContentRetargetingOnChildren(
2034 bool* aAllowContentRetargetingOnChildren) {
2035 *aAllowContentRetargetingOnChildren =
2036 mBrowsingContext->GetAllowContentRetargetingOnChildren();
2037 return NS_OK;
2040 NS_IMETHODIMP
2041 nsDocShell::SetAllowContentRetargetingOnChildren(
2042 bool aAllowContentRetargetingOnChildren) {
2043 return mBrowsingContext->SetAllowContentRetargetingOnChildren(
2044 aAllowContentRetargetingOnChildren);
2047 NS_IMETHODIMP
2048 nsDocShell::GetMayEnableCharacterEncodingMenu(
2049 bool* aMayEnableCharacterEncodingMenu) {
2050 *aMayEnableCharacterEncodingMenu = false;
2051 if (!mContentViewer) {
2052 return NS_OK;
2054 Document* doc = mContentViewer->GetDocument();
2055 if (!doc) {
2056 return NS_OK;
2058 if (doc->WillIgnoreCharsetOverride()) {
2059 return NS_OK;
2062 *aMayEnableCharacterEncodingMenu = true;
2063 return NS_OK;
2066 NS_IMETHODIMP
2067 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
2068 DocShellEnumeratorDirection aDirection,
2069 nsTArray<RefPtr<nsIDocShell>>& aResult) {
2070 aResult.Clear();
2072 nsDocShellEnumerator docShellEnum(
2073 (aDirection == ENUMERATE_FORWARDS)
2074 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2075 : nsDocShellEnumerator::EnumerationDirection::Backwards,
2076 aItemType, *this);
2078 nsresult rv = docShellEnum.BuildDocShellArray(aResult);
2079 if (NS_FAILED(rv)) {
2080 return rv;
2083 return NS_OK;
2086 NS_IMETHODIMP
2087 nsDocShell::GetAppType(AppType* aAppType) {
2088 *aAppType = mAppType;
2089 return NS_OK;
2092 NS_IMETHODIMP
2093 nsDocShell::SetAppType(AppType aAppType) {
2094 mAppType = aAppType;
2095 return NS_OK;
2098 NS_IMETHODIMP
2099 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2100 *aAllowAuth = mAllowAuth;
2101 return NS_OK;
2104 NS_IMETHODIMP
2105 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2106 mAllowAuth = aAllowAuth;
2107 return NS_OK;
2110 NS_IMETHODIMP
2111 nsDocShell::GetZoom(float* aZoom) {
2112 NS_ENSURE_ARG_POINTER(aZoom);
2113 *aZoom = 1.0f;
2114 return NS_OK;
2117 NS_IMETHODIMP
2118 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2120 NS_IMETHODIMP
2121 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
2122 NS_ENSURE_ARG_POINTER(aBusyFlags);
2124 *aBusyFlags = mBusyFlags;
2125 return NS_OK;
2128 NS_IMETHODIMP
2129 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
2130 bool* aTookFocus) {
2131 NS_ENSURE_ARG_POINTER(aTookFocus);
2133 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2134 if (chromeFocus) {
2135 if (aForward) {
2136 *aTookFocus =
2137 NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2138 } else {
2139 *aTookFocus =
2140 NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2142 } else {
2143 *aTookFocus = false;
2146 return NS_OK;
2149 NS_IMETHODIMP
2150 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2151 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
2152 delegate.forget(aLoadURIDelegate);
2153 return NS_OK;
2156 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
2157 if (nsCOMPtr<nsILoadURIDelegate> result =
2158 do_QueryActor("LoadURIDelegate", GetDocument())) {
2159 return result.forget();
2162 return nullptr;
2165 NS_IMETHODIMP
2166 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2167 *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
2168 return NS_OK;
2171 NS_IMETHODIMP
2172 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2173 return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
2176 NS_IMETHODIMP
2177 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
2178 *aPreviousEntryIndex = mPreviousEntryIndex;
2179 return NS_OK;
2182 NS_IMETHODIMP
2183 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
2184 *aLoadedEntryIndex = mLoadedEntryIndex;
2185 return NS_OK;
2188 NS_IMETHODIMP
2189 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2190 // These indices are used for fastback cache eviction, to determine
2191 // which session history entries are candidates for content viewer
2192 // eviction. We need to adjust by the number of entries that we
2193 // just purged from history, so that we look at the right session history
2194 // entries during eviction.
2195 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
2196 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
2198 for (auto* child : mChildList.ForwardRange()) {
2199 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2200 if (shell) {
2201 shell->HistoryPurged(aNumEntries);
2205 return NS_OK;
2208 void nsDocShell::TriggerParentCheckDocShellIsEmpty() {
2209 if (RefPtr<nsDocShell> parent = GetInProcessParentDocshell()) {
2210 parent->DocLoaderIsEmpty(true);
2212 if (GetBrowsingContext()->IsContentSubframe() &&
2213 !GetBrowsingContext()->GetParent()->IsInProcess()) {
2214 if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
2215 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2216 EmbedderElementEventType::NoEvent);
2221 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2222 // These indices are used for fastback cache eviction, to determine
2223 // which session history entries are candidates for content viewer
2224 // eviction. We need to adjust by the number of entries that we
2225 // just purged from history, so that we look at the right session history
2226 // entries during eviction.
2227 if (aIndex == mPreviousEntryIndex) {
2228 mPreviousEntryIndex = -1;
2229 } else if (aIndex < mPreviousEntryIndex) {
2230 --mPreviousEntryIndex;
2232 if (mLoadedEntryIndex == aIndex) {
2233 mLoadedEntryIndex = 0;
2234 } else if (aIndex < mLoadedEntryIndex) {
2235 --mLoadedEntryIndex;
2238 for (auto* child : mChildList.ForwardRange()) {
2239 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2240 if (shell) {
2241 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2245 return NS_OK;
2248 NS_IMETHODIMP
2249 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) {
2250 bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
2251 if (currentValue == aValue) {
2252 return NS_OK;
2255 if (aValue) {
2256 MOZ_ASSERT(!TimelineConsumers::HasConsumer(this));
2257 TimelineConsumers::AddConsumer(this);
2258 MOZ_ASSERT(TimelineConsumers::HasConsumer(this));
2259 UseEntryScriptProfiling();
2260 } else {
2261 MOZ_ASSERT(TimelineConsumers::HasConsumer(this));
2262 TimelineConsumers::RemoveConsumer(this);
2263 MOZ_ASSERT(!TimelineConsumers::HasConsumer(this));
2264 UnuseEntryScriptProfiling();
2267 return NS_OK;
2270 NS_IMETHODIMP
2271 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) {
2272 *aValue = !!mObserved;
2273 return NS_OK;
2276 nsresult nsDocShell::PopProfileTimelineMarkers(
2277 JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
2278 nsTArray<dom::ProfileTimelineMarker> store;
2279 SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
2281 TimelineConsumers::PopMarkers(this, aCx, store);
2283 if (!ToJSValue(aCx, store, aOut)) {
2284 JS_ClearPendingException(aCx);
2285 return NS_ERROR_UNEXPECTED;
2288 return NS_OK;
2291 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2292 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2293 return NS_OK;
2296 NS_IMETHODIMP
2297 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2298 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2299 if (!aValue && mItemType == typeChrome && !parent) {
2300 // Window dragging is always allowed for top level
2301 // chrome docshells.
2302 return NS_ERROR_FAILURE;
2304 mWindowDraggingAllowed = aValue;
2305 return NS_OK;
2308 NS_IMETHODIMP
2309 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2310 // window dragging regions in CSS (-moz-window-drag:drag)
2311 // can be slow. Default behavior is to only allow it for
2312 // chrome top level windows.
2313 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2314 if (mItemType == typeChrome && !parent) {
2315 // Top level chrome window
2316 *aValue = true;
2317 } else {
2318 *aValue = mWindowDraggingAllowed;
2320 return NS_OK;
2323 NS_IMETHODIMP
2324 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2325 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2326 return NS_OK;
2329 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2330 if (mContentViewer) {
2331 Document* doc = mContentViewer->GetDocument();
2332 if (doc) {
2333 return doc->GetChannel();
2336 return nullptr;
2339 NS_IMETHODIMP
2340 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2341 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2342 if (!weakObs) {
2343 return NS_ERROR_FAILURE;
2345 mScrollObservers.AppendElement(weakObs);
2346 return NS_OK;
2349 NS_IMETHODIMP
2350 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2351 nsWeakPtr obs = do_GetWeakReference(aObserver);
2352 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2355 void nsDocShell::NotifyAsyncPanZoomStarted() {
2356 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2357 while (iter.HasMore()) {
2358 nsWeakPtr ref = iter.GetNext();
2359 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2360 if (obs) {
2361 obs->AsyncPanZoomStarted();
2362 } else {
2363 iter.Remove();
2368 void nsDocShell::NotifyAsyncPanZoomStopped() {
2369 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2370 while (iter.HasMore()) {
2371 nsWeakPtr ref = iter.GetNext();
2372 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2373 if (obs) {
2374 obs->AsyncPanZoomStopped();
2375 } else {
2376 iter.Remove();
2381 NS_IMETHODIMP
2382 nsDocShell::NotifyScrollObservers() {
2383 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2384 while (iter.HasMore()) {
2385 nsWeakPtr ref = iter.GetNext();
2386 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2387 if (obs) {
2388 obs->ScrollPositionChanged();
2389 } else {
2390 iter.Remove();
2393 return NS_OK;
2396 //*****************************************************************************
2397 // nsDocShell::nsIDocShellTreeItem
2398 //*****************************************************************************
2400 NS_IMETHODIMP
2401 nsDocShell::GetName(nsAString& aName) {
2402 aName = mBrowsingContext->Name();
2403 return NS_OK;
2406 NS_IMETHODIMP
2407 nsDocShell::SetName(const nsAString& aName) {
2408 return mBrowsingContext->SetName(aName);
2411 NS_IMETHODIMP
2412 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2413 NS_ENSURE_ARG_POINTER(aResult);
2414 *aResult = mBrowsingContext->NameEquals(aName);
2415 return NS_OK;
2418 NS_IMETHODIMP
2419 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2420 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2421 return NS_OK;
2424 NS_IMETHODIMP
2425 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2426 if (mWillChangeProcess) {
2427 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2428 return NS_ERROR_FAILURE;
2431 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2434 NS_IMETHODIMP
2435 nsDocShell::ClearCachedPlatform() {
2436 nsCOMPtr<nsPIDOMWindowInner> win =
2437 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2438 if (win) {
2439 Navigator* navigator = win->Navigator();
2440 if (navigator) {
2441 navigator->ClearPlatformCache();
2445 return NS_OK;
2448 NS_IMETHODIMP
2449 nsDocShell::ClearCachedUserAgent() {
2450 nsCOMPtr<nsPIDOMWindowInner> win =
2451 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2452 if (win) {
2453 Navigator* navigator = win->Navigator();
2454 if (navigator) {
2455 navigator->ClearUserAgentCache();
2459 return NS_OK;
2462 NS_IMETHODIMP
2463 nsDocShell::GetMetaViewportOverride(
2464 MetaViewportOverride* aMetaViewportOverride) {
2465 NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
2467 *aMetaViewportOverride = mMetaViewportOverride;
2468 return NS_OK;
2471 NS_IMETHODIMP
2472 nsDocShell::SetMetaViewportOverride(
2473 MetaViewportOverride aMetaViewportOverride) {
2474 // We don't have a way to verify this coming from Javascript, so this check is
2475 // still needed.
2476 if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
2477 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
2478 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
2479 return NS_ERROR_INVALID_ARG;
2482 mMetaViewportOverride = aMetaViewportOverride;
2484 // Inform our presShell that it needs to re-check its need for a viewport
2485 // override.
2486 if (RefPtr<PresShell> presShell = GetPresShell()) {
2487 presShell->MaybeRecreateMobileViewportManager(true);
2490 return NS_OK;
2493 /* virtual */
2494 int32_t nsDocShell::ItemType() { return mItemType; }
2496 NS_IMETHODIMP
2497 nsDocShell::GetItemType(int32_t* aItemType) {
2498 NS_ENSURE_ARG_POINTER(aItemType);
2500 MOZ_DIAGNOSTIC_ASSERT(
2501 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2502 *aItemType = mItemType;
2503 return NS_OK;
2506 NS_IMETHODIMP
2507 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2508 if (!mParent) {
2509 *aParent = nullptr;
2510 } else {
2511 CallQueryInterface(mParent, aParent);
2513 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2514 // don't want to throw; we just want to return null.
2515 return NS_OK;
2518 // With Fission, related nsDocShell objects may exist in a different process. In
2519 // that case, this method will return `nullptr`, despite a parent nsDocShell
2520 // object existing.
2522 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2523 // parent entry is not in the current process, and handle the case where the
2524 // parent nsDocShell is inaccessible.
2525 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2526 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2527 return docshell.forget().downcast<nsDocShell>();
2530 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2531 MOZ_ASSERT(!mIsBeingDestroyed);
2533 // If there is an existing document then there is no need to create
2534 // a client for a future initial about:blank document.
2535 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindow() &&
2536 mScriptGlobal->GetCurrentInnerWindow()->GetExtantDoc()) {
2537 MOZ_DIAGNOSTIC_ASSERT(
2538 mScriptGlobal->GetCurrentInnerWindow()->GetClientInfo().isSome());
2539 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2540 return;
2543 // Don't recreate the initial client source. We call this multiple times
2544 // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
2545 if (mInitialClientSource) {
2546 return;
2549 // Don't pre-allocate the client when we are sandboxed. The inherited
2550 // principal does not take sandboxing into account.
2551 // TODO: Refactor sandboxing principal code out so we can use it here.
2552 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2553 return;
2556 // We cannot get inherited foreign partitioned principal here. Instead, we
2557 // directly check which principal we want to inherit for the service worker.
2558 nsIPrincipal* principal =
2559 aPrincipal
2560 ? aPrincipal
2561 : GetInheritedPrincipal(
2562 false, StoragePrincipalHelper::
2563 ShouldUsePartitionPrincipalForServiceWorker(this));
2565 // Sometimes there is no principal available when we are called from
2566 // CreateAboutBlankContentViewer. For example, sometimes the principal
2567 // is only extracted from the load context after the document is created
2568 // in Document::ResetToURI(). Ideally we would do something similar
2569 // here, but for now lets just avoid the issue by not preallocating the
2570 // client.
2571 if (!principal) {
2572 return;
2575 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2576 if (!win) {
2577 return;
2580 mInitialClientSource = ClientManager::CreateSource(
2581 ClientType::Window, win->EventTargetFor(TaskCategory::Other), principal);
2582 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2584 // Mark the initial client as execution ready, but owned by the docshell.
2585 // If the client is actually used this will cause ClientSource to force
2586 // the creation of the initial about:blank by calling
2587 // nsDocShell::GetDocument().
2588 mInitialClientSource->DocShellExecutionReady(this);
2590 // Next, check to see if the parent is controlled.
2591 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2592 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2593 nsPIDOMWindowInner* parentInner =
2594 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2595 if (!parentInner) {
2596 return;
2599 nsCOMPtr<nsIURI> uri;
2600 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
2602 // We're done if there is no parent controller or if this docshell
2603 // is not permitted to control for some reason.
2604 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2605 if (controller.isNothing() ||
2606 !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2607 return;
2610 mInitialClientSource->InheritController(controller.ref());
2613 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2614 if (mInitialClientSource) {
2615 Maybe<ClientInfo> result;
2616 result.emplace(mInitialClientSource->Info());
2617 return result;
2620 nsPIDOMWindowInner* innerWindow =
2621 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2622 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2624 if (!doc || !doc->IsInitialDocument()) {
2625 return Maybe<ClientInfo>();
2628 return innerWindow->GetClientInfo();
2631 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2632 bool wasFrame = IsSubframe();
2634 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2635 NS_ENSURE_SUCCESS(rv, rv);
2637 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2638 if (wasFrame != IsSubframe() && priorityGroup) {
2639 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2642 // Curse ambiguous nsISupports inheritance!
2643 nsISupports* parent = GetAsSupports(aParent);
2645 // If parent is another docshell, we inherit all their flags for
2646 // allowing plugins, scripting etc.
2647 bool value;
2648 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2650 if (parentAsDocShell) {
2651 if (mAllowMetaRedirects &&
2652 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2653 SetAllowMetaRedirects(value);
2655 if (mAllowSubframes &&
2656 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2657 SetAllowSubframes(value);
2659 if (mAllowImages &&
2660 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2661 SetAllowImages(value);
2663 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2664 if (mAllowWindowControl &&
2665 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2666 SetAllowWindowControl(value);
2668 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2669 value = false;
2671 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2673 // We don't need to inherit metaViewportOverride, because the viewport
2674 // is only relevant for the outermost nsDocShell, not for any iframes
2675 // like this that might be embedded within it.
2678 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2679 if (parentURIListener) {
2680 mContentListener->SetParentContentListener(parentURIListener);
2683 return NS_OK;
2686 void nsDocShell::MaybeRestoreWindowName() {
2687 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2688 return;
2691 // We only restore window.name for the top-level content.
2692 if (!mBrowsingContext->IsTopContent()) {
2693 return;
2696 nsAutoString name;
2698 // Following implements https://html.spec.whatwg.org/#history-traversal:
2699 // Step 4.4. Check if the loading entry has a name.
2701 if (mLSHE) {
2702 mLSHE->GetName(name);
2705 if (mLoadingEntry) {
2706 name = mLoadingEntry->mInfo.GetName();
2709 if (name.IsEmpty()) {
2710 return;
2713 // Step 4.4.1. Set the name to the browsing context.
2714 Unused << mBrowsingContext->SetName(name);
2716 // Step 4.4.2. Clear the name of all entries that are contiguous and
2717 // same-origin with the loading entry.
2718 if (mLSHE) {
2719 nsSHistory::WalkContiguousEntries(
2720 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
2723 if (mLoadingEntry) {
2724 // Clear the name of the session entry in the child side. For parent side,
2725 // the clearing will be done when we commit the history to the parent.
2726 mLoadingEntry->mInfo.SetName(EmptyString());
2730 void nsDocShell::StoreWindowNameToSHEntries() {
2731 MOZ_ASSERT(mBrowsingContext->IsTopContent());
2733 nsAutoString name;
2734 mBrowsingContext->GetName(name);
2736 if (mOSHE) {
2737 nsSHistory::WalkContiguousEntries(
2738 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2741 if (mozilla::SessionHistoryInParent()) {
2742 if (XRE_IsParentProcess()) {
2743 SessionHistoryEntry* entry =
2744 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2745 if (entry) {
2746 nsSHistory::WalkContiguousEntries(
2747 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2749 } else {
2750 // Ask parent process to store the name in entries.
2751 mozilla::Unused
2752 << ContentChild::GetSingleton()
2753 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2754 mBrowsingContext, name);
2759 NS_IMETHODIMP
2760 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2761 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2762 *aParent = do_AddRef(parentBC->GetDocShell()).take();
2764 return NS_OK;
2767 NS_IMETHODIMP
2768 nsDocShell::GetSameTypeInProcessParentIgnoreBrowserBoundaries(
2769 nsIDocShell** aParent) {
2770 NS_ENSURE_ARG_POINTER(aParent);
2771 *aParent = nullptr;
2773 nsCOMPtr<nsIDocShellTreeItem> parent =
2774 do_QueryInterface(GetAsSupports(mParent));
2775 if (!parent) {
2776 return NS_OK;
2779 if (parent->ItemType() == mItemType) {
2780 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
2781 parentDS.forget(aParent);
2783 return NS_OK;
2786 NS_IMETHODIMP
2787 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2788 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2790 RefPtr<nsDocShell> root = this;
2791 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2792 while (parent) {
2793 root = parent;
2794 parent = root->GetInProcessParentDocshell();
2797 root.forget(aRootTreeItem);
2798 return NS_OK;
2801 NS_IMETHODIMP
2802 nsDocShell::GetInProcessSameTypeRootTreeItem(
2803 nsIDocShellTreeItem** aRootTreeItem) {
2804 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2805 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2807 nsCOMPtr<nsIDocShellTreeItem> parent;
2808 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2809 NS_ERROR_FAILURE);
2810 while (parent) {
2811 *aRootTreeItem = parent;
2812 NS_ENSURE_SUCCESS(
2813 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2814 NS_ERROR_FAILURE);
2816 NS_ADDREF(*aRootTreeItem);
2817 return NS_OK;
2820 NS_IMETHODIMP
2821 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2822 NS_ENSURE_ARG_POINTER(aTreeOwner);
2824 *aTreeOwner = mTreeOwner;
2825 NS_IF_ADDREF(*aTreeOwner);
2826 return NS_OK;
2829 NS_IMETHODIMP
2830 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2831 if (mIsBeingDestroyed && aTreeOwner) {
2832 return NS_ERROR_FAILURE;
2835 // Don't automatically set the progress based on the tree owner for frames
2836 if (!IsSubframe()) {
2837 nsCOMPtr<nsIWebProgress> webProgress =
2838 do_QueryInterface(GetAsSupports(this));
2840 if (webProgress) {
2841 nsCOMPtr<nsIWebProgressListener> oldListener =
2842 do_QueryInterface(mTreeOwner);
2843 nsCOMPtr<nsIWebProgressListener> newListener =
2844 do_QueryInterface(aTreeOwner);
2846 if (oldListener) {
2847 webProgress->RemoveProgressListener(oldListener);
2850 if (newListener) {
2851 webProgress->AddProgressListener(newListener,
2852 nsIWebProgress::NOTIFY_ALL);
2857 mTreeOwner = aTreeOwner; // Weak reference per API
2859 for (auto* childDocLoader : mChildList.ForwardRange()) {
2860 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
2861 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2863 if (child->ItemType() == mItemType) {
2864 child->SetTreeOwner(aTreeOwner);
2868 // If we're in the content process and have had a TreeOwner set on us, extract
2869 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2870 // that it hasn't changed.
2871 if (mTreeOwner && XRE_IsContentProcess()) {
2872 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2873 MOZ_ASSERT(newBrowserChild,
2874 "No BrowserChild actor for tree owner in Content!");
2876 if (mBrowserChild) {
2877 nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2878 do_QueryReferent(mBrowserChild);
2879 MOZ_RELEASE_ASSERT(
2880 oldBrowserChild == newBrowserChild,
2881 "Cannot change BrowserChild during nsDocShell lifetime!");
2882 } else {
2883 mBrowserChild = do_GetWeakReference(newBrowserChild);
2887 return NS_OK;
2890 NS_IMETHODIMP
2891 nsDocShell::GetHistoryID(nsID& aID) {
2892 aID = mBrowsingContext->GetHistoryID();
2893 return NS_OK;
2896 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
2898 NS_IMETHODIMP
2899 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2900 *aIsInUnload = mFiredUnloadEvent;
2901 return NS_OK;
2904 NS_IMETHODIMP
2905 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2906 NS_ENSURE_ARG_POINTER(aChildCount);
2907 *aChildCount = mChildList.Length();
2908 return NS_OK;
2911 NS_IMETHODIMP
2912 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2913 NS_ENSURE_ARG_POINTER(aChild);
2915 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2916 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2918 // Make sure we're not creating a loop in the docshell tree
2919 nsDocLoader* ancestor = this;
2920 do {
2921 if (childAsDocLoader == ancestor) {
2922 return NS_ERROR_ILLEGAL_VALUE;
2924 ancestor = ancestor->GetParent();
2925 } while (ancestor);
2927 // Make sure to remove the child from its current parent.
2928 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2929 if (childsParent) {
2930 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2931 NS_ENSURE_SUCCESS(rv, rv);
2934 // Make sure to clear the treeowner in case this child is a different type
2935 // from us.
2936 aChild->SetTreeOwner(nullptr);
2938 nsresult res = AddChildLoader(childAsDocLoader);
2939 NS_ENSURE_SUCCESS(res, res);
2940 NS_ASSERTION(!mChildList.IsEmpty(),
2941 "child list must not be empty after a successful add");
2943 /* Set the child's global history if the parent has one */
2944 if (mBrowsingContext->GetUseGlobalHistory()) {
2945 // childDocShell->SetUseGlobalHistory(true);
2946 // this should be set through BC inherit
2947 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
2950 if (aChild->ItemType() != mItemType) {
2951 return NS_OK;
2954 aChild->SetTreeOwner(mTreeOwner);
2956 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2957 if (!childAsDocShell) {
2958 return NS_OK;
2961 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2963 // Now take this document's charset and set the child's parentCharset field
2964 // to it. We'll later use that field, in the loading process, for the
2965 // charset choosing algorithm.
2966 // If we fail, at any point, we just return NS_OK.
2967 // This code has some performance impact. But this will be reduced when
2968 // the current charset will finally be stored as an Atom, avoiding the
2969 // alias resolution extra look-up.
2971 // we are NOT going to propagate the charset is this Chrome's docshell
2972 if (mItemType == nsIDocShellTreeItem::typeChrome) {
2973 return NS_OK;
2976 // get the parent's current charset
2977 if (!mContentViewer) {
2978 return NS_OK;
2980 Document* doc = mContentViewer->GetDocument();
2981 if (!doc) {
2982 return NS_OK;
2985 const Encoding* parentCS = doc->GetDocumentCharacterSet();
2986 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2987 // set the child's parentCharset
2988 childAsDocShell->SetParentCharset(parentCS, charsetSource,
2989 doc->NodePrincipal());
2991 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2992 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2994 return NS_OK;
2997 NS_IMETHODIMP
2998 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
2999 NS_ENSURE_ARG_POINTER(aChild);
3001 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
3002 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
3004 nsresult rv = RemoveChildLoader(childAsDocLoader);
3005 NS_ENSURE_SUCCESS(rv, rv);
3007 aChild->SetTreeOwner(nullptr);
3009 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3012 NS_IMETHODIMP
3013 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
3014 NS_ENSURE_ARG_POINTER(aChild);
3016 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
3017 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3019 child.forget(aChild);
3021 return NS_OK;
3024 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
3025 #ifdef DEBUG
3026 if (aIndex < 0) {
3027 NS_WARNING("Negative index passed to GetChildAt");
3028 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3029 NS_WARNING("Too large an index passed to GetChildAt");
3031 #endif
3033 nsIDocumentLoader* child = ChildAt(aIndex);
3035 // child may be nullptr here.
3036 return static_cast<nsDocShell*>(child);
3039 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
3040 nsISHEntry* aNewEntry,
3041 int32_t aChildOffset, uint32_t aLoadType,
3042 bool aCloneChildren) {
3043 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3044 nsresult rv = NS_OK;
3046 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
3047 /* You get here if you are currently building a
3048 * hierarchy ie.,you just visited a frameset page
3050 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
3051 rv = mLSHE->AddChild(aNewEntry, aChildOffset);
3053 } else if (!aCloneRef) {
3054 /* This is an initial load in some subframe. Just append it if we can */
3055 if (mOSHE) {
3056 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
3058 } else {
3059 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
3060 if (shistory) {
3061 rv = shistory->LegacySHistory()->AddChildSHEntryHelper(
3062 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
3065 return rv;
3068 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
3069 int32_t aChildOffset,
3070 bool aCloneChildren) {
3071 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3072 /* You will get here when you are in a subframe and
3073 * a new url has been loaded on you.
3074 * The mOSHE in this subframe will be the previous url's
3075 * mOSHE. This mOSHE will be used as the identification
3076 * for this subframe in the CloneAndReplace function.
3079 // In this case, we will end up calling AddEntry, which increases the
3080 // current index by 1
3081 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3082 if (rootSH) {
3083 mPreviousEntryIndex = rootSH->Index();
3086 nsresult rv;
3087 // XXX(farre): this is not Fission safe, expect errors. This never
3088 // get's executed once session history in the parent is enabled.
3089 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
3090 NS_WARNING_ASSERTION(
3091 parent || !UseRemoteSubframes(),
3092 "Failed to add child session history entry! This will be resolved once "
3093 "session history in the parent is enabled.");
3094 if (parent) {
3095 rv = nsDocShell::Cast(parent)->AddChildSHEntry(
3096 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
3099 if (rootSH) {
3100 mLoadedEntryIndex = rootSH->Index();
3102 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
3103 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
3104 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
3105 mLoadedEntryIndex));
3109 return rv;
3112 NS_IMETHODIMP
3113 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3114 *aOSHE = false;
3115 *aEntry = nullptr;
3116 if (mLSHE) {
3117 NS_ADDREF(*aEntry = mLSHE);
3118 } else if (mOSHE) {
3119 NS_ADDREF(*aEntry = mOSHE);
3120 *aOSHE = true;
3122 return NS_OK;
3125 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
3126 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
3127 mBrowsingContext) {
3128 if (XRE_IsContentProcess()) {
3129 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
3130 if (contentChild) {
3131 contentChild->SendSynchronizeLayoutHistoryState(
3132 mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
3134 } else {
3135 SessionHistoryEntry* entry =
3136 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
3137 if (entry) {
3138 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
3141 if (mLoadingEntry &&
3142 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
3143 mLoadingEntry->mInfo.SetLayoutHistoryState(
3144 mActiveEntry->GetLayoutHistoryState());
3148 return NS_OK;
3151 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
3152 if (mLoadGroup) {
3153 mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
3154 } else {
3155 NS_WARNING(
3156 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3157 "propagate the mode to");
3161 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3162 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3163 return mScriptGlobal;
3166 Document* nsDocShell::GetDocument() {
3167 NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
3168 return mContentViewer->GetDocument();
3171 Document* nsDocShell::GetExtantDocument() {
3172 return mContentViewer ? mContentViewer->GetDocument() : nullptr;
3175 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3176 if (NS_FAILED(EnsureScriptEnvironment())) {
3177 return nullptr;
3179 return mScriptGlobal;
3182 NS_IMETHODIMP
3183 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3184 NS_ENSURE_ARG_POINTER(aWindow);
3186 nsresult rv = EnsureScriptEnvironment();
3187 NS_ENSURE_SUCCESS(rv, rv);
3189 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3190 window.forget(aWindow);
3191 return NS_OK;
3194 NS_IMETHODIMP
3195 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3196 RefPtr<ContentFrameMessageManager> mm;
3197 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3198 mm = browserChild->GetMessageManager();
3199 } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3200 mm = win->GetMessageManager();
3202 mm.forget(aMessageManager);
3203 return NS_OK;
3206 NS_IMETHODIMP
3207 nsDocShell::GetIsNavigating(bool* aOut) {
3208 *aOut = mIsNavigating;
3209 return NS_OK;
3212 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3213 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3214 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3215 if (!rootSH || !aEntry) {
3216 return;
3219 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3222 //-------------------------------------
3223 //-- Helper Method for Print discovery
3224 //-------------------------------------
3225 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
3226 if (!mBrowsingContext->Top()->GetIsPrinting()) {
3227 return false;
3229 if (aDisplayErrorDialog) {
3230 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3232 return true;
3235 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3236 bool aCheckIfUnloadFired) {
3237 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
3238 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3239 if (!isAllowed) {
3240 return false;
3242 if (!mContentViewer) {
3243 return true;
3245 bool firingBeforeUnload;
3246 mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3247 return !firingBeforeUnload;
3250 //*****************************************************************************
3251 // nsDocShell::nsIWebNavigation
3252 //*****************************************************************************
3254 NS_IMETHODIMP
3255 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3256 *aCanGoBack = false;
3257 if (!IsNavigationAllowed(false)) {
3258 return NS_OK; // JS may not handle returning of an error code
3260 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3261 if (rootSH) {
3262 *aCanGoBack = rootSH->CanGo(-1);
3263 MOZ_LOG(gSHLog, LogLevel::Verbose,
3264 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
3266 return NS_OK;
3268 return NS_ERROR_FAILURE;
3271 NS_IMETHODIMP
3272 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3273 *aCanGoForward = false;
3274 if (!IsNavigationAllowed(false)) {
3275 return NS_OK; // JS may not handle returning of an error code
3277 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3278 if (rootSH) {
3279 *aCanGoForward = rootSH->CanGo(1);
3280 MOZ_LOG(gSHLog, LogLevel::Verbose,
3281 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
3282 return NS_OK;
3284 return NS_ERROR_FAILURE;
3287 NS_IMETHODIMP
3288 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
3289 if (!IsNavigationAllowed()) {
3290 return NS_OK; // JS may not handle returning of an error code
3293 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3294 mIsNavigating = true;
3296 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3297 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3298 ErrorResult rv;
3299 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
3300 return rv.StealNSResult();
3303 NS_IMETHODIMP
3304 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
3305 if (!IsNavigationAllowed()) {
3306 return NS_OK; // JS may not handle returning of an error code
3309 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3310 mIsNavigating = true;
3312 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3313 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3314 ErrorResult rv;
3315 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
3316 return rv.StealNSResult();
3319 // XXX(nika): We may want to stop exposing this API in the child process? Going
3320 // to a specific index from multiple different processes could definitely race.
3321 NS_IMETHODIMP
3322 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
3323 if (!IsNavigationAllowed()) {
3324 return NS_OK; // JS may not handle returning of an error code
3327 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3328 mIsNavigating = true;
3330 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3331 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3333 ErrorResult rv;
3334 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
3335 rv);
3336 return rv.StealNSResult();
3339 nsresult nsDocShell::LoadURI(nsIURI* aURI,
3340 const LoadURIOptions& aLoadURIOptions) {
3341 if (!IsNavigationAllowed()) {
3342 return NS_OK; // JS may not handle returning of an error code
3344 RefPtr<nsDocShellLoadState> loadState;
3345 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3346 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3347 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
3348 if (NS_FAILED(rv) || !loadState) {
3349 return NS_ERROR_FAILURE;
3352 return LoadURI(loadState, true);
3355 NS_IMETHODIMP
3356 nsDocShell::LoadURIFromScript(nsIURI* aURI,
3357 JS::Handle<JS::Value> aLoadURIOptions,
3358 JSContext* aCx) {
3359 // generate dictionary for aLoadURIOptions and forward call
3360 LoadURIOptions loadURIOptions;
3361 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3362 return NS_ERROR_INVALID_ARG;
3364 return LoadURI(aURI, loadURIOptions);
3367 nsresult nsDocShell::FixupAndLoadURIString(
3368 const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) {
3369 if (!IsNavigationAllowed()) {
3370 return NS_OK; // JS may not handle returning of an error code
3373 RefPtr<nsDocShellLoadState> loadState;
3374 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3375 mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState));
3377 uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3378 if (NS_ERROR_MALFORMED_URI == rv) {
3379 MOZ_LOG(gSHLog, LogLevel::Debug,
3380 ("Creating an active entry on nsDocShell %p to %s (because "
3381 "we're showing an error page)",
3382 this, NS_ConvertUTF16toUTF8(aURIString).get()));
3384 // We need to store a session history entry. We don't have a valid URI, so
3385 // we use about:blank instead.
3386 nsCOMPtr<nsIURI> uri;
3387 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
3388 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
3389 if (aLoadURIOptions.mTriggeringPrincipal) {
3390 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
3391 } else {
3392 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
3394 if (mozilla::SessionHistoryInParent()) {
3395 mActiveEntry = MakeUnique<SessionHistoryInfo>(
3396 uri, triggeringPrincipal, nullptr, nullptr, nullptr,
3397 nsLiteralCString("text/html"));
3398 mBrowsingContext->SetActiveSessionHistoryEntry(
3399 Nothing(), mActiveEntry.get(), MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
3400 /* aUpdatedCacheKey = */ 0);
3402 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(),
3403 nullptr) &&
3404 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3405 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3409 if (NS_FAILED(rv) || !loadState) {
3410 return NS_ERROR_FAILURE;
3413 return LoadURI(loadState, true);
3416 NS_IMETHODIMP
3417 nsDocShell::FixupAndLoadURIStringFromScript(
3418 const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions,
3419 JSContext* aCx) {
3420 // generate dictionary for aLoadURIOptions and forward call
3421 LoadURIOptions loadURIOptions;
3422 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3423 return NS_ERROR_INVALID_ARG;
3425 return FixupAndLoadURIString(aURIString, loadURIOptions);
3428 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3429 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3430 // such as the content-chrome boundary, don't fire the error event.
3431 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3432 return;
3435 // If embedder is same-process, then unblocking the load event is already
3436 // handled by nsDocLoader. Fire the error event on our embedder element if
3437 // requested.
3439 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3440 // more like the remote case when in-process.
3441 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3442 if (element) {
3443 if (aFireFrameErrorEvent) {
3444 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3445 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3446 fl->FireErrorEvent();
3450 return;
3453 // If we have a cross-process parent document, we must notify it that we no
3454 // longer block its load event. This is necessary for OOP sub-documents
3455 // because error documents do not result in a call to
3456 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3457 // (Obviously, we must do this before any of the returns below.)
3458 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3459 if (browserChild) {
3460 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3461 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3462 : EmbedderElementEventType::NoEvent);
3466 NS_IMETHODIMP
3467 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3468 const char16_t* aURL, nsIChannel* aFailedChannel,
3469 bool* aDisplayedErrorPage) {
3470 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3471 ("DOCSHELL %p DisplayLoadError %s\n", this,
3472 aURI ? aURI->GetSpecOrDefault().get() : ""));
3474 *aDisplayedErrorPage = false;
3475 // Get prompt and string bundle services
3476 nsCOMPtr<nsIPrompt> prompter;
3477 nsCOMPtr<nsIStringBundle> stringBundle;
3478 GetPromptAndStringBundle(getter_AddRefs(prompter),
3479 getter_AddRefs(stringBundle));
3481 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3482 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3484 const char* error = nullptr;
3485 // The key used to select the appropriate error message from the properties
3486 // file.
3487 const char* errorDescriptionID = nullptr;
3488 AutoTArray<nsString, 3> formatStrs;
3489 bool addHostPort = false;
3490 bool isBadStsCertError = false;
3491 nsresult rv = NS_OK;
3492 nsAutoString messageStr;
3493 nsAutoCString cssClass;
3494 nsAutoCString errorPage;
3496 errorPage.AssignLiteral("neterror");
3498 // Turn the error code into a human readable error message.
3499 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3500 NS_ENSURE_ARG_POINTER(aURI);
3502 // Extract the schemes into a comma delimited list.
3503 nsAutoCString scheme;
3504 aURI->GetScheme(scheme);
3505 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3506 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3507 while (nestedURI) {
3508 nsCOMPtr<nsIURI> tempURI;
3509 nsresult rv2;
3510 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3511 if (NS_SUCCEEDED(rv2) && tempURI) {
3512 tempURI->GetScheme(scheme);
3513 formatStrs[0].AppendLiteral(", ");
3514 AppendASCIItoUTF16(scheme, formatStrs[0]);
3516 nestedURI = do_QueryInterface(tempURI);
3518 error = "unknownProtocolFound";
3519 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3520 NS_ENSURE_ARG_POINTER(aURI);
3521 error = "fileNotFound";
3522 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3523 NS_ENSURE_ARG_POINTER(aURI);
3524 error = "fileAccessDenied";
3525 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3526 NS_ENSURE_ARG_POINTER(aURI);
3527 // Get the host
3528 nsAutoCString host;
3529 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3530 innermostURI->GetHost(host);
3531 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3532 errorDescriptionID = "dnsNotFound2";
3533 error = "dnsNotFound";
3534 } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3535 NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3536 NS_ENSURE_ARG_POINTER(aURI);
3537 addHostPort = true;
3538 error = "connectionFailure";
3539 } else if (NS_ERROR_NET_INTERRUPT == aError) {
3540 NS_ENSURE_ARG_POINTER(aURI);
3541 addHostPort = true;
3542 error = "netInterrupt";
3543 } else if (NS_ERROR_NET_TIMEOUT == aError ||
3544 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
3545 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
3546 NS_ENSURE_ARG_POINTER(aURI);
3547 // Get the host
3548 nsAutoCString host;
3549 aURI->GetHost(host);
3550 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3551 error = "netTimeout";
3552 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3553 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError ||
3554 NS_ERROR_CSP_NAVIGATE_TO_VIOLATION == aError) {
3555 // CSP error
3556 cssClass.AssignLiteral("neterror");
3557 error = "cspBlocked";
3558 } else if (NS_ERROR_XFO_VIOLATION == aError) {
3559 // XFO error
3560 cssClass.AssignLiteral("neterror");
3561 error = "xfoBlocked";
3562 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3563 nsCOMPtr<nsINSSErrorsService> nsserr =
3564 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3566 uint32_t errorClass;
3567 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3568 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3571 nsCOMPtr<nsITransportSecurityInfo> tsi;
3572 if (aFailedChannel) {
3573 aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
3575 if (tsi) {
3576 uint32_t securityState;
3577 tsi->GetSecurityState(&securityState);
3578 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3579 error = "sslv3Used";
3580 addHostPort = true;
3581 } else if (securityState &
3582 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3583 error = "weakCryptoUsed";
3584 addHostPort = true;
3586 } else {
3587 // No channel, let's obtain the generic error message
3588 if (nsserr) {
3589 nsserr->GetErrorMessage(aError, messageStr);
3592 // We don't have a message string here anymore but DisplayLoadError
3593 // requires a non-empty messageStr.
3594 messageStr.Truncate();
3595 messageStr.AssignLiteral(u" ");
3596 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3597 error = "nssBadCert";
3599 // If this is an HTTP Strict Transport Security host or a pinned host
3600 // and the certificate is bad, don't allow overrides (RFC 6797 section
3601 // 12.1).
3602 bool isStsHost = false;
3603 bool isPinnedHost = false;
3604 OriginAttributes attrsForHSTS;
3605 if (aFailedChannel) {
3606 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
3607 attrsForHSTS);
3608 } else {
3609 attrsForHSTS = GetOriginAttributes();
3612 if (XRE_IsParentProcess()) {
3613 nsCOMPtr<nsISiteSecurityService> sss =
3614 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3615 NS_ENSURE_SUCCESS(rv, rv);
3616 rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost);
3617 NS_ENSURE_SUCCESS(rv, rv);
3618 } else {
3619 mozilla::dom::ContentChild* cc =
3620 mozilla::dom::ContentChild::GetSingleton();
3621 cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost);
3623 nsCOMPtr<nsIPublicKeyPinningService> pkps =
3624 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
3625 NS_ENSURE_SUCCESS(rv, rv);
3626 rv = pkps->HostHasPins(aURI, &isPinnedHost);
3628 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3629 false)) {
3630 cssClass.AssignLiteral("expertBadCert");
3633 // HSTS/pinning takes precedence over the expert bad cert pref. We
3634 // never want to show the "Add Exception" button for these sites.
3635 // In the future we should differentiate between an HSTS host and a
3636 // pinned host and display a more informative message to the user.
3637 if (isStsHost || isPinnedHost) {
3638 isBadStsCertError = true;
3639 cssClass.AssignLiteral("badStsCert");
3642 errorPage.Assign("certerror");
3643 } else {
3644 error = "nssFailure2";
3646 } else if (NS_ERROR_PHISHING_URI == aError ||
3647 NS_ERROR_MALWARE_URI == aError ||
3648 NS_ERROR_UNWANTED_URI == aError ||
3649 NS_ERROR_HARMFUL_URI == aError) {
3650 nsAutoCString host;
3651 aURI->GetHost(host);
3652 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3654 // Malware and phishing detectors may want to use an alternate error
3655 // page, but if the pref's not set, we'll fall back on the standard page
3656 nsAutoCString alternateErrorPage;
3657 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3658 alternateErrorPage);
3659 if (NS_SUCCEEDED(rv)) {
3660 errorPage.Assign(alternateErrorPage);
3663 if (NS_ERROR_PHISHING_URI == aError) {
3664 error = "deceptiveBlocked";
3665 } else if (NS_ERROR_MALWARE_URI == aError) {
3666 error = "malwareBlocked";
3667 } else if (NS_ERROR_UNWANTED_URI == aError) {
3668 error = "unwantedBlocked";
3669 } else if (NS_ERROR_HARMFUL_URI == aError) {
3670 error = "harmfulBlocked";
3673 cssClass.AssignLiteral("blacklist");
3674 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3675 errorPage.AssignLiteral("tabcrashed");
3676 error = "tabcrashed";
3678 RefPtr<EventTarget> handler = mChromeEventHandler;
3679 if (handler) {
3680 nsCOMPtr<Element> element = do_QueryInterface(handler);
3681 element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
3684 // DisplayLoadError requires a non-empty messageStr to proceed and call
3685 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3686 // space which will be trimmed and thus treated as empty by the front-end.
3687 if (messageStr.IsEmpty()) {
3688 messageStr.AssignLiteral(u" ");
3690 } else if (NS_ERROR_FRAME_CRASHED == aError) {
3691 errorPage.AssignLiteral("framecrashed");
3692 error = "framecrashed";
3693 messageStr.AssignLiteral(u" ");
3694 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3695 errorPage.AssignLiteral("restartrequired");
3696 error = "restartrequired";
3698 // DisplayLoadError requires a non-empty messageStr to proceed and call
3699 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3700 // space which will be trimmed and thus treated as empty by the front-end.
3701 if (messageStr.IsEmpty()) {
3702 messageStr.AssignLiteral(u" ");
3704 } else {
3705 // Errors requiring simple formatting
3706 switch (aError) {
3707 case NS_ERROR_MALFORMED_URI:
3708 // URI is malformed
3709 error = "malformedURI";
3710 errorDescriptionID = "malformedURI2";
3711 break;
3712 case NS_ERROR_REDIRECT_LOOP:
3713 // Doc failed to load because the server generated too many redirects
3714 error = "redirectLoop";
3715 break;
3716 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3717 // Doc failed to load because PSM is not installed
3718 error = "unknownSocketType";
3719 break;
3720 case NS_ERROR_NET_RESET:
3721 // Doc failed to load because the server kept reseting the connection
3722 // before we could read any data from it
3723 error = "netReset";
3724 break;
3725 case NS_ERROR_DOCUMENT_NOT_CACHED:
3726 // Doc failed to load because the cache does not contain a copy of
3727 // the document.
3728 error = "notCached";
3729 break;
3730 case NS_ERROR_OFFLINE:
3731 // Doc failed to load because we are offline.
3732 error = "netOffline";
3733 break;
3734 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3735 // Doc navigation attempted while Printing or Print Preview
3736 error = "isprinting";
3737 break;
3738 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3739 // Port blocked for security reasons
3740 addHostPort = true;
3741 error = "deniedPortAccess";
3742 break;
3743 case NS_ERROR_UNKNOWN_PROXY_HOST:
3744 // Proxy hostname could not be resolved.
3745 error = "proxyResolveFailure";
3746 break;
3747 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3748 case NS_ERROR_PROXY_FORBIDDEN:
3749 case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3750 case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3751 case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3752 // Proxy connection was refused.
3753 error = "proxyConnectFailure";
3754 break;
3755 case NS_ERROR_INVALID_CONTENT_ENCODING:
3756 // Bad Content Encoding.
3757 error = "contentEncodingError";
3758 break;
3759 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3760 // Channel refused to load from an unrecognized content type.
3761 error = "unsafeContentType";
3762 break;
3763 case NS_ERROR_CORRUPTED_CONTENT:
3764 // Broken Content Detected. e.g. Content-MD5 check failure.
3765 error = "corruptedContentErrorv2";
3766 break;
3767 case NS_ERROR_INTERCEPTION_FAILED:
3768 // ServiceWorker intercepted request, but something went wrong.
3769 error = "corruptedContentErrorv2";
3770 break;
3771 case NS_ERROR_NET_INADEQUATE_SECURITY:
3772 // Server negotiated bad TLS for HTTP/2.
3773 error = "inadequateSecurityError";
3774 addHostPort = true;
3775 break;
3776 case NS_ERROR_BLOCKED_BY_POLICY:
3777 case NS_ERROR_DOM_COOP_FAILED:
3778 case NS_ERROR_DOM_COEP_FAILED:
3779 // Page blocked by policy
3780 error = "blockedByPolicy";
3781 break;
3782 case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3783 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3784 // HTTP/2 or HTTP/3 stack detected a protocol error
3785 error = "networkProtocolError";
3786 break;
3788 default:
3789 break;
3793 nsresult delegateErrorCode = aError;
3794 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3795 // caused this error, we replace the error-page with about:httpsonlyerror
3796 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
3797 errorPage.AssignLiteral("httpsonlyerror");
3798 delegateErrorCode = NS_ERROR_HTTPS_ONLY;
3799 } else if (isBadStsCertError) {
3800 delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
3803 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3804 nsCOMPtr<nsIURI> errorPageURI;
3805 rv = loadURIDelegate->HandleLoadError(
3806 aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
3807 getter_AddRefs(errorPageURI));
3808 // If the docshell is going away there's no point in showing an error page.
3809 if (NS_FAILED(rv) || mIsBeingDestroyed) {
3810 *aDisplayedErrorPage = false;
3811 return NS_OK;
3814 if (errorPageURI) {
3815 *aDisplayedErrorPage =
3816 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3817 return NS_OK;
3821 // Test if the error should be displayed
3822 if (!error) {
3823 return NS_OK;
3826 if (!errorDescriptionID) {
3827 errorDescriptionID = error;
3830 Telemetry::AccumulateCategoricalKeyed(
3831 IsSubframe() ? "frame"_ns : "top"_ns,
3832 mozilla::dom::LoadErrorToTelemetryLabel(aError));
3834 // Test if the error needs to be formatted
3835 if (!messageStr.IsEmpty()) {
3836 // already obtained message
3837 } else {
3838 if (addHostPort) {
3839 // Build up the host:port string.
3840 nsAutoCString hostport;
3841 if (aURI) {
3842 aURI->GetHostPort(hostport);
3843 } else {
3844 hostport.Assign('?');
3846 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3849 nsAutoCString spec;
3850 rv = NS_ERROR_NOT_AVAILABLE;
3851 auto& nextFormatStr = *formatStrs.AppendElement();
3852 if (aURI) {
3853 // displaying "file://" is aesthetically unpleasing and could even be
3854 // confusing to the user
3855 if (SchemeIsFile(aURI)) {
3856 aURI->GetPathQueryRef(spec);
3857 } else {
3858 aURI->GetSpec(spec);
3861 nsCOMPtr<nsITextToSubURI> textToSubURI(
3862 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3863 if (NS_SUCCEEDED(rv)) {
3864 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3866 } else {
3867 spec.Assign('?');
3869 if (NS_FAILED(rv)) {
3870 CopyUTF8toUTF16(spec, nextFormatStr);
3872 rv = NS_OK;
3874 nsAutoString str;
3875 rv =
3876 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3877 NS_ENSURE_SUCCESS(rv, rv);
3878 messageStr.Assign(str);
3881 // Display the error as a page or an alert prompt
3882 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3884 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3885 SchemeIsHTTPS(aURI)) {
3886 // Maybe TLS intolerant. Treat this as an SSL error.
3887 error = "nssFailure2";
3890 if (mBrowsingContext->GetUseErrorPages()) {
3891 // Display an error page
3892 nsresult loadedPage =
3893 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3894 cssClass.get(), aFailedChannel);
3895 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3896 } else {
3897 // The prompter reqires that our private window has a document (or it
3898 // asserts). Satisfy that assertion now since GetDoc will force
3899 // creation of one if it hasn't already been created.
3900 if (mScriptGlobal) {
3901 Unused << mScriptGlobal->GetDoc();
3904 // Display a message box
3905 prompter->Alert(nullptr, messageStr.get());
3908 return NS_OK;
3911 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3913 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3914 const char* aErrorPage,
3915 const char* aErrorType,
3916 const char16_t* aDescription,
3917 const char* aCSSClass,
3918 nsIChannel* aFailedChannel) {
3919 if (mIsBeingDestroyed) {
3920 return NS_ERROR_NOT_AVAILABLE;
3923 #if defined(DEBUG)
3924 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3925 nsAutoCString chanName;
3926 if (aFailedChannel) {
3927 aFailedChannel->GetName(chanName);
3928 } else {
3929 chanName.AssignLiteral("<no channel>");
3932 MOZ_LOG(gDocShellLog, LogLevel::Debug,
3933 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3934 this, aURI ? aURI->GetSpecOrDefault().get() : "",
3935 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3937 #endif
3939 nsAutoCString url;
3940 if (aURI) {
3941 nsresult rv = aURI->GetSpec(url);
3942 NS_ENSURE_SUCCESS(rv, rv);
3943 } else if (aURL) {
3944 CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3945 } else {
3946 return NS_ERROR_INVALID_POINTER;
3949 // Create a URL to pass all the error information through to the page.
3951 #undef SAFE_ESCAPE
3952 #define SAFE_ESCAPE(output, input, params) \
3953 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3954 return NS_ERROR_OUT_OF_MEMORY; \
3957 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3958 SAFE_ESCAPE(escapedUrl, url, url_Path);
3959 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3960 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3961 url_Path);
3962 if (aCSSClass) {
3963 nsCString cssClass(aCSSClass);
3964 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3966 nsCString errorPageUrl("about:");
3967 errorPageUrl.AppendASCII(aErrorPage);
3968 errorPageUrl.AppendLiteral("?e=");
3970 errorPageUrl.AppendASCII(escapedError.get());
3971 errorPageUrl.AppendLiteral("&u=");
3972 errorPageUrl.AppendASCII(escapedUrl.get());
3973 if ((strcmp(aErrorPage, "blocked") == 0) &&
3974 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
3975 errorPageUrl.AppendLiteral("&o=1");
3977 if (!escapedCSSClass.IsEmpty()) {
3978 errorPageUrl.AppendLiteral("&s=");
3979 errorPageUrl.AppendASCII(escapedCSSClass.get());
3981 errorPageUrl.AppendLiteral("&c=UTF-8");
3983 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
3984 int32_t cpsState;
3985 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
3986 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
3987 errorPageUrl.AppendLiteral("&captive=true");
3990 errorPageUrl.AppendLiteral("&d=");
3991 errorPageUrl.AppendASCII(escapedDescription.get());
3993 nsCOMPtr<nsIURI> errorPageURI;
3994 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3995 NS_ENSURE_SUCCESS(rv, rv);
3997 return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
4000 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
4001 nsIChannel* aFailedChannel) {
4002 mFailedChannel = aFailedChannel;
4003 mFailedURI = aFailedURI;
4004 mFailedLoadType = mLoadType;
4006 if (mLSHE) {
4007 // Abandon mLSHE's BFCache entry and create a new one. This way, if
4008 // we go back or forward to another SHEntry with the same doc
4009 // identifier, the error page won't persist.
4010 mLSHE->AbandonBFCacheEntry();
4013 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
4014 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4015 if (mBrowsingContext) {
4016 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
4018 loadState->SetLoadType(LOAD_ERROR_PAGE);
4019 loadState->SetFirstParty(true);
4020 loadState->SetSourceBrowsingContext(mBrowsingContext);
4021 if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
4022 // We keep the loading entry for the load that failed here. If the user
4023 // reloads we want to try to reload the original load, not the error page.
4024 loadState->SetLoadingSessionHistoryInfo(
4025 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
4027 return InternalLoad(loadState);
4030 NS_IMETHODIMP
4031 nsDocShell::Reload(uint32_t aReloadFlags) {
4032 if (!IsNavigationAllowed()) {
4033 return NS_OK; // JS may not handle returning of an error code
4036 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
4037 "Reload command not updated to use load flags!");
4038 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4039 "Don't pass these flags to Reload");
4041 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4042 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4044 // Send notifications to the HistoryListener if any, about the impending
4045 // reload
4046 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4047 if (mozilla::SessionHistoryInParent()) {
4048 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
4049 bool forceReload = IsForceReloadType(loadType);
4050 if (!XRE_IsParentProcess()) {
4051 ++mPendingReloadCount;
4052 RefPtr<nsDocShell> docShell(this);
4053 nsCOMPtr<nsIContentViewer> cv(mContentViewer);
4054 NS_ENSURE_STATE(cv);
4056 bool okToUnload = true;
4057 MOZ_TRY(cv->PermitUnload(&okToUnload));
4058 if (!okToUnload) {
4059 return NS_OK;
4062 RefPtr<Document> doc(GetDocument());
4063 RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
4064 nsCOMPtr<nsIURI> currentURI(mCurrentURI);
4065 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
4066 RefPtr<StopDetector> stopDetector = new StopDetector();
4067 nsCOMPtr<nsILoadGroup> loadGroup;
4068 GetLoadGroup(getter_AddRefs(loadGroup));
4069 if (loadGroup) {
4070 // loadGroup may be null in theory. In that case stopDetector just
4071 // doesn't do anything.
4072 loadGroup->AddRequest(stopDetector, nullptr);
4075 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
4076 mBrowsingContext, forceReload,
4077 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo,
4078 loadGroup, stopDetector](
4079 std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>,
4080 Maybe<bool>>&& aResult) {
4081 auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() {
4082 if (loadGroup) {
4083 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
4087 // Decrease mPendingReloadCount before any other early returns!
4088 if (--(docShell->mPendingReloadCount) > 0) {
4089 return;
4092 if (stopDetector->Canceled()) {
4093 return;
4095 bool canReload;
4096 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4097 Maybe<bool> reloadingActiveEntry;
4099 std::tie(canReload, loadState, reloadingActiveEntry) = aResult;
4101 if (!canReload) {
4102 return;
4105 if (loadState.isSome()) {
4106 MOZ_LOG(
4107 gSHLog, LogLevel::Debug,
4108 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
4109 loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
4110 docShell->LoadHistoryEntry(loadState.ref(), loadType,
4111 reloadingActiveEntry.ref());
4112 } else {
4113 MOZ_LOG(gSHLog, LogLevel::Debug,
4114 ("nsDocShell %p ReloadDocument", docShell.get()));
4115 ReloadDocument(docShell, doc, loadType, browsingContext,
4116 currentURI, referrerInfo,
4117 /* aNotifiedBeforeUnloadListeners */ true);
4120 [](mozilla::ipc::ResponseRejectReason) {});
4121 } else {
4122 // Parent process
4123 bool canReload = false;
4124 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4125 Maybe<bool> reloadingActiveEntry;
4126 if (!mBrowsingContext->IsDiscarded()) {
4127 mBrowsingContext->Canonical()->NotifyOnHistoryReload(
4128 forceReload, canReload, loadState, reloadingActiveEntry);
4130 if (canReload) {
4131 if (loadState.isSome()) {
4132 MOZ_LOG(gSHLog, LogLevel::Debug,
4133 ("nsDocShell %p Reload - LoadHistoryEntry", this));
4134 LoadHistoryEntry(loadState.ref(), loadType,
4135 reloadingActiveEntry.ref());
4136 } else {
4137 MOZ_LOG(gSHLog, LogLevel::Debug,
4138 ("nsDocShell %p ReloadDocument", this));
4139 RefPtr<Document> doc = GetDocument();
4140 RefPtr<BrowsingContext> bc = mBrowsingContext;
4141 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4142 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4143 ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4147 return NS_OK;
4150 bool canReload = true;
4151 if (rootSH) {
4152 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
4155 if (!canReload) {
4156 return NS_OK;
4159 /* If you change this part of code, make sure bug 45297 does not re-occur */
4160 if (mOSHE) {
4161 nsCOMPtr<nsISHEntry> oshe = mOSHE;
4162 return LoadHistoryEntry(
4163 oshe, loadType,
4164 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4167 if (mLSHE) { // In case a reload happened before the current load is done
4168 nsCOMPtr<nsISHEntry> lshe = mLSHE;
4169 return LoadHistoryEntry(
4170 lshe, loadType,
4171 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4174 RefPtr<Document> doc = GetDocument();
4175 RefPtr<BrowsingContext> bc = mBrowsingContext;
4176 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4177 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4178 return ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4181 /* static */
4182 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
4183 uint32_t aLoadType,
4184 BrowsingContext* aBrowsingContext,
4185 nsIURI* aCurrentURI,
4186 nsIReferrerInfo* aReferrerInfo,
4187 bool aNotifiedBeforeUnloadListeners) {
4188 if (!aDocument) {
4189 return NS_OK;
4192 // Do not inherit owner from document
4193 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4194 nsAutoString srcdoc;
4195 nsIURI* baseURI = nullptr;
4196 nsCOMPtr<nsIURI> originalURI;
4197 nsCOMPtr<nsIURI> resultPrincipalURI;
4198 bool loadReplace = false;
4200 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
4201 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
4202 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
4204 nsAutoString contentTypeHint;
4205 aDocument->GetContentType(contentTypeHint);
4207 if (aDocument->IsSrcdocDocument()) {
4208 aDocument->GetSrcdocData(srcdoc);
4209 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4210 baseURI = aDocument->GetBaseURI();
4211 } else {
4212 srcdoc = VoidString();
4214 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
4215 if (chan) {
4216 uint32_t loadFlags;
4217 chan->GetLoadFlags(&loadFlags);
4218 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4219 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4220 if (httpChan) {
4221 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4224 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
4225 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4228 if (!triggeringPrincipal) {
4229 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4230 return NS_ERROR_FAILURE;
4233 // Stack variables to ensure changes to the member variables don't affect to
4234 // the call.
4235 nsCOMPtr<nsIURI> currentURI = aCurrentURI;
4237 // Reload always rewrites result principal URI.
4238 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4239 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4241 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
4242 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
4243 loadState->SetReferrerInfo(aReferrerInfo);
4244 loadState->SetOriginalURI(originalURI);
4245 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
4246 loadState->SetLoadReplace(loadReplace);
4247 loadState->SetTriggeringPrincipal(triggeringPrincipal);
4248 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
4249 loadState->SetPrincipalToInherit(triggeringPrincipal);
4250 loadState->SetCsp(csp);
4251 loadState->SetInternalLoadFlags(flags);
4252 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
4253 loadState->SetLoadType(aLoadType);
4254 loadState->SetFirstParty(true);
4255 loadState->SetSrcdocData(srcdoc);
4256 loadState->SetSourceBrowsingContext(aBrowsingContext);
4257 loadState->SetBaseURI(baseURI);
4258 loadState->SetHasValidUserGestureActivation(
4259 context && context->HasValidTransientUserGestureActivation());
4260 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
4261 return aDocShell->InternalLoad(loadState);
4264 NS_IMETHODIMP
4265 nsDocShell::Stop(uint32_t aStopFlags) {
4266 // Revoke any pending event related to content viewer restoration
4267 mRestorePresentationEvent.Revoke();
4269 if (mLoadType == LOAD_ERROR_PAGE) {
4270 if (mLSHE) {
4271 // Since error page loads never unset mLSHE, do so now
4272 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
4274 mActiveEntryIsLoadingFromSessionHistory = false;
4276 mFailedChannel = nullptr;
4277 mFailedURI = nullptr;
4280 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4281 // Stop the document loading and animations
4282 if (mContentViewer) {
4283 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4284 cv->Stop();
4286 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4287 // Stop the document loading only
4288 if (mContentViewer) {
4289 RefPtr<Document> doc = mContentViewer->GetDocument();
4290 if (doc) {
4291 doc->StopDocumentLoad();
4296 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4297 // Suspend any timers that were set for this loader. We'll clear
4298 // them out for good in CreateContentViewer.
4299 if (mRefreshURIList) {
4300 SuspendRefreshURIs();
4301 mSavedRefreshURIList.swap(mRefreshURIList);
4302 mRefreshURIList = nullptr;
4305 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4306 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4307 // redundant apis!
4308 Stop();
4310 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4311 // BFCache now, and the Stop above will have removed the DocumentChannel
4312 // from the loadgroup.
4313 mChannelToDisconnectOnPageHide = 0;
4316 for (auto* child : mChildList.ForwardRange()) {
4317 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
4318 if (shellAsNav) {
4319 shellAsNav->Stop(aStopFlags);
4323 return NS_OK;
4326 NS_IMETHODIMP
4327 nsDocShell::GetDocument(Document** aDocument) {
4328 NS_ENSURE_ARG_POINTER(aDocument);
4329 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4331 RefPtr<Document> doc = mContentViewer->GetDocument();
4332 if (!doc) {
4333 return NS_ERROR_NOT_AVAILABLE;
4336 doc.forget(aDocument);
4337 return NS_OK;
4340 NS_IMETHODIMP
4341 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4342 NS_ENSURE_ARG_POINTER(aURI);
4344 nsCOMPtr<nsIURI> uri = mCurrentURI;
4345 uri.forget(aURI);
4346 return NS_OK;
4349 NS_IMETHODIMP
4350 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4351 NS_ENSURE_ARG_POINTER(aSessionHistory);
4352 RefPtr<ChildSHistory> shistory = GetSessionHistory();
4353 shistory.forget(aSessionHistory);
4354 return NS_OK;
4357 //*****************************************************************************
4358 // nsDocShell::nsIWebPageDescriptor
4359 //*****************************************************************************
4361 NS_IMETHODIMP
4362 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
4363 const nsAString& aURI) {
4364 if (!aOtherDocShell) {
4365 return NS_ERROR_INVALID_POINTER;
4367 nsCOMPtr<nsIURI> newURI;
4368 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
4369 if (NS_FAILED(rv)) {
4370 return rv;
4373 RefPtr<nsDocShellLoadState> loadState;
4374 uint32_t cacheKey;
4375 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
4376 if (mozilla::SessionHistoryInParent()) {
4377 loadState = new nsDocShellLoadState(newURI);
4378 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
4379 return NS_ERROR_INVALID_POINTER;
4381 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
4382 } else {
4383 nsCOMPtr<nsISHEntry> entry;
4384 bool isOriginalSHE;
4385 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
4386 if (!entry) {
4387 return NS_ERROR_INVALID_POINTER;
4389 rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
4390 NS_ENSURE_SUCCESS(rv, rv);
4391 entry->GetCacheKey(&cacheKey);
4392 loadState->SetURI(newURI);
4393 loadState->SetSHEntry(nullptr);
4396 // We're doing a load of the page, via an API that
4397 // is only exposed to system code. The triggering principal for this load
4398 // should be the system principal.
4399 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4400 loadState->SetOriginalURI(nullptr);
4401 loadState->SetResultPrincipalURI(nullptr);
4403 return InternalLoad(loadState, Some(cacheKey));
4406 NS_IMETHODIMP
4407 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4408 MOZ_ASSERT(aPageDescriptor, "Null out param?");
4410 *aPageDescriptor = nullptr;
4412 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4413 if (src) {
4414 nsCOMPtr<nsISHEntry> dest;
4416 nsresult rv = src->Clone(getter_AddRefs(dest));
4417 if (NS_FAILED(rv)) {
4418 return rv;
4421 // null out inappropriate cloned attributes...
4422 dest->SetParent(nullptr);
4423 dest->SetIsSubFrame(false);
4425 return CallQueryInterface(dest, aPageDescriptor);
4428 return NS_ERROR_NOT_AVAILABLE;
4431 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
4432 const {
4433 nsCOMPtr<nsIInputStream> postData;
4434 if (mozilla::SessionHistoryInParent()) {
4435 if (mActiveEntry) {
4436 postData = mActiveEntry->GetPostData();
4437 } else if (mLoadingEntry) {
4438 postData = mLoadingEntry->mInfo.GetPostData();
4440 } else {
4441 if (mOSHE) {
4442 postData = mOSHE->GetPostData();
4443 } else if (mLSHE) {
4444 postData = mLSHE->GetPostData();
4448 return postData.forget();
4451 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4452 if (mozilla::SessionHistoryInParent()) {
4453 if (mActiveEntry) {
4454 return Some(mActiveEntry->GetCacheKey());
4457 if (mLoadingEntry) {
4458 return Some(mLoadingEntry->mInfo.GetCacheKey());
4460 } else {
4461 if (mOSHE) {
4462 return Some(mOSHE->GetCacheKey());
4465 if (mLSHE) {
4466 return Some(mLSHE->GetCacheKey());
4470 return Nothing();
4473 bool nsDocShell::FillLoadStateFromCurrentEntry(
4474 nsDocShellLoadState& aLoadState) {
4475 if (mLoadingEntry) {
4476 mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
4477 return true;
4479 if (mActiveEntry) {
4480 mActiveEntry->FillLoadInfo(aLoadState);
4481 return true;
4483 return false;
4486 //*****************************************************************************
4487 // nsDocShell::nsIBaseWindow
4488 //*****************************************************************************
4490 NS_IMETHODIMP
4491 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4492 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4493 int32_t aWidth, int32_t aHeight) {
4494 SetParentWidget(aParentWidget);
4495 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4496 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE);
4498 return NS_OK;
4501 NS_IMETHODIMP
4502 nsDocShell::Destroy() {
4503 // XXX: We allow this function to be called just once. If you are going to
4504 // reset new variables in this function, please make sure the variables will
4505 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4506 // in the setter functions for the variables would be enough.
4507 if (mIsBeingDestroyed) {
4508 return NS_ERROR_DOCSHELL_DYING;
4511 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4512 "Unexpected item type in docshell");
4514 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4515 if (serv) {
4516 const char* msg = mItemType == typeContent
4517 ? NS_WEBNAVIGATION_DESTROY
4518 : NS_CHROME_WEBNAVIGATION_DESTROY;
4519 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4522 mIsBeingDestroyed = true;
4524 // Brak the cycle with the initial client, if present.
4525 mInitialClientSource.reset();
4527 // Make sure we don't record profile timeline markers anymore
4528 SetRecordProfileTimelineMarkers(false);
4530 // Make sure to blow away our mLoadingURI just in case. No loads
4531 // from inside this pagehide.
4532 mLoadingURI = nullptr;
4534 // Fire unload event before we blow anything away.
4535 (void)FirePageHideNotification(true);
4537 // Clear pointers to any detached nsEditorData that's lying
4538 // around in shistory entries. Breaks cycle. See bug 430921.
4539 if (mOSHE) {
4540 mOSHE->SetEditorData(nullptr);
4542 if (mLSHE) {
4543 mLSHE->SetEditorData(nullptr);
4546 // Note: mContentListener can be null if Init() failed and we're being
4547 // called from the destructor.
4548 if (mContentListener) {
4549 mContentListener->DropDocShellReference();
4550 mContentListener->SetParentContentListener(nullptr);
4551 // Note that we do NOT set mContentListener to null here; that
4552 // way if someone tries to do a load in us after this point
4553 // the nsDSURIContentListener will block it. All of which
4554 // means that we should do this before calling Stop(), of
4555 // course.
4558 // Stop any URLs that are currently being loaded...
4559 Stop(nsIWebNavigation::STOP_ALL);
4561 mEditorData = nullptr;
4563 // Save the state of the current document, before destroying the window.
4564 // This is needed to capture the state of a frameset when the new document
4565 // causes the frameset to be destroyed...
4566 PersistLayoutHistoryState();
4568 // Remove this docshell from its parent's child list
4569 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4570 do_QueryInterface(GetAsSupports(mParent));
4571 if (docShellParentAsItem) {
4572 docShellParentAsItem->RemoveChild(this);
4575 if (mContentViewer) {
4576 mContentViewer->Close(nullptr);
4577 mContentViewer->Destroy();
4578 mContentViewer = nullptr;
4581 nsDocLoader::Destroy();
4583 mParentWidget = nullptr;
4584 SetCurrentURIInternal(nullptr);
4586 if (mScriptGlobal) {
4587 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4588 mScriptGlobal = nullptr;
4591 if (GetSessionHistory()) {
4592 // We want to destroy these content viewers now rather than
4593 // letting their destruction wait for the session history
4594 // entries to get garbage collected. (Bug 488394)
4595 GetSessionHistory()->EvictLocalContentViewers();
4598 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
4599 mBrowsingContext->PrepareForProcessChange();
4602 SetTreeOwner(nullptr);
4604 mBrowserChild = nullptr;
4606 mChromeEventHandler = nullptr;
4608 // Cancel any timers that were set for this docshell; this is needed
4609 // to break the cycle between us and the timers.
4610 CancelRefreshURITimers();
4612 return NS_OK;
4615 double nsDocShell::GetWidgetCSSToDeviceScale() {
4616 if (mParentWidget) {
4617 return mParentWidget->GetDefaultScale().scale;
4619 if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) {
4620 return ownerWindow->GetWidgetCSSToDeviceScale();
4622 return 1.0;
4625 NS_IMETHODIMP
4626 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4627 if (mParentWidget) {
4628 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4629 return NS_OK;
4632 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4633 if (ownerWindow) {
4634 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4637 *aScale = 1.0;
4638 return NS_OK;
4641 NS_IMETHODIMP
4642 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4643 mBounds.MoveTo(aX, aY);
4645 if (mContentViewer) {
4646 NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4649 return NS_OK;
4652 NS_IMETHODIMP
4653 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4654 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4655 if (ownerWindow) {
4656 return ownerWindow->SetPositionDesktopPix(aX, aY);
4659 double scale = 1.0;
4660 GetDevicePixelsPerDesktopPixel(&scale);
4661 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4664 NS_IMETHODIMP
4665 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4666 return GetPositionAndSize(aX, aY, nullptr, nullptr);
4669 NS_IMETHODIMP
4670 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4671 int32_t x = 0, y = 0;
4672 GetPosition(&x, &y);
4673 return SetPositionAndSize(x, y, aWidth, aHeight,
4674 aRepaint ? nsIBaseWindow::eRepaint : 0);
4677 NS_IMETHODIMP
4678 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4679 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4682 NS_IMETHODIMP
4683 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4684 int32_t aHeight, uint32_t aFlags) {
4685 mBounds.SetRect(aX, aY, aWidth, aHeight);
4687 // Hold strong ref, since SetBounds can make us null out mContentViewer
4688 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
4689 if (viewer) {
4690 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4691 ? nsIContentViewer::eDelayResize
4692 : 0;
4693 // XXX Border figured in here or is that handled elsewhere?
4694 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4695 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4698 return NS_OK;
4701 NS_IMETHODIMP
4702 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4703 int32_t* aHeight) {
4704 if (mParentWidget) {
4705 // ensure size is up-to-date if window has changed resolution
4706 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4707 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4710 // We should really consider just getting this information from
4711 // our window instead of duplicating the storage and code...
4712 if (aWidth || aHeight) {
4713 // Caller wants to know our size; make sure to give them up to
4714 // date information.
4715 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4716 if (doc) {
4717 doc->FlushPendingNotifications(FlushType::Layout);
4721 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4722 return NS_OK;
4725 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4726 int32_t* aHeight) {
4727 if (aX) {
4728 *aX = mBounds.X();
4730 if (aY) {
4731 *aY = mBounds.Y();
4733 if (aWidth) {
4734 *aWidth = mBounds.Width();
4736 if (aHeight) {
4737 *aHeight = mBounds.Height();
4741 NS_IMETHODIMP
4742 nsDocShell::SetDimensions(DimensionRequest&& aRequest) {
4743 return NS_ERROR_NOT_IMPLEMENTED;
4746 NS_IMETHODIMP
4747 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
4748 int32_t* aY, int32_t* aCX, int32_t* aCY) {
4749 return NS_ERROR_NOT_IMPLEMENTED;
4752 NS_IMETHODIMP
4753 nsDocShell::Repaint(bool aForce) {
4754 PresShell* presShell = GetPresShell();
4755 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4757 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4758 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4760 viewManager->InvalidateAllViews();
4761 return NS_OK;
4764 NS_IMETHODIMP
4765 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4766 NS_ENSURE_ARG_POINTER(aParentWidget);
4768 *aParentWidget = mParentWidget;
4769 NS_IF_ADDREF(*aParentWidget);
4771 return NS_OK;
4774 NS_IMETHODIMP
4775 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4776 MOZ_ASSERT(!mIsBeingDestroyed);
4777 mParentWidget = aParentWidget;
4779 return NS_OK;
4782 NS_IMETHODIMP
4783 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4784 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4786 if (mParentWidget) {
4787 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4788 } else {
4789 *aParentNativeWindow = nullptr;
4792 return NS_OK;
4795 NS_IMETHODIMP
4796 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4797 return NS_ERROR_NOT_IMPLEMENTED;
4800 NS_IMETHODIMP
4801 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4802 // the nativeHandle should be accessed from nsIAppWindow
4803 return NS_ERROR_NOT_IMPLEMENTED;
4806 NS_IMETHODIMP
4807 nsDocShell::GetVisibility(bool* aVisibility) {
4808 NS_ENSURE_ARG_POINTER(aVisibility);
4810 *aVisibility = false;
4812 if (!mContentViewer) {
4813 return NS_OK;
4816 PresShell* presShell = GetPresShell();
4817 if (!presShell) {
4818 return NS_OK;
4821 // get the view manager
4822 nsViewManager* vm = presShell->GetViewManager();
4823 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4825 // get the root view
4826 nsView* view = vm->GetRootView(); // views are not ref counted
4827 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4829 // if our root view is hidden, we are not visible
4830 if (view->GetVisibility() == ViewVisibility::Hide) {
4831 return NS_OK;
4834 // otherwise, we must walk up the document and view trees checking
4835 // for a hidden view, unless we're an off screen browser, which
4836 // would make this test meaningless.
4838 RefPtr<nsDocShell> docShell = this;
4839 RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4840 while (parentItem) {
4841 // Null-check for crash in bug 267804
4842 if (!parentItem->GetPresShell()) {
4843 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4844 return NS_OK;
4847 vm = docShell->GetPresShell()->GetViewManager();
4848 if (vm) {
4849 view = vm->GetRootView();
4852 if (view) {
4853 view = view->GetParent(); // anonymous inner view
4854 if (view) {
4855 view = view->GetParent(); // subdocumentframe's view
4859 nsIFrame* frame = view ? view->GetFrame() : nullptr;
4860 if (frame && !frame->IsVisibleConsideringAncestors(
4861 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
4862 return NS_OK;
4865 docShell = parentItem;
4866 parentItem = docShell->GetInProcessParentDocshell();
4869 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4870 if (!treeOwnerAsWin) {
4871 *aVisibility = true;
4872 return NS_OK;
4875 // Check with the tree owner as well to give embedders a chance to
4876 // expose visibility as well.
4877 nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility);
4878 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
4879 // The tree owner had no opinion on our visibility.
4880 *aVisibility = true;
4881 return NS_OK;
4883 return rv;
4886 void nsDocShell::ActivenessMaybeChanged() {
4887 const bool isActive = mBrowsingContext->IsActive();
4888 if (RefPtr<PresShell> presShell = GetPresShell()) {
4889 presShell->ActivenessMaybeChanged();
4892 // Tell the window about it
4893 if (mScriptGlobal) {
4894 mScriptGlobal->SetIsBackground(!isActive);
4895 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4896 // Update orientation when the top-level browsing context becomes active.
4897 if (isActive && mBrowsingContext->IsTop()) {
4898 // We only care about the top-level browsing context.
4899 auto orientation = mBrowsingContext->GetOrientationLock();
4900 ScreenOrientation::UpdateActiveOrientationLock(orientation);
4903 doc->PostVisibilityUpdateEvent();
4907 // Tell the nsDOMNavigationTiming about it
4908 RefPtr<nsDOMNavigationTiming> timing = mTiming;
4909 if (!timing && mContentViewer) {
4910 if (Document* doc = mContentViewer->GetDocument()) {
4911 timing = doc->GetNavigationTiming();
4914 if (timing) {
4915 timing->NotifyDocShellStateChanged(
4916 isActive ? nsDOMNavigationTiming::DocShellState::eActive
4917 : nsDOMNavigationTiming::DocShellState::eInactive);
4920 // Restart or stop meta refresh timers if necessary
4921 if (mDisableMetaRefreshWhenInactive) {
4922 if (isActive) {
4923 ResumeRefreshURIs();
4924 } else {
4925 SuspendRefreshURIs();
4929 if (InputTaskManager::CanSuspendInputEvent()) {
4930 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
4934 NS_IMETHODIMP
4935 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4936 if (!mWillChangeProcess) {
4937 // Intentionally ignoring handling discarded browsing contexts.
4938 Unused << mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4939 } else {
4940 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4941 // shutdown. Sorry DevTools, your DocShell is in another process.
4942 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4944 return NS_OK;
4947 NS_IMETHODIMP
4948 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4949 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4950 return NS_OK;
4953 NS_IMETHODIMP
4954 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4955 NS_ENSURE_ARG_POINTER(aFailedChannel);
4956 Document* doc = GetDocument();
4957 if (!doc) {
4958 *aFailedChannel = nullptr;
4959 return NS_OK;
4961 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4962 return NS_OK;
4965 NS_IMETHODIMP
4966 nsDocShell::SetVisibility(bool aVisibility) {
4967 // Show()/Hide() may change mContentViewer.
4968 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4969 if (!cv) {
4970 return NS_OK;
4972 if (aVisibility) {
4973 cv->Show();
4974 } else {
4975 cv->Hide();
4978 return NS_OK;
4981 NS_IMETHODIMP
4982 nsDocShell::GetEnabled(bool* aEnabled) {
4983 NS_ENSURE_ARG_POINTER(aEnabled);
4984 *aEnabled = true;
4985 return NS_ERROR_NOT_IMPLEMENTED;
4988 NS_IMETHODIMP
4989 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4991 NS_IMETHODIMP
4992 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4993 // We don't create our own widget, so simply return the parent one.
4994 return GetParentWidget(aMainWidget);
4997 NS_IMETHODIMP
4998 nsDocShell::GetTitle(nsAString& aTitle) {
4999 aTitle = mTitle;
5000 return NS_OK;
5003 NS_IMETHODIMP
5004 nsDocShell::SetTitle(const nsAString& aTitle) {
5005 // Avoid unnecessary updates of the title if the URI and the title haven't
5006 // changed.
5007 if (mTitleValidForCurrentURI && mTitle == aTitle) {
5008 return NS_OK;
5011 // Store local title
5012 mTitle = aTitle;
5013 mTitleValidForCurrentURI = true;
5015 // When title is set on the top object it should then be passed to the
5016 // tree owner.
5017 if (mBrowsingContext->IsTop()) {
5018 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5019 if (treeOwnerAsWin) {
5020 treeOwnerAsWin->SetTitle(aTitle);
5024 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
5025 UpdateGlobalHistoryTitle(mCurrentURI);
5028 // Update SessionHistory with the document's title.
5029 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
5030 SetTitleOnHistoryEntry(true);
5033 return NS_OK;
5036 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
5037 if (mOSHE) {
5038 mOSHE->SetTitle(mTitle);
5041 if (mActiveEntry && mBrowsingContext) {
5042 mActiveEntry->SetTitle(mTitle);
5043 if (aUpdateEntryInSessionHistory) {
5044 if (XRE_IsParentProcess()) {
5045 SessionHistoryEntry* entry =
5046 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
5047 if (entry) {
5048 entry->SetTitle(mTitle);
5050 } else {
5051 mozilla::Unused
5052 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
5053 mBrowsingContext, mTitle);
5059 nsPoint nsDocShell::GetCurScrollPos() {
5060 nsPoint scrollPos;
5061 if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
5062 scrollPos = sf->GetVisualViewportOffset();
5064 return scrollPos;
5067 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
5068 int32_t aCurVerticalPos) {
5069 nsIScrollableFrame* sf = GetRootScrollFrame();
5070 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5072 ScrollMode scrollMode =
5073 sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
5075 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
5076 sf->ScrollTo(targetPos, scrollMode);
5078 // Set the visual viewport offset as well.
5080 RefPtr<PresShell> presShell = GetPresShell();
5081 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5083 nsPresContext* presContext = presShell->GetPresContext();
5084 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
5086 // Only the root content document can have a distinct visual viewport offset.
5087 if (!presContext->IsRootContentDocumentCrossProcess()) {
5088 return NS_OK;
5091 // Not on a platform with a distinct visual viewport - don't bother setting
5092 // the visual viewport offset.
5093 if (!presShell->IsVisualViewportSizeSet()) {
5094 return NS_OK;
5097 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
5098 scrollMode);
5100 return NS_OK;
5103 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
5104 if (mScrollbarPref == aPref) {
5105 return;
5107 mScrollbarPref = aPref;
5108 auto* ps = GetPresShell();
5109 if (!ps) {
5110 return;
5112 nsIFrame* scrollFrame = ps->GetRootScrollFrame();
5113 if (!scrollFrame) {
5114 return;
5116 ps->FrameNeedsReflow(scrollFrame,
5117 IntrinsicDirty::FrameAncestorsAndDescendants,
5118 NS_FRAME_IS_DIRTY);
5121 //*****************************************************************************
5122 // nsDocShell::nsIRefreshURI
5123 //*****************************************************************************
5125 NS_IMETHODIMP
5126 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5127 uint32_t aDelay) {
5128 MOZ_ASSERT(!mIsBeingDestroyed);
5130 NS_ENSURE_ARG(aURI);
5132 /* Check if Meta refresh/redirects are permitted. Some
5133 * embedded applications may not want to do this.
5134 * Must do this before sending out NOTIFY_REFRESH events
5135 * because listeners may have side effects (e.g. displaying a
5136 * button to manually trigger the refresh later).
5138 bool allowRedirects = true;
5139 GetAllowMetaRedirects(&allowRedirects);
5140 if (!allowRedirects) {
5141 return NS_OK;
5144 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5145 // give them a chance to block this refresh.
5146 bool sameURI;
5147 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5148 if (NS_FAILED(rv)) {
5149 sameURI = false;
5151 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
5152 return NS_OK;
5155 nsCOMPtr<nsITimerCallback> refreshTimer =
5156 new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
5158 BusyFlags busyFlags = GetBusyFlags();
5160 if (!mRefreshURIList) {
5161 mRefreshURIList = nsArray::Create();
5164 if (busyFlags & BUSY_FLAGS_BUSY ||
5165 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
5166 // We don't want to create the timer right now. Instead queue up the
5167 // request and trigger the timer in EndPageLoad() or whenever we become
5168 // active.
5169 mRefreshURIList->AppendElement(refreshTimer);
5170 } else {
5171 // There is no page loading going on right now. Create the
5172 // timer and fire it right away.
5173 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5174 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
5176 nsCOMPtr<nsITimer> timer;
5177 MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
5178 nsITimer::TYPE_ONE_SHOT));
5180 mRefreshURIList->AppendElement(timer); // owning timer ref
5182 return NS_OK;
5185 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
5186 nsIPrincipal* aPrincipal,
5187 uint32_t aDelay,
5188 nsITimer* aTimer) {
5189 MOZ_ASSERT(aTimer, "Must have a timer here");
5191 // Remove aTimer from mRefreshURIList if needed
5192 if (mRefreshURIList) {
5193 uint32_t n = 0;
5194 mRefreshURIList->GetLength(&n);
5196 for (uint32_t i = 0; i < n; ++i) {
5197 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5198 if (timer == aTimer) {
5199 mRefreshURIList->RemoveElementAt(i);
5200 break;
5205 return ForceRefreshURI(aURI, aPrincipal, aDelay);
5208 NS_IMETHODIMP
5209 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5210 uint32_t aDelay) {
5211 NS_ENSURE_ARG(aURI);
5213 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
5214 loadState->SetOriginalURI(mCurrentURI);
5215 loadState->SetResultPrincipalURI(aURI);
5216 loadState->SetResultPrincipalURIIsSome(true);
5217 loadState->SetKeepResultPrincipalURIIfSet(true);
5218 loadState->SetIsMetaRefresh(true);
5220 // Set the triggering pricipal to aPrincipal if available, or current
5221 // document's principal otherwise.
5222 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
5223 RefPtr<Document> doc = GetDocument();
5224 if (!principal) {
5225 if (!doc) {
5226 return NS_ERROR_FAILURE;
5228 principal = doc->NodePrincipal();
5230 loadState->SetTriggeringPrincipal(principal);
5231 if (doc) {
5232 loadState->SetCsp(doc->GetCsp());
5233 loadState->SetHasValidUserGestureActivation(
5234 doc->HasValidTransientUserGestureActivation());
5235 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
5238 loadState->SetPrincipalIsExplicit(true);
5240 /* Check if this META refresh causes a redirection
5241 * to another site.
5243 bool equalUri = false;
5244 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5246 nsCOMPtr<nsIReferrerInfo> referrerInfo;
5247 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
5248 /* It is a META refresh based redirection within the threshold time
5249 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5250 * Pass a REPLACE flag to LoadURI().
5252 loadState->SetLoadType(LOAD_REFRESH_REPLACE);
5254 /* For redirects we mimic HTTP, which passes the
5255 * original referrer.
5256 * We will pass in referrer but will not send to server
5258 if (mReferrerInfo) {
5259 referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5260 ->CloneWithNewSendReferrer(false);
5262 } else {
5263 loadState->SetLoadType(LOAD_REFRESH);
5264 /* We do need to pass in a referrer, but we don't want it to
5265 * be sent to the server.
5266 * For most refreshes the current URI is an appropriate
5267 * internal referrer.
5269 referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5272 loadState->SetReferrerInfo(referrerInfo);
5273 loadState->SetLoadFlags(
5274 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5275 loadState->SetFirstParty(true);
5278 * LoadURI(...) will cancel all refresh timers... This causes the
5279 * Timer and its refreshData instance to be released...
5281 LoadURI(loadState, false);
5283 return NS_OK;
5286 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
5287 const char16_t* aEnd) {
5288 const char16_t* iter = aStart;
5289 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
5290 ++iter;
5292 return iter;
5295 static std::tuple<const char16_t*, const char16_t*> ExtractURLString(
5296 const char16_t* aPosition, const char16_t* aEnd) {
5297 MOZ_ASSERT(aPosition != aEnd);
5299 // 1. Let urlString be the substring of input from the code point at
5300 // position to the end of the string.
5301 const char16_t* urlStart = aPosition;
5302 const char16_t* urlEnd = aEnd;
5304 // 2. If the code point in input pointed to by position is U+0055 (U) or
5305 // U+0075 (u), then advance position to the next code point.
5306 // Otherwise, jump to the step labeled skip quotes.
5307 if (*aPosition == 'U' || *aPosition == 'u') {
5308 ++aPosition;
5310 // 3. If the code point in input pointed to by position is U+0052 (R) or
5311 // U+0072 (r), then advance position to the next code point.
5312 // Otherwise, jump to the step labeled parse.
5313 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
5314 return std::make_tuple(urlStart, urlEnd);
5317 ++aPosition;
5319 // 4. If the code point in input pointed to by position is U+004C (L) or
5320 // U+006C (l), then advance position to the next code point.
5321 // Otherwise, jump to the step labeled parse.
5322 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
5323 return std::make_tuple(urlStart, urlEnd);
5326 ++aPosition;
5328 // 5. Skip ASCII whitespace within input given position.
5329 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5331 // 6. If the code point in input pointed to by position is U+003D (=),
5332 // then advance position to the next code point. Otherwise, jump to
5333 // the step labeled parse.
5334 if (aPosition == aEnd || *aPosition != '=') {
5335 return std::make_tuple(urlStart, urlEnd);
5338 ++aPosition;
5340 // 7. Skip ASCII whitespace within input given position.
5341 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5344 // 8. Skip quotes: If the code point in input pointed to by position is
5345 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5346 // advance position to the next code point. Otherwise, let quote be
5347 // the empty string.
5348 Maybe<char> quote;
5349 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
5350 quote.emplace(*aPosition);
5351 ++aPosition;
5354 // 9. Set urlString to the substring of input from the code point at
5355 // position to the end of the string.
5356 urlStart = aPosition;
5357 urlEnd = aEnd;
5359 // 10. If quote is not the empty string, and there is a code point in
5360 // urlString equal to quote, then truncate urlString at that code
5361 // point, so that it and all subsequent code points are removed.
5362 const char16_t* quotePos;
5363 if (quote.isSome() &&
5364 (quotePos = nsCharTraits<char16_t>::find(
5365 urlStart, std::distance(urlStart, aEnd), quote.value()))) {
5366 urlEnd = quotePos;
5369 return std::make_tuple(urlStart, urlEnd);
5372 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
5373 const nsAString& aHeader) {
5374 if (mIsBeingDestroyed) {
5375 return;
5378 const char16_t* position = aHeader.BeginReading();
5379 const char16_t* end = aHeader.EndReading();
5381 // See
5382 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5384 // 3. Skip ASCII whitespace
5385 position = SkipASCIIWhitespace(position, end);
5387 // 4. Let time be 0.
5388 CheckedInt<uint32_t> milliSeconds;
5390 // 5. Collect a sequence of code points that are ASCII digits
5391 const char16_t* digitsStart = position;
5392 while (position != end && mozilla::IsAsciiDigit(*position)) {
5393 ++position;
5396 if (position == digitsStart) {
5397 // 6. If timeString is the empty string, then:
5398 // 1. If the code point in input pointed to by position is not U+002E
5399 // (.), then return.
5400 if (position == end || *position != '.') {
5401 return;
5403 } else {
5404 // 7. Otherwise, set time to the result of parsing timeString using the
5405 // rules for parsing non-negative integers.
5406 nsContentUtils::ParseHTMLIntegerResultFlags result;
5407 uint32_t seconds =
5408 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
5409 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
5410 if (result & nsContentUtils::eParseHTMLInteger_Error) {
5411 // The spec assumes no errors here (since we only pass ASCII digits in),
5412 // but we can still overflow, so this block should deal with that (and
5413 // only that).
5414 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
5415 return;
5417 MOZ_ASSERT(
5418 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
5420 milliSeconds = seconds;
5421 milliSeconds *= 1000;
5422 if (!milliSeconds.isValid()) {
5423 return;
5427 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5428 // STOP characters (.) from input given position. Ignore any collected
5429 // characters.
5430 while (position != end &&
5431 (mozilla::IsAsciiDigit(*position) || *position == '.')) {
5432 ++position;
5435 // 9. Let urlRecord be document's URL.
5436 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
5438 // 10. If position is not past the end of input
5439 if (position != end) {
5440 // 1. If the code point in input pointed to by position is not U+003B (;),
5441 // U+002C (,), or ASCII whitespace, then return.
5442 if (*position != ';' && *position != ',' &&
5443 !mozilla::IsAsciiWhitespace(*position)) {
5444 return;
5447 // 2. Skip ASCII whitespace within input given position.
5448 position = SkipASCIIWhitespace(position, end);
5450 // 3. If the code point in input pointed to by position is U+003B (;) or
5451 // U+002C (,), then advance position to the next code point.
5452 if (position != end && (*position == ';' || *position == ',')) {
5453 ++position;
5455 // 4. Skip ASCII whitespace within input given position.
5456 position = SkipASCIIWhitespace(position, end);
5459 // 11. If position is not past the end of input, then:
5460 if (position != end) {
5461 const char16_t* urlStart;
5462 const char16_t* urlEnd;
5464 // 1-10. See ExtractURLString.
5465 std::tie(urlStart, urlEnd) = ExtractURLString(position, end);
5467 // 11. Parse: Parse urlString relative to document. If that fails, return.
5468 // Otherwise, set urlRecord to the resulting URL record.
5469 nsresult rv =
5470 NS_NewURI(getter_AddRefs(urlRecord),
5471 Substring(urlStart, std::distance(urlStart, urlEnd)),
5472 /* charset = */ nullptr, aDocument->GetDocBaseURI());
5473 NS_ENSURE_SUCCESS_VOID(rv);
5477 nsIPrincipal* principal = aDocument->NodePrincipal();
5478 nsCOMPtr<nsIScriptSecurityManager> securityManager =
5479 nsContentUtils::GetSecurityManager();
5480 nsresult rv = securityManager->CheckLoadURIWithPrincipal(
5481 principal, urlRecord,
5482 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5483 aDocument->InnerWindowID());
5484 NS_ENSURE_SUCCESS_VOID(rv);
5486 bool isjs = true;
5487 rv = NS_URIChainHasFlags(
5488 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5489 NS_ENSURE_SUCCESS_VOID(rv);
5491 if (isjs) {
5492 return;
5495 RefreshURI(urlRecord, principal, milliSeconds.value());
5498 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5499 if (!aTimerList) {
5500 return;
5503 uint32_t n = 0;
5504 aTimerList->GetLength(&n);
5506 while (n) {
5507 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5509 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5511 if (timer) {
5512 timer->Cancel();
5517 NS_IMETHODIMP
5518 nsDocShell::CancelRefreshURITimers() {
5519 DoCancelRefreshURITimers(mRefreshURIList);
5520 DoCancelRefreshURITimers(mSavedRefreshURIList);
5521 DoCancelRefreshURITimers(mBFCachedRefreshURIList);
5522 mRefreshURIList = nullptr;
5523 mSavedRefreshURIList = nullptr;
5524 mBFCachedRefreshURIList = nullptr;
5526 return NS_OK;
5529 NS_IMETHODIMP
5530 nsDocShell::GetRefreshPending(bool* aResult) {
5531 if (!mRefreshURIList) {
5532 *aResult = false;
5533 return NS_OK;
5536 uint32_t count;
5537 nsresult rv = mRefreshURIList->GetLength(&count);
5538 if (NS_SUCCEEDED(rv)) {
5539 *aResult = (count != 0);
5541 return rv;
5544 void nsDocShell::RefreshURIToQueue() {
5545 if (mRefreshURIList) {
5546 uint32_t n = 0;
5547 mRefreshURIList->GetLength(&n);
5549 for (uint32_t i = 0; i < n; ++i) {
5550 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5551 if (!timer) {
5552 continue; // this must be a nsRefreshURI already
5555 // Replace this timer object with a nsRefreshTimer object.
5556 nsCOMPtr<nsITimerCallback> callback;
5557 timer->GetCallback(getter_AddRefs(callback));
5559 timer->Cancel();
5561 mRefreshURIList->ReplaceElementAt(callback, i);
5566 NS_IMETHODIMP
5567 nsDocShell::SuspendRefreshURIs() {
5568 RefreshURIToQueue();
5570 // Suspend refresh URIs for our child shells as well.
5571 for (auto* child : mChildList.ForwardRange()) {
5572 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5573 if (shell) {
5574 shell->SuspendRefreshURIs();
5578 return NS_OK;
5581 NS_IMETHODIMP
5582 nsDocShell::ResumeRefreshURIs() {
5583 RefreshURIFromQueue();
5585 // Resume refresh URIs for our child shells as well.
5586 for (auto* child : mChildList.ForwardRange()) {
5587 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5588 if (shell) {
5589 shell->ResumeRefreshURIs();
5593 return NS_OK;
5596 nsresult nsDocShell::RefreshURIFromQueue() {
5597 if (!mRefreshURIList) {
5598 return NS_OK;
5600 uint32_t n = 0;
5601 mRefreshURIList->GetLength(&n);
5603 while (n) {
5604 nsCOMPtr<nsITimerCallback> refreshInfo =
5605 do_QueryElementAt(mRefreshURIList, --n);
5607 if (refreshInfo) {
5608 // This is the nsRefreshTimer object, waiting to be
5609 // setup in a timer object and fired.
5610 // Create the timer and trigger it.
5611 uint32_t delay = static_cast<nsRefreshTimer*>(
5612 static_cast<nsITimerCallback*>(refreshInfo))
5613 ->GetDelay();
5614 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5615 if (win) {
5616 nsCOMPtr<nsITimer> timer;
5617 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5618 nsITimer::TYPE_ONE_SHOT);
5620 if (timer) {
5621 // Replace the nsRefreshTimer element in the queue with
5622 // its corresponding timer object, so that in case another
5623 // load comes through before the timer can go off, the timer will
5624 // get cancelled in CancelRefreshURITimer()
5625 mRefreshURIList->ReplaceElementAt(timer, n);
5631 return NS_OK;
5634 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) {
5635 nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
5636 bool firstPart = false;
5637 return multiPartChannel &&
5638 NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) &&
5639 !firstPart;
5642 nsresult nsDocShell::Embed(nsIContentViewer* aContentViewer,
5643 WindowGlobalChild* aWindowActor,
5644 bool aIsTransientAboutBlank, bool aPersist,
5645 nsIRequest* aRequest, nsIURI* aPreviousURI) {
5646 // Save the LayoutHistoryState of the previous document, before
5647 // setting up new document
5648 PersistLayoutHistoryState();
5650 nsresult rv = SetupNewViewer(aContentViewer, aWindowActor);
5651 NS_ENSURE_SUCCESS(rv, rv);
5653 // XXX What if SetupNewViewer fails?
5654 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
5655 // Set history.state
5656 SetDocCurrentStateObj(mLSHE,
5657 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
5660 if (mLSHE) {
5661 // Restore the editing state, if it's stored in session history.
5662 if (mLSHE->HasDetachedEditor()) {
5663 ReattachEditorToWindow(mLSHE);
5666 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5669 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() &&
5670 !IsFollowupPartOfMultipart(aRequest)) {
5671 bool expired = false;
5672 uint32_t cacheKey = 0;
5673 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
5674 if (cacheChannel) {
5675 // Check if the page has expired from cache
5676 uint32_t expTime = 0;
5677 cacheChannel->GetCacheTokenExpirationTime(&expTime);
5678 uint32_t now = PRTimeToSeconds(PR_Now());
5679 if (expTime <= now) {
5680 expired = true;
5683 // The checks for updating cache key are similar to the old session
5684 // history in OnNewURI. Try to update the cache key if
5685 // - we should update session history and aren't doing a session
5686 // history load.
5687 // - we're doing a forced reload.
5688 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
5689 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
5690 IsForceReloadType(mLoadType)) {
5691 cacheChannel->GetCacheKey(&cacheKey);
5695 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
5696 MoveLoadingToActiveEntry(aPersist, expired, cacheKey, aPreviousURI);
5699 bool updateHistory = true;
5701 // Determine if this type of load should update history
5702 switch (mLoadType) {
5703 case LOAD_NORMAL_REPLACE:
5704 case LOAD_REFRESH_REPLACE:
5705 case LOAD_STOP_CONTENT_AND_REPLACE:
5706 case LOAD_RELOAD_BYPASS_CACHE:
5707 case LOAD_RELOAD_BYPASS_PROXY:
5708 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5709 case LOAD_REPLACE_BYPASS_CACHE:
5710 updateHistory = false;
5711 break;
5712 default:
5713 break;
5716 if (!updateHistory) {
5717 SetLayoutHistoryState(nullptr);
5720 return NS_OK;
5723 //*****************************************************************************
5724 // nsDocShell::nsIWebProgressListener
5725 //*****************************************************************************
5727 NS_IMETHODIMP
5728 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5729 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5730 int32_t aCurTotalProgress,
5731 int32_t aMaxTotalProgress) {
5732 return NS_OK;
5735 NS_IMETHODIMP
5736 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5737 uint32_t aStateFlags, nsresult aStatus) {
5738 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5739 // Save timing statistics.
5740 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5741 nsCOMPtr<nsIURI> uri;
5742 channel->GetURI(getter_AddRefs(uri));
5743 nsAutoCString aURI;
5744 uri->GetAsciiSpec(aURI);
5746 if (this == aProgress) {
5747 mozilla::Unused << MaybeInitTiming();
5748 mTiming->NotifyFetchStart(uri,
5749 ConvertLoadTypeToNavigationType(mLoadType));
5750 // If we are starting a DocumentChannel, we need to pass the timing
5751 // statistics so that should a process switch occur, the starting type can
5752 // be passed to the new DocShell running in the other content process.
5753 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5754 docChannel->SetNavigationTiming(mTiming);
5758 // Page has begun to load
5759 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5761 if ((aStateFlags & STATE_RESTORING) == 0) {
5762 // Show the progress cursor if the pref is set
5763 if (StaticPrefs::ui_use_activity_cursor()) {
5764 nsCOMPtr<nsIWidget> mainWidget;
5765 GetMainWidget(getter_AddRefs(mainWidget));
5766 if (mainWidget) {
5767 mainWidget->SetCursor(nsIWidget::Cursor{eCursor_spinning});
5771 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
5772 if (IsForceReloadType(mLoadType)) {
5773 if (WindowContext* windowContext =
5774 mBrowsingContext->GetCurrentWindowContext()) {
5775 SessionStoreChild::From(windowContext->GetWindowGlobalChild())
5776 ->ResetSessionStore(mBrowsingContext,
5777 mBrowsingContext->GetSessionStoreEpoch());
5782 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5783 // Page is loading
5784 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5785 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5786 // Page has finished loading
5787 mBusyFlags = BUSY_FLAGS_NONE;
5789 // Hide the progress cursor if the pref is set
5790 if (StaticPrefs::ui_use_activity_cursor()) {
5791 nsCOMPtr<nsIWidget> mainWidget;
5792 GetMainWidget(getter_AddRefs(mainWidget));
5793 if (mainWidget) {
5794 mainWidget->SetCursor(nsIWidget::Cursor{eCursor_standard});
5799 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5800 nsCOMPtr<nsIWebProgress> webProgress =
5801 do_QueryInterface(GetAsSupports(this));
5802 // Is the document stop notification for this document?
5803 if (aProgress == webProgress.get()) {
5804 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5805 EndPageLoad(aProgress, channel, aStatus);
5808 // note that redirect state changes will go through here as well, but it
5809 // is better to handle those in OnRedirectStateChange where more
5810 // information is available.
5811 return NS_OK;
5814 NS_IMETHODIMP
5815 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5816 nsIURI* aURI, uint32_t aFlags) {
5817 // Since we've now changed Documents, notify the BrowsingContext that we've
5818 // changed. Ideally we'd just let the BrowsingContext do this when it
5819 // changes the current window global, but that happens before this and we
5820 // have a lot of tests that depend on the specific ordering of messages.
5821 bool isTopLevel = false;
5822 if (XRE_IsParentProcess() &&
5823 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
5824 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
5825 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5827 return NS_OK;
5830 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5831 nsIChannel* aNewChannel,
5832 uint32_t aRedirectFlags,
5833 uint32_t aStateFlags) {
5834 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5835 "Calling OnRedirectStateChange when there is no redirect");
5837 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5838 return; // not a toplevel document
5841 nsCOMPtr<nsIURI> oldURI, newURI;
5842 aOldChannel->GetURI(getter_AddRefs(oldURI));
5843 aNewChannel->GetURI(getter_AddRefs(newURI));
5844 if (!oldURI || !newURI) {
5845 return;
5848 // DocumentChannel adds redirect chain to global history in the parent
5849 // process. The redirect chain can't be queried from the content process, so
5850 // there's no need to update global history here.
5851 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5852 if (!docChannel) {
5853 // Below a URI visit is saved (see AddURIVisit method doc).
5854 // The visit chain looks something like:
5855 // ...
5856 // Site N - 1
5857 // => Site N
5858 // (redirect to =>) Site N + 1 (we are here!)
5860 // Get N - 1 and transition type
5861 nsCOMPtr<nsIURI> previousURI;
5862 uint32_t previousFlags = 0;
5863 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5865 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5866 net::ChannelIsPost(aOldChannel)) {
5867 // 1. Internal redirects are ignored because they are specific to the
5868 // channel implementation.
5869 // 2. POSTs are not saved by global history.
5871 // Regardless, we need to propagate the previous visit to the new
5872 // channel.
5873 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5874 } else {
5875 // Get the HTTP response code, if available.
5876 uint32_t responseStatus = 0;
5877 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5878 if (httpChannel) {
5879 Unused << httpChannel->GetResponseStatus(&responseStatus);
5882 // Add visit N -1 => N
5883 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5885 // Since N + 1 could be the final destination, we will not save N => N + 1
5886 // here. OnNewURI will do that, so we will cache it.
5887 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5891 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5892 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5893 mLoadType = LOAD_NORMAL_REPLACE;
5894 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5898 NS_IMETHODIMP
5899 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5900 nsresult aStatus, const char16_t* aMessage) {
5901 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5902 return NS_OK;
5905 NS_IMETHODIMP
5906 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5907 uint32_t aState) {
5908 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5909 return NS_OK;
5912 NS_IMETHODIMP
5913 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5914 nsIRequest* aRequest, uint32_t aEvent) {
5915 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5916 return NS_OK;
5919 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5920 const nsACString& aKeyword, bool aIsPrivateContext) {
5921 nsCOMPtr<nsIURIFixupInfo> info;
5922 if (!XRE_IsContentProcess()) {
5923 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5924 if (uriFixup) {
5925 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
5928 return info.forget();
5931 /* static */
5932 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
5933 nsIChannel* aChannel, nsIURI* aUrl) {
5934 if (!aChannel) {
5935 return nullptr;
5938 nsresult rv = NS_OK;
5939 nsAutoCString host;
5940 rv = aUrl->GetAsciiHost(host);
5941 if (NS_WARN_IF(NS_FAILED(rv))) {
5942 return nullptr;
5945 // No point in going further if "www." is included in the hostname
5946 // already. That is the only hueristic we're applying in this function.
5947 if (StringBeginsWith(host, "www."_ns)) {
5948 return nullptr;
5951 // Return if fixup enable pref is turned off.
5952 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5953 return nullptr;
5956 // Return if scheme is not HTTPS.
5957 if (!SchemeIsHTTPS(aUrl)) {
5958 return nullptr;
5961 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
5962 if (!info) {
5963 return nullptr;
5966 // Skip doing the fixup if our channel was redirected, because we
5967 // shouldn't be guessing things about the post-redirect URI.
5968 if (!info->RedirectChain().IsEmpty()) {
5969 return nullptr;
5972 int32_t port = 0;
5973 rv = aUrl->GetPort(&port);
5974 if (NS_WARN_IF(NS_FAILED(rv))) {
5975 return nullptr;
5978 // Don't fix up hosts with ports.
5979 if (port != -1) {
5980 return nullptr;
5983 // Don't fix up localhost url.
5984 if (host == "localhost") {
5985 return nullptr;
5988 // Don't fix up hostnames with IP address.
5989 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
5990 return nullptr;
5993 nsAutoCString userPass;
5994 rv = aUrl->GetUserPass(userPass);
5995 if (NS_WARN_IF(NS_FAILED(rv))) {
5996 return nullptr;
5999 // Security - URLs with user / password info should NOT be modified.
6000 if (!userPass.IsEmpty()) {
6001 return nullptr;
6004 nsCOMPtr<nsITransportSecurityInfo> tsi;
6005 rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi));
6006 if (NS_WARN_IF(NS_FAILED(rv))) {
6007 return nullptr;
6010 if (NS_WARN_IF(!tsi)) {
6011 return nullptr;
6014 nsCOMPtr<nsIX509Cert> cert;
6015 rv = tsi->GetServerCert(getter_AddRefs(cert));
6016 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
6017 return nullptr;
6020 nsTArray<uint8_t> certBytes;
6021 rv = cert->GetRawDER(certBytes);
6022 if (NS_FAILED(rv)) {
6023 return nullptr;
6026 mozilla::pkix::Input serverCertInput;
6027 mozilla::pkix::Result rv1 =
6028 serverCertInput.Init(certBytes.Elements(), certBytes.Length());
6029 if (rv1 != mozilla::pkix::Success) {
6030 return nullptr;
6033 nsAutoCString newHost("www."_ns);
6034 newHost.Append(host);
6036 mozilla::pkix::Input newHostInput;
6037 rv1 = newHostInput.Init(
6038 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
6039 newHost.Length());
6040 if (rv1 != mozilla::pkix::Success) {
6041 return nullptr;
6044 // Check if adding a "www." prefix to the request's hostname will
6045 // cause the response's certificate to match.
6046 rv1 = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput);
6047 if (rv1 != mozilla::pkix::Success) {
6048 return nullptr;
6051 nsCOMPtr<nsIURI> newURI;
6052 Unused << NS_MutateURI(aUrl).SetHost(newHost).Finalize(
6053 getter_AddRefs(newURI));
6055 return newURI.forget();
6058 /* static */
6059 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
6060 nsIChannel* aChannel, nsresult aStatus,
6061 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
6062 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
6063 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData) {
6064 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
6065 aStatus != NS_ERROR_CONNECTION_REFUSED &&
6066 aStatus !=
6067 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6068 return nullptr;
6071 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
6072 return nullptr;
6075 nsCOMPtr<nsIURI> url;
6076 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6077 if (NS_FAILED(rv)) {
6078 return nullptr;
6082 // Try and make an alternative URI from the old one
6084 nsCOMPtr<nsIURI> newURI;
6085 nsCOMPtr<nsIInputStream> newPostData;
6087 nsAutoCString oldSpec;
6088 url->GetSpec(oldSpec);
6091 // First try keyword fixup
6093 nsAutoString keywordProviderName, keywordAsSent;
6094 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
6095 // we should only perform a keyword search under the following
6096 // conditions:
6097 // (0) Pref keyword.enabled is true
6098 // (1) the url scheme is http (or https)
6099 // (2) the url does not have a protocol scheme
6100 // If we don't enforce such a policy, then we end up doing
6101 // keyword searchs on urls we don't intend like imap, file,
6102 // mailbox, etc. This could lead to a security problem where we
6103 // send data to the keyword server that we shouldn't be.
6104 // Someone needs to clean up keywords in general so we can
6105 // determine on a per url basis if we want keywords
6106 // enabled...this is just a bandaid...
6107 nsAutoCString scheme;
6108 Unused << url->GetScheme(scheme);
6109 if (Preferences::GetBool("keyword.enabled", false) &&
6110 StringBeginsWith(scheme, "http"_ns)) {
6111 bool attemptFixup = false;
6112 nsAutoCString host;
6113 Unused << url->GetHost(host);
6114 if (host.FindChar('.') == kNotFound) {
6115 attemptFixup = true;
6116 } else {
6117 // For domains with dots, we check the public suffix validity.
6118 nsCOMPtr<nsIEffectiveTLDService> tldService =
6119 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
6120 if (tldService) {
6121 nsAutoCString suffix;
6122 attemptFixup =
6123 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
6124 suffix.IsEmpty();
6127 if (attemptFixup) {
6128 nsCOMPtr<nsIURIFixupInfo> info;
6129 // only send non-qualified hosts to the keyword server
6130 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
6131 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
6132 } else {
6134 // If this string was passed through nsStandardURL by
6135 // chance, then it may have been converted from UTF-8 to
6136 // ACE, which would result in a completely bogus keyword
6137 // query. Here we try to recover the original Unicode
6138 // value, but this is not 100% correct since the value may
6139 // have been normalized per the IDN normalization rules.
6141 // Since we don't have access to the exact original string
6142 // that was entered by the user, this will just have to do.
6143 bool isACE;
6144 nsAutoCString utf8Host;
6145 nsCOMPtr<nsIIDNService> idnSrv =
6146 do_GetService(NS_IDNSERVICE_CONTRACTID);
6147 if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6148 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6149 info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
6151 } else {
6152 info = KeywordToURI(host, aUsePrivateBrowsing);
6155 if (info) {
6156 info->GetPreferredURI(getter_AddRefs(newURI));
6157 if (newURI) {
6158 info->GetKeywordAsSent(keywordAsSent);
6159 info->GetKeywordProviderName(keywordProviderName);
6160 info->GetPostData(getter_AddRefs(newPostData));
6168 // Now try change the address, e.g. turn http://foo into
6169 // http://www.foo.com, and if that doesn't work try https with
6170 // https://foo and https://www.foo.com.
6172 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6173 // Skip fixup for anything except a normal document load
6174 // operation on the topframe.
6175 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
6177 if (doCreateAlternate) {
6178 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6179 nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
6180 // Only do this if our channel was loaded directly by the user from the
6181 // URL bar or similar (system principal) and not redirected, because we
6182 // shouldn't be guessing things about links from other sites, or a
6183 // post-redirect URI.
6184 doCreateAlternate = principal && principal->IsSystemPrincipal() &&
6185 loadInfo->RedirectChain().IsEmpty();
6187 // Test if keyword lookup produced a new URI or not
6188 if (doCreateAlternate && newURI) {
6189 bool sameURI = false;
6190 url->Equals(newURI, &sameURI);
6191 if (!sameURI) {
6192 // Keyword lookup made a new URI so no need to try
6193 // an alternate one.
6194 doCreateAlternate = false;
6197 if (doCreateAlternate) {
6198 newURI = nullptr;
6199 newPostData = nullptr;
6200 keywordProviderName.Truncate();
6201 keywordAsSent.Truncate();
6202 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6203 if (uriFixup) {
6204 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
6205 uriFixup->GetFixupURIInfo(oldSpec,
6206 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
6207 getter_AddRefs(fixupInfo));
6208 if (fixupInfo) {
6209 fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
6213 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6214 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6215 // Try HTTPS, since http didn't work
6216 if (SchemeIsHTTP(url)) {
6217 int32_t port = 0;
6218 url->GetPort(&port);
6220 // Fall back to HTTPS only if port is default
6221 if (port == -1) {
6222 newURI = nullptr;
6223 newPostData = nullptr;
6224 Unused << NS_MutateURI(url)
6225 .SetScheme("https"_ns)
6226 .Finalize(getter_AddRefs(newURI));
6231 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try prefixing the domain name
6232 // with www. to see if we can avoid showing the cert error page. For example,
6233 // https://example.com -> https://www.example.com.
6234 if (aStatus ==
6235 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6236 newPostData = nullptr;
6237 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
6240 // Did we make a new URI that is different to the old one? If so
6241 // load it.
6243 if (newURI) {
6244 // Make sure the new URI is different from the old one,
6245 // otherwise there's little point trying to load it again.
6246 bool sameURI = false;
6247 url->Equals(newURI, &sameURI);
6248 if (!sameURI) {
6249 if (aNewPostData) {
6250 newPostData.forget(aNewPostData);
6252 if (aNotifyKeywordSearchLoading) {
6253 // This notification is meant for Firefox Health Report so it
6254 // can increment counts from the search engine
6255 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6257 return newURI.forget();
6261 return nullptr;
6264 nsresult nsDocShell::FilterStatusForErrorPage(
6265 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
6266 bool aIsTopFrame, bool aUseErrorPages, bool aIsInitialDocument,
6267 bool* aSkippedUnknownProtocolNavigation) {
6268 // Errors to be shown only on top-level frames
6269 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6270 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6271 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6272 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6273 aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6274 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6275 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6276 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6277 aStatus == NS_ERROR_MALFORMED_URI ||
6278 aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
6279 aStatus == NS_ERROR_DOM_COOP_FAILED ||
6280 aStatus == NS_ERROR_DOM_COEP_FAILED) &&
6281 (aIsTopFrame || aUseErrorPages)) {
6282 return aStatus;
6285 if (aStatus == NS_ERROR_NET_TIMEOUT ||
6286 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
6287 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6288 aStatus == NS_ERROR_REDIRECT_LOOP ||
6289 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6290 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
6291 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
6292 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
6293 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
6294 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6295 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6296 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6297 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6298 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6299 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
6300 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
6301 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
6302 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
6303 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6304 // Errors to be shown for any frame
6305 return aStatus;
6308 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6309 // For unknown protocols we only display an error if the load is triggered
6310 // by the browser itself, or we're replacing the initial document (and
6311 // nothing else). Showing the error for page-triggered navigations causes
6312 // annoying behavior for users, see bug 1528305.
6314 // We could, maybe, try to detect if this is in response to some user
6315 // interaction (like clicking a link, or something else) and maybe show
6316 // the error page in that case. But this allows for ctrl+clicking and such
6317 // to see the error page.
6318 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6319 if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6320 StaticPrefs::dom_no_unknown_protocol_error_enabled() &&
6321 !aIsInitialDocument) {
6322 if (aSkippedUnknownProtocolNavigation) {
6323 *aSkippedUnknownProtocolNavigation = true;
6325 return NS_OK;
6327 return aStatus;
6330 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6331 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6332 // Caching channels would have to look at their flags to work
6333 // out which error to return. Or we can fix up the error here.
6334 if (!(aLoadType & LOAD_CMD_HISTORY)) {
6335 return NS_ERROR_OFFLINE;
6337 return aStatus;
6340 return NS_OK;
6343 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6344 nsIChannel* aChannel, nsresult aStatus) {
6345 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
6346 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
6347 static_cast<uint32_t>(aStatus)));
6348 if (!aChannel) {
6349 return NS_ERROR_NULL_POINTER;
6352 // Make sure to discard the initial client if we never created the initial
6353 // about:blank document. Do this before possibly returning from the method
6354 // due to an error.
6355 mInitialClientSource.reset();
6357 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6358 if (reporter) {
6359 nsCOMPtr<nsILoadGroup> loadGroup;
6360 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6361 if (loadGroup) {
6362 reporter->FlushConsoleReports(loadGroup);
6363 } else {
6364 reporter->FlushConsoleReports(GetDocument());
6368 nsCOMPtr<nsIURI> url;
6369 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6370 if (NS_FAILED(rv)) {
6371 return rv;
6374 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6375 if (timingChannel) {
6376 TimeStamp channelCreationTime;
6377 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6378 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6379 Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
6380 channelCreationTime);
6384 // Timing is picked up by the window, we don't need it anymore
6385 mTiming = nullptr;
6387 // clean up reload state for meta charset
6388 if (eCharsetReloadRequested == mCharsetReloadState) {
6389 mCharsetReloadState = eCharsetReloadStopOrigional;
6390 } else {
6391 mCharsetReloadState = eCharsetReloadInit;
6394 // Save a pointer to the currently-loading history entry.
6395 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6396 // entry further down in this method.
6397 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6398 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
6401 // one of many safeguards that prevent death and destruction if
6402 // someone is so very very rude as to bring this window down
6403 // during this load handler.
6405 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6407 // Notify the ContentViewer that the Document has finished loading. This
6408 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6409 if (!mEODForCurrentDocument && mContentViewer) {
6410 mIsExecutingOnLoadHandler = true;
6411 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
6412 contentViewer->LoadComplete(aStatus);
6413 mIsExecutingOnLoadHandler = false;
6415 mEODForCurrentDocument = true;
6417 /* Check if the httpChannel has any cache-control related response headers,
6418 * like no-store, no-cache. If so, update SHEntry so that
6419 * when a user goes back/forward to this page, we appropriately do
6420 * form value restoration or load from server.
6422 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6423 if (!httpChannel) {
6424 // HttpChannel could be hiding underneath a Multipart channel.
6425 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6428 if (httpChannel) {
6429 // figure out if SH should be saving layout state.
6430 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6431 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6432 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
6433 mLSHE->SetSaveLayoutStateFlag(false);
6437 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6438 // onLoadHandler tries to load something different in
6439 // itself or one of its children, we can deal with it appropriately.
6440 if (mLSHE) {
6441 mLSHE->SetLoadType(LOAD_HISTORY);
6443 // Clear the mLSHE reference to indicate document loading is done one
6444 // way or another.
6445 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6447 mActiveEntryIsLoadingFromSessionHistory = false;
6449 // if there's a refresh header in the channel, this method
6450 // will set it up for us.
6451 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
6452 RefreshURIFromQueue();
6454 // Test whether this is the top frame or a subframe
6455 bool isTopFrame = mBrowsingContext->IsTop();
6457 bool hadErrorStatus = false;
6458 // If status code indicates an error it means that DocumentChannel already
6459 // tried to fixup the uri and failed. Throw an error dialog box here.
6460 if (NS_FAILED(aStatus)) {
6461 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6462 // the error event to our embedder, since tests are relying on this.
6463 // The error event is usually fired by the caller of InternalLoad, but
6464 // this particular error can happen asynchronously.
6465 // Bug 1629201 is filed for having much clearer decision making around
6466 // which cases need error events.
6467 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6468 aStatus == NS_ERROR_CONTENT_BLOCKED);
6469 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6471 bool isInitialDocument =
6472 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6473 bool skippedUnknownProtocolNavigation = false;
6474 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
6475 mBrowsingContext->GetUseErrorPages(),
6476 isInitialDocument,
6477 &skippedUnknownProtocolNavigation);
6478 hadErrorStatus = true;
6479 if (NS_FAILED(aStatus)) {
6480 if (!mIsBeingDestroyed) {
6481 DisplayLoadError(aStatus, url, nullptr, aChannel);
6483 } else if (skippedUnknownProtocolNavigation) {
6484 nsTArray<nsString> params;
6485 if (NS_FAILED(
6486 NS_GetSanitizedURIStringFromURI(url, *params.AppendElement()))) {
6487 params.LastElement().AssignLiteral(u"(unknown uri)");
6489 nsContentUtils::ReportToConsole(
6490 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
6491 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
6492 params);
6494 } else {
6495 // If we have a host
6496 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6497 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6500 if (hadErrorStatus) {
6501 // Don't send session store updates if the reason EndPageLoad was called is
6502 // because we are process switching. Sometimes the update takes too long and
6503 // incorrectly overrides session store data from the following load.
6504 return NS_OK;
6506 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
6507 if (WindowContext* windowContext =
6508 mBrowsingContext->GetCurrentWindowContext()) {
6509 using Change = SessionStoreChangeListener::Change;
6511 // We've finished loading the page and now we want to collect all the
6512 // session store state that the page is initialized with.
6513 SessionStoreChangeListener::CollectSessionStoreData(
6514 windowContext,
6515 EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory,
6516 Change::WireFrame));
6520 return NS_OK;
6523 //*****************************************************************************
6524 // nsDocShell: Content Viewer Management
6525 //*****************************************************************************
6527 nsresult nsDocShell::EnsureContentViewer() {
6528 if (mContentViewer) {
6529 return NS_OK;
6531 if (mIsBeingDestroyed) {
6532 return NS_ERROR_FAILURE;
6535 nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6536 nsCOMPtr<nsIURI> baseURI;
6537 nsIPrincipal* principal = GetInheritedPrincipal(false);
6538 nsIPrincipal* partitionedPrincipal = GetInheritedPrincipal(false, true);
6540 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6541 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6542 if (parentItem) {
6543 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6544 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6545 if (parentElement) {
6546 baseURI = parentElement->GetBaseURI();
6547 cspToInheritForAboutBlank = parentElement->GetCsp();
6552 nsresult rv = CreateAboutBlankContentViewer(
6553 principal, partitionedPrincipal, cspToInheritForAboutBlank, baseURI,
6554 /* aIsInitialDocument */ true);
6556 NS_ENSURE_STATE(mContentViewer);
6558 if (NS_SUCCEEDED(rv)) {
6559 RefPtr<Document> doc(GetDocument());
6560 MOZ_ASSERT(doc,
6561 "Should have doc if CreateAboutBlankContentViewer "
6562 "succeeded!");
6563 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
6565 // Documents created using EnsureContentViewer may be transient
6566 // placeholders created by framescripts before content has a
6567 // chance to load. In some cases, window.open(..., "noopener")
6568 // will create such a document and then synchronously tear it
6569 // down, firing a "pagehide" event. Doing so violates our
6570 // assertions about DocGroups. It's easier to silence the
6571 // assertion here than to avoid creating the extra document.
6572 doc->IgnoreDocGroupMismatches();
6575 return rv;
6578 nsresult nsDocShell::CreateAboutBlankContentViewer(
6579 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
6580 nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI, bool aIsInitialDocument,
6581 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
6582 bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6583 WindowGlobalChild* aActor) {
6584 RefPtr<Document> blankDoc;
6585 nsCOMPtr<nsIContentViewer> viewer;
6586 nsresult rv = NS_ERROR_FAILURE;
6588 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6590 /* mCreatingDocument should never be true at this point. However, it's
6591 a theoretical possibility. We want to know about it and make it stop,
6592 and this sounds like a job for an assertion. */
6593 NS_ASSERTION(!mCreatingDocument,
6594 "infinite(?) loop creating document averted");
6595 if (mCreatingDocument) {
6596 return NS_ERROR_FAILURE;
6599 if (!mBrowsingContext->AncestorsAreCurrent() ||
6600 mBrowsingContext->IsInBFCache()) {
6601 mBrowsingContext->RemoveRootFromBFCacheSync();
6602 return NS_ERROR_NOT_AVAILABLE;
6605 // mContentViewer->PermitUnload may release |this| docshell.
6606 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6608 AutoRestore<bool> creatingDocument(mCreatingDocument);
6609 mCreatingDocument = true;
6611 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6612 mItemType != typeChrome) {
6613 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6614 mBrowsingContext->OriginAttributesRef());
6617 // Make sure timing is created. But first record whether we had it
6618 // already, so we don't clobber the timing for an in-progress load.
6619 bool hadTiming = mTiming;
6620 bool toBeReset = MaybeInitTiming();
6621 if (mContentViewer) {
6622 if (aCheckPermitUnload) {
6623 // We've got a content viewer already. Make sure the user
6624 // permits us to discard the current document and replace it
6625 // with about:blank. And also ensure we fire the unload events
6626 // in the current document.
6628 // Unload gets fired first for
6629 // document loaded from the session history.
6630 mTiming->NotifyBeforeUnload();
6632 bool okToUnload;
6633 rv = mContentViewer->PermitUnload(&okToUnload);
6635 if (NS_SUCCEEDED(rv) && !okToUnload) {
6636 // The user chose not to unload the page, interrupt the load.
6637 MaybeResetInitTiming(toBeReset);
6638 return NS_ERROR_FAILURE;
6640 if (mTiming) {
6641 mTiming->NotifyUnloadAccepted(mCurrentURI);
6645 mSavingOldViewer =
6646 aTryToSaveOldPresentation &&
6647 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
6648 /* aReportBFCacheComboTelemetry */ true);
6650 // Make sure to blow away our mLoadingURI just in case. No loads
6651 // from inside this pagehide.
6652 mLoadingURI = nullptr;
6654 // Stop any in-progress loading, so that we don't accidentally trigger any
6655 // PageShow notifications from Embed() interrupting our loading below.
6656 Stop();
6658 // Notify the current document that it is about to be unloaded!!
6660 // It is important to fire the unload() notification *before* any state
6661 // is changed within the DocShell - otherwise, javascript will get the
6662 // wrong information :-(
6664 (void)FirePageHideNotification(!mSavingOldViewer);
6665 // pagehide notification might destroy this docshell.
6666 if (mIsBeingDestroyed) {
6667 return NS_ERROR_DOCSHELL_DYING;
6671 // Now make sure we don't think we're in the middle of firing unload after
6672 // this point. This will make us fire unload when the about:blank document
6673 // unloads... but that's ok, more or less. Would be nice if it fired load
6674 // too, of course.
6675 mFiredUnloadEvent = false;
6677 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6678 nsContentUtils::FindInternalContentViewer("text/html"_ns);
6680 if (docFactory) {
6681 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6682 const uint32_t sandboxFlags =
6683 mBrowsingContext->GetHasLoadedNonInitialDocument()
6684 ? mBrowsingContext->GetSandboxFlags()
6685 : mBrowsingContext->GetInitialSandboxFlags();
6686 // If we're sandboxed, then create a new null principal. We skip
6687 // this if we're being created from WindowGlobalChild, since in
6688 // that case we already have a null principal if required.
6689 // We can't compare againt the BrowsingContext sandbox flag, since
6690 // the value was taken when the load initiated and may have since
6691 // changed.
6692 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6693 if (aPrincipal) {
6694 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6695 } else {
6696 principal = NullPrincipal::Create(GetOriginAttributes());
6698 partitionedPrincipal = principal;
6699 } else {
6700 principal = aPrincipal;
6701 partitionedPrincipal = aPartitionedPrincipal;
6704 // We cannot get the foreign partitioned prinicpal for the initial
6705 // about:blank page. So, we change to check if we need to use the
6706 // partitioned principal for the service worker here.
6707 MaybeCreateInitialClientSource(
6708 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6709 this)
6710 ? partitionedPrincipal
6711 : principal);
6713 // generate (about:blank) document to load
6714 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6715 partitionedPrincipal, this);
6716 if (blankDoc) {
6717 // Hack: manually set the CSP for the new document
6718 // Please create an actual copy of the CSP (do not share the same
6719 // reference) otherwise appending a new policy within the new
6720 // document will be incorrectly propagated to the opening doc.
6721 if (aCSP) {
6722 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6723 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6724 blankDoc->SetCsp(cspToInherit);
6727 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6729 blankDoc->SetEmbedderPolicy(aCOEP);
6731 // Hack: set the base URI manually, since this document never
6732 // got Reset() with a channel.
6733 blankDoc->SetBaseURI(aBaseURI);
6735 // Copy our sandbox flags to the document. These are immutable
6736 // after being set here.
6737 blankDoc->SetSandboxFlags(sandboxFlags);
6739 blankDoc->InitFeaturePolicy();
6741 // create a content viewer for us and the new document
6742 docFactory->CreateInstanceForDocument(
6743 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6744 getter_AddRefs(viewer));
6746 // hook 'em up
6747 if (viewer) {
6748 viewer->SetContainer(this);
6749 rv = Embed(viewer, aActor, true, false, nullptr, mCurrentURI);
6750 NS_ENSURE_SUCCESS(rv, rv);
6752 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6753 /* aFireLocationChange */ true,
6754 /* aIsInitialAboutBlank */ true,
6755 /* aLocationFlags */ 0);
6756 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6761 // The transient about:blank viewer doesn't have a session history entry.
6762 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6764 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6765 // have one before entering this function.
6766 if (!hadTiming) {
6767 mTiming = nullptr;
6768 mBlankTiming = true;
6771 return rv;
6774 NS_IMETHODIMP
6775 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
6776 nsIPrincipal* aPartitionedPrincipal,
6777 nsIContentSecurityPolicy* aCSP) {
6778 return CreateAboutBlankContentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6779 nullptr, /* aIsInitialDocument */ false);
6782 nsresult nsDocShell::CreateContentViewerForActor(
6783 WindowGlobalChild* aWindowActor) {
6784 MOZ_ASSERT(aWindowActor);
6786 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6787 // FIXME: We may want to support non-initial documents here.
6788 nsresult rv = CreateAboutBlankContentViewer(
6789 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6790 /* aCsp */ nullptr,
6791 /* aBaseURI */ nullptr,
6792 /* aIsInitialDocument */ true,
6793 /* aCOEP */ Nothing(),
6794 /* aTryToSaveOldPresentation */ true,
6795 /* aCheckPermitUnload */ true, aWindowActor);
6796 #ifdef DEBUG
6797 if (NS_SUCCEEDED(rv)) {
6798 RefPtr<Document> doc(GetDocument());
6799 MOZ_ASSERT(
6800 doc,
6801 "Should have a document if CreateAboutBlankContentViewer succeeded");
6802 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6803 "New document should be in the same global as our actor");
6804 MOZ_ASSERT(doc->IsInitialDocument(),
6805 "New document should be an initial document");
6807 #endif
6809 return rv;
6812 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6813 nsIRequest* aNewRequest,
6814 Document* aNewDocument,
6815 bool aReportBFCacheComboTelemetry) {
6816 if (!mOSHE) {
6817 return false; // no entry to save into
6820 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6821 "mOSHE cannot be non-null with SHIP");
6822 nsCOMPtr<nsIContentViewer> viewer = mOSHE->GetContentViewer();
6823 if (viewer) {
6824 NS_WARNING("mOSHE already has a content viewer!");
6825 return false;
6828 // Only save presentation for "normal" loads and link loads. Anything else
6829 // probably wants to refetch the page, so caching the old presentation
6830 // would be incorrect.
6831 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6832 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6833 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6834 aLoadType != LOAD_ERROR_PAGE) {
6835 return false;
6838 // If the session history entry has the saveLayoutState flag set to false,
6839 // then we should not cache the presentation.
6840 if (!mOSHE->GetSaveLayoutStateFlag()) {
6841 return false;
6844 // If the document is not done loading, don't cache it.
6845 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6846 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6847 ("Blocked due to document still loading"));
6848 return false;
6851 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6852 return false;
6855 // Avoid doing the work of saving the presentation state in the case where
6856 // the content viewer cache is disabled.
6857 if (nsSHistory::GetMaxTotalViewers() == 0) {
6858 return false;
6861 // Don't cache the content viewer if we're in a subframe.
6862 if (mBrowsingContext->GetParent()) {
6863 return false; // this is a subframe load
6866 // If the document does not want its presentation cached, then don't.
6867 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6869 uint32_t bfCacheCombo = 0;
6870 bool canSavePresentation =
6871 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6872 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6873 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6874 auto* browsingContextGroup = mBrowsingContext->Group();
6875 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6876 browsingContextGroup->Toplevels();
6878 for (const auto& browsingContext : topLevelContext) {
6879 if (browsingContext != mBrowsingContext) {
6880 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6881 canSavePresentation = false;
6883 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6884 break;
6889 if (aReportBFCacheComboTelemetry) {
6890 ReportBFCacheComboTelemetry(bfCacheCombo);
6892 return doc && canSavePresentation;
6895 /* static */
6896 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6897 // There are 11 possible reasons to make a request fails to use BFCache
6898 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6899 // the common combinations for reasons which make requests fail to use
6900 // BFCache. These combinations are generated based on some local browsings,
6901 // we need to adjust them when necessary.
6902 enum BFCacheStatusCombo : uint32_t {
6903 BFCACHE_SUCCESS,
6904 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6905 // If both unload and beforeunload listeners are presented, it'll be
6906 // recorded as unload
6907 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6908 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6909 mozilla::dom::BFCacheStatus::REQUEST,
6910 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6911 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6912 mozilla::dom::BFCacheStatus::REQUEST |
6913 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6914 UNLOAD_REQUEST_PEER_MSE =
6915 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6916 mozilla::dom::BFCacheStatus::REQUEST |
6917 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6918 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6919 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6920 mozilla::dom::BFCacheStatus::REQUEST |
6921 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6922 SUSPENDED_UNLOAD_REQUEST_PEER =
6923 mozilla::dom::BFCacheStatus::SUSPENDED |
6924 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6925 mozilla::dom::BFCacheStatus::REQUEST |
6926 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6927 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6928 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6931 // Beforeunload is recorded as a blocker only if it is the only one to block
6932 // bfcache.
6933 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6934 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6936 switch (aCombo) {
6937 case BFCACHE_SUCCESS:
6938 Telemetry::AccumulateCategorical(
6939 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6940 break;
6941 case NOT_ONLY_TOPLEVEL:
6942 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6943 Telemetry::AccumulateCategorical(
6944 Telemetry::LABELS_BFCACHE_COMBO::Other);
6945 break;
6947 Telemetry::AccumulateCategorical(
6948 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6949 Telemetry::AccumulateCategorical(
6950 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6951 break;
6952 case UNLOAD:
6953 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6954 break;
6955 case BEFOREUNLOAD:
6956 Telemetry::AccumulateCategorical(
6957 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6958 break;
6959 case UNLOAD_REQUEST:
6960 Telemetry::AccumulateCategorical(
6961 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6962 break;
6963 case REQUEST:
6964 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6965 break;
6966 case UNLOAD_REQUEST_PEER:
6967 Telemetry::AccumulateCategorical(
6968 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6969 break;
6970 case UNLOAD_REQUEST_PEER_MSE:
6971 Telemetry::AccumulateCategorical(
6972 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6973 break;
6974 case UNLOAD_REQUEST_MSE:
6975 Telemetry::AccumulateCategorical(
6976 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6977 break;
6978 case SUSPENDED_UNLOAD_REQUEST_PEER:
6979 Telemetry::AccumulateCategorical(
6980 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6981 break;
6982 case REMOTE_SUBFRAMES:
6983 Telemetry::AccumulateCategorical(
6984 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6985 break;
6986 default:
6987 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6988 break;
6992 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6993 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6994 MOZ_ASSERT(!mIsBeingDestroyed);
6996 NS_ASSERTION(!mEditorData,
6997 "Why reattach an editor when we already have one?");
6998 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6999 "Reattaching when there's not a detached editor.");
7001 if (mEditorData || !aSHEntry) {
7002 return;
7005 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
7006 if (mEditorData) {
7007 #ifdef DEBUG
7008 nsresult rv =
7009 #endif
7010 mEditorData->ReattachToWindow(this);
7011 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7015 void nsDocShell::DetachEditorFromWindow() {
7016 if (!mEditorData || mEditorData->WaitingForLoad()) {
7017 // If there's nothing to detach, or if the editor data is actually set
7018 // up for the _new_ page that's coming in, don't detach.
7019 return;
7022 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7023 "Detaching editor when it's already detached.");
7025 nsresult res = mEditorData->DetachFromWindow();
7026 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7028 if (NS_SUCCEEDED(res)) {
7029 // Make mOSHE hold the owning ref to the editor data.
7030 if (mOSHE) {
7031 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
7032 "We should not set the editor data again once after we "
7033 "detached the editor data during destroying this docshell");
7034 mOSHE->SetEditorData(mEditorData.release());
7035 } else {
7036 mEditorData = nullptr;
7040 #ifdef DEBUG
7042 bool isEditable;
7043 GetEditable(&isEditable);
7044 NS_ASSERTION(!isEditable,
7045 "Window is still editable after detaching editor.");
7047 #endif // DEBUG
7050 nsresult nsDocShell::CaptureState() {
7051 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7053 if (!mOSHE || mOSHE == mLSHE) {
7054 // No entry to save into, or we're replacing the existing entry.
7055 return NS_ERROR_FAILURE;
7058 if (!mScriptGlobal) {
7059 return NS_ERROR_FAILURE;
7062 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7063 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7065 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7066 nsAutoCString spec;
7067 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
7068 if (uri) {
7069 uri->GetSpec(spec);
7071 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7072 ("Saving presentation into session history, URI: %s", spec.get()));
7075 mOSHE->SetWindowState(windowState);
7077 // Suspend refresh URIs and save off the timer queue
7078 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7080 // Capture the current content viewer bounds.
7081 if (mContentViewer) {
7082 nsIntRect bounds;
7083 mContentViewer->GetBounds(bounds);
7084 mOSHE->SetViewerBounds(bounds);
7087 // Capture the docshell hierarchy.
7088 mOSHE->ClearChildShells();
7090 uint32_t childCount = mChildList.Length();
7091 for (uint32_t i = 0; i < childCount; ++i) {
7092 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7093 NS_ASSERTION(childShell, "null child shell");
7095 mOSHE->AddChildShell(childShell);
7098 return NS_OK;
7101 NS_IMETHODIMP
7102 nsDocShell::RestorePresentationEvent::Run() {
7103 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7105 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7106 NS_WARNING("RestoreFromHistory failed");
7108 return NS_OK;
7111 NS_IMETHODIMP
7112 nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop) {
7113 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7115 nsresult rv;
7116 if (!aContentViewer) {
7117 rv = EnsureContentViewer();
7118 NS_ENSURE_SUCCESS(rv, rv);
7120 aContentViewer = mContentViewer;
7123 // Dispatch events for restoring the presentation. We try to simulate
7124 // the progress notifications loading the document would cause, so we add
7125 // the document's channel to the loadgroup to initiate stateChange
7126 // notifications.
7128 RefPtr<Document> doc = aContentViewer->GetDocument();
7129 if (doc) {
7130 nsIChannel* channel = doc->GetChannel();
7131 if (channel) {
7132 mEODForCurrentDocument = false;
7133 mIsRestoringDocument = true;
7134 mLoadGroup->AddRequest(channel, nullptr);
7135 mIsRestoringDocument = false;
7139 if (!aTop) {
7140 // This point corresponds to us having gotten OnStartRequest or
7141 // STATE_START, so do the same thing that CreateContentViewer does at
7142 // this point to ensure that unload/pagehide events for this document
7143 // will fire when it's unloaded again.
7144 mFiredUnloadEvent = false;
7146 // For non-top frames, there is no notion of making sure that the
7147 // previous document is in the domwindow when STATE_START notifications
7148 // happen. We can just call BeginRestore for all of the child shells
7149 // now.
7150 rv = BeginRestoreChildren();
7151 NS_ENSURE_SUCCESS(rv, rv);
7154 return NS_OK;
7157 nsresult nsDocShell::BeginRestoreChildren() {
7158 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7160 for (auto* childDocLoader : mChildList.ForwardRange()) {
7161 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7162 if (child) {
7163 nsresult rv = child->BeginRestore(nullptr, false);
7164 NS_ENSURE_SUCCESS(rv, rv);
7167 return NS_OK;
7170 NS_IMETHODIMP
7171 nsDocShell::FinishRestore() {
7172 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7174 // First we call finishRestore() on our children. In the simulated load,
7175 // all of the child frames finish loading before the main document.
7177 for (auto* childDocLoader : mChildList.ForwardRange()) {
7178 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7179 if (child) {
7180 child->FinishRestore();
7184 if (mOSHE && mOSHE->HasDetachedEditor()) {
7185 ReattachEditorToWindow(mOSHE);
7188 RefPtr<Document> doc = GetDocument();
7189 if (doc) {
7190 // Finally, we remove the request from the loadgroup. This will
7191 // cause onStateChange(STATE_STOP) to fire, which will fire the
7192 // pageshow event to the chrome.
7194 nsIChannel* channel = doc->GetChannel();
7195 if (channel) {
7196 mIsRestoringDocument = true;
7197 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7198 mIsRestoringDocument = false;
7202 return NS_OK;
7205 NS_IMETHODIMP
7206 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7207 *aRestoring = mIsRestoringDocument;
7208 return NS_OK;
7211 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7212 bool* aRestoring) {
7213 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7214 MOZ_ASSERT(!mIsBeingDestroyed);
7216 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7217 "RestorePresentation should only be called for history loads");
7219 nsCOMPtr<nsIContentViewer> viewer = aSHEntry->GetContentViewer();
7221 nsAutoCString spec;
7222 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7223 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7224 if (uri) {
7225 uri->GetSpec(spec);
7229 *aRestoring = false;
7231 if (!viewer) {
7232 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7233 ("no saved presentation for uri: %s", spec.get()));
7234 return NS_OK;
7237 // We need to make sure the content viewer's container is this docshell.
7238 // In subframe navigation, it's possible for the docshell that the
7239 // content viewer was originally loaded into to be replaced with a
7240 // different one. We don't currently support restoring the presentation
7241 // in that case.
7243 nsCOMPtr<nsIDocShell> container;
7244 viewer->GetContainer(getter_AddRefs(container));
7245 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7246 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7247 ("No valid container, clearing presentation"));
7248 aSHEntry->SetContentViewer(nullptr);
7249 return NS_ERROR_FAILURE;
7252 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7254 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7255 ("restoring presentation from session history: %s", spec.get()));
7257 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7259 // Post an event that will remove the request after we've returned
7260 // to the event loop. This mimics the way it is called by nsIChannel
7261 // implementations.
7263 // Revoke any pending restore (just in case).
7264 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7265 "should only have one RestorePresentationEvent");
7266 mRestorePresentationEvent.Revoke();
7268 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7269 nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(evt));
7270 if (NS_SUCCEEDED(rv)) {
7271 mRestorePresentationEvent = evt.get();
7272 // The rest of the restore processing will happen on our event
7273 // callback.
7274 *aRestoring = true;
7277 return rv;
7280 namespace {
7281 class MOZ_STACK_CLASS PresentationEventForgetter {
7282 public:
7283 explicit PresentationEventForgetter(
7284 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7285 aRestorePresentationEvent)
7286 : mRestorePresentationEvent(aRestorePresentationEvent),
7287 mEvent(aRestorePresentationEvent.get()) {}
7289 ~PresentationEventForgetter() { Forget(); }
7291 void Forget() {
7292 if (mRestorePresentationEvent.get() == mEvent) {
7293 mRestorePresentationEvent.Forget();
7294 mEvent = nullptr;
7298 private:
7299 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7300 mRestorePresentationEvent;
7301 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7304 } // namespace
7306 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7307 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7310 nsresult nsDocShell::RestoreFromHistory() {
7311 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7312 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7313 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7315 // This section of code follows the same ordering as CreateContentViewer.
7316 if (!mLSHE) {
7317 return NS_ERROR_FAILURE;
7320 nsCOMPtr<nsIContentViewer> viewer = mLSHE->GetContentViewer();
7321 if (!viewer) {
7322 return NS_ERROR_FAILURE;
7325 if (mSavingOldViewer) {
7326 // We determined that it was safe to cache the document presentation
7327 // at the time we initiated the new load. We need to check whether
7328 // it's still safe to do so, since there may have been DOM mutations
7329 // or new requests initiated.
7330 RefPtr<Document> doc = viewer->GetDocument();
7331 nsIRequest* request = nullptr;
7332 if (doc) {
7333 request = doc->GetChannel();
7335 mSavingOldViewer = CanSavePresentation(
7336 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
7339 // Protect against mLSHE going away via a load triggered from
7340 // pagehide or unload.
7341 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7343 // Make sure to blow away our mLoadingURI just in case. No loads
7344 // from inside this pagehide.
7345 mLoadingURI = nullptr;
7347 // Notify the old content viewer that it's being hidden.
7348 FirePageHideNotification(!mSavingOldViewer);
7349 // pagehide notification might destroy this docshell.
7350 if (mIsBeingDestroyed) {
7351 return NS_ERROR_DOCSHELL_DYING;
7354 // If mLSHE was changed as a result of the pagehide event, then
7355 // something else was loaded. Don't finish restoring.
7356 if (mLSHE != origLSHE) {
7357 return NS_OK;
7360 // Add the request to our load group. We do this before swapping out
7361 // the content viewers so that consumers of STATE_START can access
7362 // the old document. We only deal with the toplevel load at this time --
7363 // to be consistent with normal document loading, subframes cannot start
7364 // loading until after data arrives, which is after STATE_START completes.
7366 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7367 mRestorePresentationEvent.get();
7368 Stop();
7369 // Make sure we're still restoring the same presentation.
7370 // If we aren't, docshell is in process doing another load already.
7371 NS_ENSURE_STATE(currentPresentationRestoration ==
7372 mRestorePresentationEvent.get());
7373 BeginRestore(viewer, true);
7374 NS_ENSURE_STATE(currentPresentationRestoration ==
7375 mRestorePresentationEvent.get());
7376 forgetter.Forget();
7378 // Set mFiredUnloadEvent = false so that the unload handler for the
7379 // *new* document will fire.
7380 mFiredUnloadEvent = false;
7382 mURIResultedInDocument = true;
7383 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7384 if (rootSH) {
7385 mPreviousEntryIndex = rootSH->Index();
7386 rootSH->LegacySHistory()->UpdateIndex();
7387 mLoadedEntryIndex = rootSH->Index();
7388 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7389 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7390 mLoadedEntryIndex));
7393 // Rather than call Embed(), we will retrieve the viewer from the session
7394 // history entry and swap it in.
7395 // XXX can we refactor this so that we can just call Embed()?
7396 PersistLayoutHistoryState();
7397 nsresult rv;
7398 if (mContentViewer) {
7399 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7400 if (mOSHE) {
7401 mOSHE->SyncPresentationState();
7403 mSavingOldViewer = false;
7407 mSavedRefreshURIList = nullptr;
7409 // In cases where we use a transient about:blank viewer between loads,
7410 // we never show the transient viewer, so _its_ previous viewer is never
7411 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7412 // before we grab the root view sibling, so that we don't grab a view
7413 // that's about to go away.
7415 if (mContentViewer) {
7416 // Make sure to hold a strong ref to previousViewer here while we
7417 // drop the reference to it from mContentViewer.
7418 nsCOMPtr<nsIContentViewer> previousViewer =
7419 mContentViewer->GetPreviousViewer();
7420 if (previousViewer) {
7421 mContentViewer->SetPreviousViewer(nullptr);
7422 previousViewer->Destroy();
7426 // Save off the root view's parent and sibling so that we can insert the
7427 // new content viewer's root view at the same position. Also save the
7428 // bounds of the root view's widget.
7430 nsView* rootViewSibling = nullptr;
7431 nsView* rootViewParent = nullptr;
7432 nsIntRect newBounds(0, 0, 0, 0);
7434 PresShell* oldPresShell = GetPresShell();
7435 if (oldPresShell) {
7436 nsViewManager* vm = oldPresShell->GetViewManager();
7437 if (vm) {
7438 nsView* oldRootView = vm->GetRootView();
7440 if (oldRootView) {
7441 rootViewSibling = oldRootView->GetNextSibling();
7442 rootViewParent = oldRootView->GetParent();
7444 mContentViewer->GetBounds(newBounds);
7449 nsCOMPtr<nsIContent> container;
7450 RefPtr<Document> sibling;
7451 if (rootViewParent && rootViewParent->GetParent()) {
7452 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7453 container = frame ? frame->GetContent() : nullptr;
7455 if (rootViewSibling) {
7456 nsIFrame* frame = rootViewSibling->GetFrame();
7457 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7460 // Transfer ownership to mContentViewer. By ensuring that either the
7461 // docshell or the session history, but not both, have references to the
7462 // content viewer, we prevent the viewer from being torn down after
7463 // Destroy() is called.
7465 if (mContentViewer) {
7466 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7467 viewer->SetPreviousViewer(mContentViewer);
7469 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
7470 // We don't plan to save a viewer in mOSHE; tell it to drop
7471 // any other state it's holding.
7472 mOSHE->SyncPresentationState();
7475 // Order the mContentViewer setup just like Embed does.
7476 mContentViewer = nullptr;
7478 // Now that we're about to switch documents, forget all of our children.
7479 // Note that we cached them as needed up in CaptureState above.
7480 DestroyChildren();
7482 mContentViewer.swap(viewer);
7484 // Grab all of the related presentation from the SHEntry now.
7485 // Clearing the viewer from the SHEntry will clear all of this state.
7486 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7487 mLSHE->SetWindowState(nullptr);
7489 bool sticky = mLSHE->GetSticky();
7491 RefPtr<Document> document = mContentViewer->GetDocument();
7493 nsCOMArray<nsIDocShellTreeItem> childShells;
7494 int32_t i = 0;
7495 nsCOMPtr<nsIDocShellTreeItem> child;
7496 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7497 child) {
7498 childShells.AppendObject(child);
7501 // get the previous content viewer size
7502 nsIntRect oldBounds(0, 0, 0, 0);
7503 mLSHE->GetViewerBounds(oldBounds);
7505 // Restore the refresh URI list. The refresh timers will be restarted
7506 // when EndPageLoad() is called.
7507 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7509 // Reattach to the window object.
7510 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7511 rv = mContentViewer->Open(windowState, mLSHE);
7512 mIsRestoringDocument = false;
7514 // Hack to keep nsDocShellEditorData alive across the
7515 // SetContentViewer(nullptr) call below.
7516 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7518 // Now remove it from the cached presentation.
7519 mLSHE->SetContentViewer(nullptr);
7520 mEODForCurrentDocument = false;
7522 mLSHE->SetEditorData(data.release());
7524 #ifdef DEBUG
7526 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7527 nsCOMPtr<nsIDocShellTreeItem> childShell;
7528 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7529 NS_ASSERTION(!refreshURIs && !childShell,
7530 "SHEntry should have cleared presentation state");
7532 #endif
7534 // Restore the sticky state of the viewer. The viewer has set this state
7535 // on the history entry in Destroy() just before marking itself non-sticky,
7536 // to avoid teardown of the presentation.
7537 mContentViewer->SetSticky(sticky);
7539 NS_ENSURE_SUCCESS(rv, rv);
7541 // mLSHE is now our currently-loaded document.
7542 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7544 // We aren't going to restore any items from the LayoutHistoryState,
7545 // but we don't want them to stay around in case the page is reloaded.
7546 SetLayoutHistoryState(nullptr);
7548 // This is the end of our Embed() replacement
7550 mSavingOldViewer = false;
7551 mEODForCurrentDocument = false;
7553 if (document) {
7554 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7555 if (parent) {
7556 RefPtr<Document> d = parent->GetDocument();
7557 if (d) {
7558 if (d->EventHandlingSuppressed()) {
7559 document->SuppressEventHandling(d->EventHandlingSuppressed());
7564 // Use the uri from the mLSHE we had when we entered this function
7565 // (which need not match the document's URI if anchors are involved),
7566 // since that's the history entry we're loading. Note that if we use
7567 // origLSHE we don't have to worry about whether the entry in question
7568 // is still mLSHE or whether it's now mOSHE.
7569 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7570 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7571 /* aIsInitialAboutBlank */ false,
7572 /* aLocationFlags */ 0);
7575 // This is the end of our CreateContentViewer() replacement.
7576 // Now we simulate a load. First, we restore the state of the javascript
7577 // window object.
7578 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7579 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7581 // Now, dispatch a title change event which would happen as the
7582 // <head> is parsed.
7583 document->NotifyPossibleTitleChange(false);
7585 // Now we simulate appending child docshells for subframes.
7586 for (i = 0; i < childShells.Count(); ++i) {
7587 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7588 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7590 // Make sure to not clobber the state of the child. Since AddChild
7591 // always clobbers it, save it off first.
7592 bool allowRedirects;
7593 childShell->GetAllowMetaRedirects(&allowRedirects);
7595 bool allowSubframes;
7596 childShell->GetAllowSubframes(&allowSubframes);
7598 bool allowImages;
7599 childShell->GetAllowImages(&allowImages);
7601 bool allowMedia = childShell->GetAllowMedia();
7603 bool allowDNSPrefetch;
7604 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7606 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7607 bool allowContentRetargetingOnChildren =
7608 childShell->GetAllowContentRetargetingOnChildren();
7610 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7611 // the child inherits our state. Among other things, this means that the
7612 // child inherits our mPrivateBrowsingId, which is what we want.
7613 AddChild(childItem);
7615 childShell->SetAllowMetaRedirects(allowRedirects);
7616 childShell->SetAllowSubframes(allowSubframes);
7617 childShell->SetAllowImages(allowImages);
7618 childShell->SetAllowMedia(allowMedia);
7619 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7620 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7621 childShell->SetAllowContentRetargetingOnChildren(
7622 allowContentRetargetingOnChildren);
7624 rv = childShell->BeginRestore(nullptr, false);
7625 NS_ENSURE_SUCCESS(rv, rv);
7628 // Make sure to restore the window state after adding the child shells back
7629 // to the tree. This is necessary for Thaw() and Resume() to propagate
7630 // properly.
7631 rv = privWin->RestoreWindowState(windowState);
7632 NS_ENSURE_SUCCESS(rv, rv);
7634 RefPtr<PresShell> presShell = GetPresShell();
7636 // We may be displayed on a different monitor (or in a different
7637 // HiDPI mode) than when we got into the history list. So we need
7638 // to check if this has happened. See bug 838239.
7640 // Because the prescontext normally handles resolution changes via
7641 // a runnable (see nsPresContext::UIResolutionChanged), its device
7642 // context won't be -immediately- updated as a result of calling
7643 // presShell->BackingScaleFactorChanged().
7645 // But we depend on that device context when adjusting the view size
7646 // via mContentViewer->SetBounds(newBounds) below. So we need to
7647 // explicitly tell it to check for changed resolution here.
7648 if (presShell) {
7649 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7650 if (pc->DeviceContext()->CheckDPIChange()) {
7651 presShell->BackingScaleFactorChanged();
7653 // Recompute zoom and text-zoom and such.
7654 pc->RecomputeBrowsingContextDependentData();
7657 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7658 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7660 // Insert the new root view at the correct location in the view tree.
7661 if (container) {
7662 nsSubDocumentFrame* subDocFrame =
7663 do_QueryFrame(container->GetPrimaryFrame());
7664 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7665 } else {
7666 rootViewParent = nullptr;
7668 if (sibling && sibling->GetPresShell() &&
7669 sibling->GetPresShell()->GetViewManager()) {
7670 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7671 } else {
7672 rootViewSibling = nullptr;
7674 if (rootViewParent && newRootView &&
7675 newRootView->GetParent() != rootViewParent) {
7676 nsViewManager* parentVM = rootViewParent->GetViewManager();
7677 if (parentVM) {
7678 // InsertChild(parent, child, sib, true) inserts the child after
7679 // sib in content order, which is before sib in view order. BUT
7680 // when sib is null it inserts at the end of the the document
7681 // order, i.e., first in view order. But when oldRootSibling is
7682 // null, the old root as at the end of the view list --- last in
7683 // content order --- and we want to call InsertChild(parent, child,
7684 // nullptr, false) in that case.
7685 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7686 rootViewSibling ? true : false);
7688 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7689 "error in InsertChild");
7693 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7695 // If parent is suspended, increase suspension count.
7696 // This can't be done as early as event suppression since this
7697 // depends on docshell tree.
7698 privWinInner->SyncStateFromParentWindow();
7700 // Now that all of the child docshells have been put into place, we can
7701 // restart the timers for the window and all of the child frames.
7702 privWinInner->Resume();
7704 // Now that we have found the inner window of the page restored
7705 // from the history, we have to make sure that
7706 // performance.navigation.type is 2.
7707 Performance* performance = privWinInner->GetPerformance();
7708 if (performance) {
7709 performance->GetDOMTiming()->NotifyRestoreStart();
7712 // Restore the refresh URI list. The refresh timers will be restarted
7713 // when EndPageLoad() is called.
7714 mRefreshURIList = refreshURIList;
7716 // Meta-refresh timers have been restarted for this shell, but not
7717 // for our children. Walk the child shells and restart their timers.
7718 for (auto* childDocLoader : mChildList.ForwardRange()) {
7719 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7720 if (child) {
7721 child->ResumeRefreshURIs();
7725 // Make sure this presentation is the same size as the previous
7726 // presentation. If this is not the same size we showed it at last time,
7727 // then we need to resize the widget.
7729 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7730 // presentation had the infobar visible, then we will resize the new
7731 // presentation to that smaller size. However, firing the locationchanged
7732 // event will hide the infobar, which will immediately resize the window
7733 // back to the larger size. A future optimization might be to restore
7734 // the presentation at the "wrong" size, then fire the locationchanged
7735 // event and check whether the docshell's new size is the same as the
7736 // cached viewer size (skipping the resize if they are equal).
7738 if (newRootView) {
7739 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7740 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7741 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7742 newBounds.width, newBounds.height));
7743 mContentViewer->SetBounds(newBounds);
7744 } else {
7745 nsIScrollableFrame* rootScrollFrame =
7746 presShell->GetRootScrollFrameAsScrollable();
7747 if (rootScrollFrame) {
7748 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7753 // The FinishRestore call below can kill these, null them out so we don't
7754 // have invalid pointer lying around.
7755 newRootView = rootViewSibling = rootViewParent = nullptr;
7756 newVM = nullptr;
7758 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7759 // update it.
7760 if (oldPresShell && presShell &&
7761 presShell->IsUnderHiddenEmbedderElement() !=
7762 oldPresShell->IsUnderHiddenEmbedderElement()) {
7763 presShell->SetIsUnderHiddenEmbedderElement(
7764 oldPresShell->IsUnderHiddenEmbedderElement());
7767 // Simulate the completion of the load.
7768 nsDocShell::FinishRestore();
7770 // Restart plugins, and paint the content.
7771 if (presShell) {
7772 presShell->Thaw();
7775 return privWin->FireDelayedDOMEvents(true);
7778 nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
7779 nsIRequest* aRequest,
7780 nsIStreamListener** aContentHandler) {
7781 if (DocGroup::TryToLoadIframesInBackground()) {
7782 ResetToFirstLoad();
7785 *aContentHandler = nullptr;
7787 if (!mTreeOwner || mIsBeingDestroyed) {
7788 // If we don't have a tree owner, then we're in the process of being
7789 // destroyed. Rather than continue trying to load something, just give up.
7790 return NS_ERROR_DOCSHELL_DYING;
7793 if (!mBrowsingContext->AncestorsAreCurrent() ||
7794 mBrowsingContext->IsInBFCache()) {
7795 mBrowsingContext->RemoveRootFromBFCacheSync();
7796 return NS_ERROR_NOT_AVAILABLE;
7799 // Can we check the content type of the current content viewer
7800 // and reuse it without destroying it and re-creating it?
7802 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7804 // Instantiate the content viewer object
7805 nsCOMPtr<nsIContentViewer> viewer;
7806 nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
7807 aContentHandler, getter_AddRefs(viewer));
7809 if (NS_FAILED(rv)) {
7810 return rv;
7813 // Notify the current document that it is about to be unloaded!!
7815 // It is important to fire the unload() notification *before* any state
7816 // is changed within the DocShell - otherwise, javascript will get the
7817 // wrong information :-(
7820 if (mSavingOldViewer) {
7821 // We determined that it was safe to cache the document presentation
7822 // at the time we initiated the new load. We need to check whether
7823 // it's still safe to do so, since there may have been DOM mutations
7824 // or new requests initiated.
7825 RefPtr<Document> doc = viewer->GetDocument();
7826 mSavingOldViewer = CanSavePresentation(
7827 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
7830 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7832 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7833 if (aOpenedChannel) {
7834 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7837 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7838 // it before we do call Embed.
7839 nsCOMPtr<nsIURI> previousURI = mCurrentURI;
7841 FirePageHideNotification(!mSavingOldViewer);
7842 if (mIsBeingDestroyed) {
7843 // Force to stop the newly created orphaned viewer.
7844 viewer->Stop();
7845 return NS_ERROR_DOCSHELL_DYING;
7847 mLoadingURI = nullptr;
7849 // Set mFiredUnloadEvent = false so that the unload handler for the
7850 // *new* document will fire.
7851 mFiredUnloadEvent = false;
7853 // we've created a new document so go ahead and call
7854 // OnNewURI(), but don't fire OnLocationChange()
7855 // notifications before we've called Embed(). See bug 284993.
7856 mURIResultedInDocument = true;
7857 bool errorOnLocationChangeNeeded = false;
7858 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7859 nsCOMPtr<nsIURI> failedURI;
7861 if (mLoadType == LOAD_ERROR_PAGE) {
7862 // We need to set the SH entry and our current URI here and not
7863 // at the moment we load the page. We want the same behavior
7864 // of Stop() as for a normal page load. See bug 514232 for details.
7866 // Revert mLoadType to load type to state the page load failed,
7867 // following function calls need it.
7868 mLoadType = mFailedLoadType;
7870 Document* doc = viewer->GetDocument();
7871 if (doc) {
7872 doc->SetFailedChannel(failedChannel);
7875 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7876 if (failedChannel) {
7877 // Make sure we have a URI to set currentURI.
7878 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7879 } else {
7880 // if there is no failed channel we have to explicitly provide
7881 // a triggeringPrincipal for the history entry.
7882 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7885 if (!failedURI) {
7886 failedURI = mFailedURI;
7888 if (!failedURI) {
7889 // We need a URI object to store a session history entry, so make up a URI
7890 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7893 // When we don't have failedURI, something wrong will happen. See
7894 // bug 291876.
7895 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7897 mFailedChannel = nullptr;
7898 mFailedURI = nullptr;
7900 // Create an shistory entry for the old load.
7901 if (failedURI) {
7902 errorOnLocationChangeNeeded =
7903 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7904 nullptr, nullptr, false, false, false);
7907 // Be sure to have a correct mLSHE, it may have been cleared by
7908 // EndPageLoad. See bug 302115.
7909 ChildSHistory* shistory = GetSessionHistory();
7910 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7911 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7912 if (idx == -1) {
7913 idx = shistory->Index();
7915 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7918 mLoadType = LOAD_ERROR_PAGE;
7921 nsCOMPtr<nsIURI> finalURI;
7922 // If this a redirect, use the final url (uri)
7923 // else use the original url
7925 // Note that this should match what documents do (see Document::Reset).
7926 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7928 bool onLocationChangeNeeded = false;
7929 if (finalURI) {
7930 // Pass false for aCloneSHChildren, since we're loading a new page here.
7931 onLocationChangeNeeded =
7932 OnNewURI(finalURI, aOpenedChannel, nullptr, nullptr, nullptr, nullptr,
7933 false, true, false);
7936 // let's try resetting the load group if we need to...
7937 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7938 NS_ENSURE_SUCCESS(
7939 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7940 NS_ERROR_FAILURE);
7942 if (currentLoadGroup != mLoadGroup) {
7943 nsLoadFlags loadFlags = 0;
7945 // Cancel any URIs that are currently loading...
7946 // XXX: Need to do this eventually Stop();
7948 // Retarget the document to this loadgroup...
7950 /* First attach the channel to the right loadgroup
7951 * and then remove from the old loadgroup. This
7952 * puts the notifications in the right order and
7953 * we don't null-out mLSHE in OnStateChange() for
7954 * all redirected urls
7956 aOpenedChannel->SetLoadGroup(mLoadGroup);
7958 // Mark the channel as being a document URI...
7959 aOpenedChannel->GetLoadFlags(&loadFlags);
7960 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7961 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7962 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7963 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7966 aOpenedChannel->SetLoadFlags(loadFlags);
7968 mLoadGroup->AddRequest(aRequest, nullptr);
7969 if (currentLoadGroup) {
7970 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7973 // Update the notification callbacks, so that progress and
7974 // status information are sent to the right docshell...
7975 aOpenedChannel->SetNotificationCallbacks(this);
7978 if (DocGroup::TryToLoadIframesInBackground()) {
7979 if ((!mContentViewer || GetDocument()->IsInitialDocument()) &&
7980 IsSubframe()) {
7981 // At this point, we know we just created a new iframe document based on
7982 // the response from the server, and we check if it's a cross-domain
7983 // iframe
7985 RefPtr<Document> newDoc = viewer->GetDocument();
7987 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7988 nsCOMPtr<nsIPrincipal> parentPrincipal =
7989 parent->GetDocument()->NodePrincipal();
7990 nsCOMPtr<nsIPrincipal> thisPrincipal = newDoc->NodePrincipal();
7992 SiteIdentifier parentSite;
7993 SiteIdentifier thisSite;
7995 nsresult rv =
7996 BasePrincipal::Cast(parentPrincipal)->GetSiteIdentifier(parentSite);
7997 NS_ENSURE_SUCCESS(rv, rv);
7999 rv = BasePrincipal::Cast(thisPrincipal)->GetSiteIdentifier(thisSite);
8000 NS_ENSURE_SUCCESS(rv, rv);
8002 if (!parentSite.Equals(thisSite)) {
8003 if (profiler_thread_is_being_profiled_for_markers()) {
8004 nsCOMPtr<nsIURI> prinURI;
8005 BasePrincipal::Cast(thisPrincipal)->GetURI(getter_AddRefs(prinURI));
8006 nsPrintfCString marker(
8007 "Iframe loaded in background: %s",
8008 nsContentUtils::TruncatedURLForDisplay(prinURI).get());
8009 PROFILER_MARKER_TEXT("Background Iframe", DOM, {}, marker);
8011 SetBackgroundLoadIframe();
8016 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
8017 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
8018 aOpenedChannel, previousURI),
8019 NS_ERROR_FAILURE);
8021 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
8022 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
8025 if (TreatAsBackgroundLoad()) {
8026 nsCOMPtr<nsIRunnable> triggerParentCheckDocShell =
8027 NewRunnableMethod("nsDocShell::TriggerParentCheckDocShellIsEmpty", this,
8028 &nsDocShell::TriggerParentCheckDocShellIsEmpty);
8029 nsresult rv = NS_DispatchToCurrentThread(triggerParentCheckDocShell);
8030 NS_ENSURE_SUCCESS(rv, rv);
8033 mSavedRefreshURIList = nullptr;
8034 mSavingOldViewer = false;
8035 mEODForCurrentDocument = false;
8037 // if this document is part of a multipart document,
8038 // the ID can be used to distinguish it from the other parts.
8039 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
8040 if (multiPartChannel) {
8041 if (PresShell* presShell = GetPresShell()) {
8042 if (Document* doc = presShell->GetDocument()) {
8043 uint32_t partID;
8044 multiPartChannel->GetPartID(&partID);
8045 doc->SetPartID(partID);
8050 if (errorOnLocationChangeNeeded) {
8051 FireOnLocationChange(this, failedChannel, failedURI,
8052 LOCATION_CHANGE_ERROR_PAGE);
8053 } else if (onLocationChangeNeeded) {
8054 uint32_t locationFlags =
8055 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
8056 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
8059 return NS_OK;
8062 nsresult nsDocShell::NewContentViewerObj(const nsACString& aContentType,
8063 nsIRequest* aRequest,
8064 nsILoadGroup* aLoadGroup,
8065 nsIStreamListener** aContentHandler,
8066 nsIContentViewer** aViewer) {
8067 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8069 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8070 nsContentUtils::FindInternalContentViewer(aContentType);
8071 if (!docLoaderFactory) {
8072 return NS_ERROR_FAILURE;
8075 // Now create an instance of the content viewer nsLayoutDLF makes the
8076 // determination if it should be a "view-source" instead of "view"
8077 nsresult rv = docLoaderFactory->CreateInstance(
8078 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
8079 aContentHandler, aViewer);
8080 NS_ENSURE_SUCCESS(rv, rv);
8082 (*aViewer)->SetContainer(this);
8083 return NS_OK;
8086 nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
8087 WindowGlobalChild* aWindowActor) {
8088 MOZ_ASSERT(!mIsBeingDestroyed);
8091 // Copy content viewer state from previous or parent content viewer.
8093 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8095 // Do NOT to maintain a reference to the old content viewer outside
8096 // of this "copying" block, or it will not be destroyed until the end of
8097 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8099 // In this block of code, if we get an error result, we return it
8100 // but if we get a null pointer, that's perfectly legal for parent
8101 // and parentContentViewer.
8104 int32_t x = 0;
8105 int32_t y = 0;
8106 int32_t cx = 0;
8107 int32_t cy = 0;
8109 // This will get the size from the current content viewer or from the
8110 // Init settings
8111 DoGetPositionAndSize(&x, &y, &cx, &cy);
8113 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8114 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
8115 NS_ERROR_FAILURE);
8116 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8118 const Encoding* reloadEncoding = nullptr;
8119 int32_t reloadEncodingSource = kCharsetUninitialized;
8120 // |newMUDV| also serves as a flag to set the data from the above vars
8121 nsCOMPtr<nsIContentViewer> newCv;
8123 if (mContentViewer || parent) {
8124 nsCOMPtr<nsIContentViewer> oldCv;
8125 if (mContentViewer) {
8126 // Get any interesting state from old content viewer
8127 // XXX: it would be far better to just reuse the document viewer ,
8128 // since we know we're just displaying the same document as before
8129 oldCv = mContentViewer;
8131 // Tell the old content viewer to hibernate in session history when
8132 // it is destroyed.
8134 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8135 if (mOSHE) {
8136 mOSHE->SyncPresentationState();
8138 mSavingOldViewer = false;
8140 } else {
8141 // No old content viewer, so get state from parent's content viewer
8142 parent->GetContentViewer(getter_AddRefs(oldCv));
8145 if (oldCv) {
8146 newCv = aNewViewer;
8147 if (newCv) {
8148 reloadEncoding =
8149 oldCv->GetReloadEncodingAndSource(&reloadEncodingSource);
8154 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8155 bool isUnderHiddenEmbedderElement = false;
8156 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8157 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
8158 if (contentViewer) {
8159 // Stop any activity that may be happening in the old document before
8160 // releasing it...
8161 contentViewer->Stop();
8163 // Try to extract the canvas background color from the old
8164 // presentation shell, so we can use it for the next document.
8165 if (PresShell* presShell = contentViewer->GetPresShell()) {
8166 bgcolor = presShell->GetCanvasBackground();
8167 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
8170 contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8171 aNewViewer->SetPreviousViewer(contentViewer);
8173 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8174 // We don't plan to save a viewer in mOSHE; tell it to drop
8175 // any other state it's holding.
8176 mOSHE->SyncPresentationState();
8179 mContentViewer = nullptr;
8181 // Now that we're about to switch documents, forget all of our children.
8182 // Note that we cached them as needed up in CaptureState above.
8183 DestroyChildren();
8185 mContentViewer = aNewViewer;
8187 nsCOMPtr<nsIWidget> widget;
8188 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8190 nsIntRect bounds(x, y, cx, cy);
8192 mContentViewer->SetNavigationTiming(mTiming);
8194 if (NS_FAILED(mContentViewer->Init(widget, bounds, aWindowActor))) {
8195 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
8196 viewer->Close(nullptr);
8197 viewer->Destroy();
8198 mContentViewer = nullptr;
8199 SetCurrentURIInternal(nullptr);
8200 NS_WARNING("ContentViewer Initialization failed");
8201 return NS_ERROR_FAILURE;
8204 // If we have old state to copy, set the old state onto the new content
8205 // viewer
8206 if (newCv) {
8207 newCv->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8210 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8212 // Stuff the bgcolor from the old pres shell into the new
8213 // pres shell. This improves page load continuity.
8214 if (RefPtr<PresShell> presShell = mContentViewer->GetPresShell()) {
8215 presShell->SetCanvasBackground(bgcolor);
8216 presShell->ActivenessMaybeChanged();
8217 if (isUnderHiddenEmbedderElement) {
8218 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8222 // XXX: It looks like the LayoutState gets restored again in Embed()
8223 // right after the call to SetupNewViewer(...)
8225 // We don't show the mContentViewer yet, since we want to draw the old page
8226 // until we have enough of the new page to show. Just return with the new
8227 // viewer still set to hidden.
8229 return NS_OK;
8232 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8233 SessionHistoryInfo* aInfo) {
8234 NS_ENSURE_TRUE_VOID(mContentViewer);
8236 RefPtr<Document> document = GetDocument();
8237 NS_ENSURE_TRUE_VOID(document);
8239 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8240 if (mozilla::SessionHistoryInParent()) {
8241 // If aInfo is null, just set the document's state object to null.
8242 if (aInfo) {
8243 scContainer = aInfo->GetStateData();
8245 MOZ_LOG(gSHLog, LogLevel::Debug,
8246 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8247 } else {
8248 if (aShEntry) {
8249 scContainer = aShEntry->GetStateData();
8251 // If aShEntry is null, just set the document's state object to null.
8255 // It's OK for scContainer too be null here; that just means there's no
8256 // state data associated with this history entry.
8257 document->SetStateObject(scContainer);
8260 nsresult nsDocShell::CheckLoadingPermissions() {
8261 // This method checks whether the caller may load content into
8262 // this docshell. Even though we've done our best to hide windows
8263 // from code that doesn't have the right to access them, it's
8264 // still possible for an evil site to open a window and access
8265 // frames in the new window through window.frames[] (which is
8266 // allAccess for historic reasons), so we still need to do this
8267 // check on load.
8268 nsresult rv = NS_OK;
8270 if (!IsSubframe()) {
8271 // We're not a frame. Permit all loads.
8272 return rv;
8275 // Note - The check for a current JSContext here isn't necessarily sensical.
8276 // It's just designed to preserve the old semantics during a mass-conversion
8277 // patch.
8278 if (!nsContentUtils::GetCurrentJSContext()) {
8279 return NS_OK;
8282 // Check if the caller is from the same origin as this docshell,
8283 // or any of its ancestors.
8284 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8285 bc = bc->GetParent()) {
8286 // If the BrowsingContext is not in process, then it
8287 // is true by construction that its principal will not
8288 // subsume the current docshell principal.
8289 if (!bc->IsInProcess()) {
8290 continue;
8293 nsCOMPtr<nsIScriptGlobalObject> sgo =
8294 bc->GetDocShell()->GetScriptGlobalObject();
8295 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8297 nsIPrincipal* p;
8298 if (!sop || !(p = sop->GetPrincipal())) {
8299 return NS_ERROR_UNEXPECTED;
8302 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8303 // Same origin, permit load
8304 return NS_OK;
8308 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8311 //*****************************************************************************
8312 // nsDocShell: Site Loading
8313 //*****************************************************************************
8315 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8316 bool aInPrivateBrowsing) {
8317 if (XRE_IsContentProcess()) {
8318 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8319 if (contentChild) {
8320 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8322 return;
8325 #ifdef MOZ_PLACES
8326 nsCOMPtr<nsIFaviconService> favSvc =
8327 do_GetService("@mozilla.org/browser/favicon-service;1");
8328 if (favSvc) {
8329 favSvc->CopyFavicons(aOldURI, aNewURI,
8330 aInPrivateBrowsing
8331 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8332 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8333 nullptr);
8335 #endif
8338 class InternalLoadEvent : public Runnable {
8339 public:
8340 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8341 : mozilla::Runnable("InternalLoadEvent"),
8342 mDocShell(aDocShell),
8343 mLoadState(aLoadState) {
8344 // For events, both target and filename should be the version of "null" they
8345 // expect. By the time the event is fired, both window targeting and file
8346 // downloading have been handled, so we should never have an internal load
8347 // event that retargets or had a download.
8348 mLoadState->SetTarget(u""_ns);
8349 mLoadState->SetFileName(VoidString());
8352 NS_IMETHOD
8353 Run() override {
8354 #ifndef ANDROID
8355 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8356 "InternalLoadEvent: Should always have a principal here");
8357 #endif
8358 return mDocShell->InternalLoad(mLoadState);
8361 private:
8362 RefPtr<nsDocShell> mDocShell;
8363 RefPtr<nsDocShellLoadState> mLoadState;
8367 * Returns true if we started an asynchronous load (i.e., from the network), but
8368 * the document we're loading there hasn't yet become this docshell's active
8369 * document.
8371 * When JustStartedNetworkLoad is true, you should be careful about modifying
8372 * mLoadType and mLSHE. These are both set when the asynchronous load first
8373 * starts, and the load expects that, when it eventually runs InternalLoad,
8374 * mLoadType and mLSHE will have their original values.
8376 bool nsDocShell::JustStartedNetworkLoad() {
8377 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8380 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8381 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8382 // <embed>, etc)
8384 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8385 // later when we call DoURILoad.
8386 nsContentPolicyType nsDocShell::DetermineContentType() {
8387 if (!IsSubframe()) {
8388 return nsIContentPolicy::TYPE_DOCUMENT;
8391 const auto& maybeEmbedderElementType =
8392 GetBrowsingContext()->GetEmbedderElementType();
8393 if (!maybeEmbedderElementType) {
8394 // If the EmbedderElementType hasn't been set yet, just assume we're
8395 // an iframe since that's more common.
8396 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8399 return maybeEmbedderElementType->EqualsLiteral("iframe")
8400 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8401 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8404 bool nsDocShell::NoopenerForceEnabled() {
8405 // If current's top-level browsing context's active document's
8406 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8407 // if currentDoc's origin is not same origin with currentDoc's top-level
8408 // origin, noopener is force enabled, and name is cleared to "_blank".
8409 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8410 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8411 topPolicy ==
8412 nsILoadInfo::
8413 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8414 !mBrowsingContext->SameOriginWithTop();
8417 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8418 MOZ_ASSERT(aLoadState, "need a load state!");
8419 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8420 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8421 "should not have picked target yet");
8423 nsresult rv = NS_OK;
8424 RefPtr<BrowsingContext> targetContext;
8426 // Only _self, _parent, and _top are supported in noopener case. But we
8427 // have to be careful to not apply that to the noreferrer case. See bug
8428 // 1358469.
8429 bool allowNamedTarget =
8430 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8431 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8432 if (allowNamedTarget ||
8433 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8434 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8435 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8436 Document* document = GetDocument();
8437 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8438 WindowGlobalChild* wgc = document->GetWindowGlobalChild();
8439 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
8440 targetContext = wgc->FindBrowsingContextWithName(
8441 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8444 if (!targetContext) {
8445 // If the targetContext doesn't exist, then this is a new docShell and we
8446 // should consider this a TYPE_DOCUMENT load
8448 // For example, when target="_blank"
8450 // If there's no targetContext, that means we are about to create a new
8451 // window. Perform a content policy check before creating the window. Please
8452 // note for all other docshell loads content policy checks are performed
8453 // within the contentSecurityManager when the channel is about to be
8454 // openend.
8455 nsISupports* requestingContext = nullptr;
8456 if (XRE_IsContentProcess()) {
8457 // In e10s the child process doesn't have access to the element that
8458 // contains the browsing context (because that element is in the chrome
8459 // process). So we just pass mScriptGlobal.
8460 requestingContext = ToSupports(mScriptGlobal);
8461 } else {
8462 // This is for loading non-e10s tabs and toplevel windows of various
8463 // sorts.
8464 // For the toplevel window cases, requestingElement will be null.
8465 nsCOMPtr<Element> requestingElement =
8466 mScriptGlobal->GetFrameElementInternal();
8467 requestingContext = requestingElement;
8470 // Ideally we should use the same loadinfo as within DoURILoad which
8471 // should match this one when both are applicable.
8472 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
8473 new LoadInfo(mScriptGlobal, aLoadState->URI(),
8474 aLoadState->TriggeringPrincipal(), requestingContext,
8475 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8477 // Since Content Policy checks are performed within docShell as well as
8478 // the ContentSecurityManager we need a reliable way to let certain
8479 // nsIContentPolicy consumers ignore duplicate calls.
8480 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8482 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8483 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8484 ""_ns, // mime guess
8485 &shouldLoad);
8487 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8488 if (NS_SUCCEEDED(rv)) {
8489 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8490 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8492 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8493 return NS_ERROR_BLOCKED_BY_POLICY;
8497 return NS_ERROR_CONTENT_BLOCKED;
8502 // Resolve the window target before going any further...
8503 // If the load has been targeted to another DocShell, then transfer the
8504 // load to it...
8507 // We've already done our owner-inheriting. Mask out that bit, so we
8508 // don't try inheriting an owner from the target window if we came up
8509 // with a null owner above.
8510 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8512 if (!targetContext) {
8513 // If the docshell's document is sandboxed, only open a new window
8514 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8515 // (i.e. if allow-popups is specified)
8516 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8517 Document* doc = mContentViewer->GetDocument();
8519 const bool isDocumentAuxSandboxed =
8520 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8522 if (isDocumentAuxSandboxed) {
8523 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8526 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8527 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8529 RefPtr<BrowsingContext> newBC;
8530 nsAutoCString spec;
8531 aLoadState->URI()->GetSpec(spec);
8533 // If we are a noopener load, we just hand the whole thing over to our
8534 // window.
8535 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8536 NoopenerForceEnabled()) {
8537 // Various asserts that we know to hold because NO_OPENER loads can only
8538 // happen for links.
8539 MOZ_ASSERT(!aLoadState->LoadReplace());
8540 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8541 aLoadState->TriggeringPrincipal());
8542 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8543 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8544 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8545 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8546 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8547 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8548 aLoadState->IsFormSubmission());
8549 MOZ_ASSERT(!aLoadState->HeadersStream());
8550 // If OnLinkClickSync was invoked inside the onload handler, the load
8551 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8552 // LOAD_LINK.
8553 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8554 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8555 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8556 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8558 RefPtr<nsDocShellLoadState> loadState =
8559 new nsDocShellLoadState(aLoadState->URI());
8561 // Set up our loadinfo so it will do the load as much like we would have
8562 // as possible.
8563 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8564 loadState->SetOriginalURI(aLoadState->OriginalURI());
8566 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8567 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8569 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8570 loadState->SetKeepResultPrincipalURIIfSet(
8571 aLoadState->KeepResultPrincipalURIIfSet());
8572 // LoadReplace will always be false due to asserts above, skip setting
8573 // it.
8574 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8575 loadState->SetTriggeringSandboxFlags(
8576 aLoadState->TriggeringSandboxFlags());
8577 loadState->SetCsp(aLoadState->Csp());
8578 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8579 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8580 // Explicit principal because we do not want any guesses as to what the
8581 // principal to inherit is: it should be aTriggeringPrincipal.
8582 loadState->SetPrincipalIsExplicit(true);
8583 loadState->SetLoadType(aLoadState->LoadType());
8584 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8585 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8587 loadState->SetHasValidUserGestureActivation(
8588 aLoadState->HasValidUserGestureActivation());
8590 // Propagate POST data to the new load.
8591 loadState->SetPostDataStream(aLoadState->PostDataStream());
8592 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8594 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8595 aLoadState->Target(), // window name
8596 u""_ns, // Features
8597 loadState,
8598 true, // aForceNoOpener
8599 getter_AddRefs(newBC));
8600 MOZ_ASSERT(!newBC);
8601 return rv;
8604 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8605 aLoadState->Target(), // window name
8606 u""_ns, // Features
8607 getter_AddRefs(newBC));
8609 // In some cases the Open call doesn't actually result in a new
8610 // window being opened. We can detect these cases by examining the
8611 // document in |newBC|, if any.
8612 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8613 newBC ? newBC->GetDOMWindow() : nullptr;
8614 if (piNewWin) {
8615 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8616 if (!newDoc || newDoc->IsInitialDocument()) {
8617 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8621 if (newBC) {
8622 targetContext = newBC;
8625 NS_ENSURE_SUCCESS(rv, rv);
8626 NS_ENSURE_TRUE(targetContext, rv);
8628 // If our target BrowsingContext is still pending initialization, ignore the
8629 // navigation request targeting it.
8630 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8631 return NS_OK;
8634 aLoadState->SetTargetBrowsingContext(targetContext);
8635 if (aLoadState->IsFormSubmission()) {
8636 aLoadState->SetLoadType(
8637 GetLoadTypeForFormSubmission(targetContext, aLoadState));
8641 // Transfer the load to the target BrowsingContext... Clear the window target
8642 // name to the empty string to prevent recursive retargeting!
8644 // No window target
8645 aLoadState->SetTarget(u""_ns);
8646 // No forced download
8647 aLoadState->SetFileName(VoidString());
8648 return targetContext->InternalLoad(aLoadState);
8651 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8652 nsAutoCString result;
8653 if (NS_FAILED(aURI->GetRef(result))) {
8654 result.SetIsVoid(true);
8656 return result;
8659 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8660 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8662 bool equal = false;
8663 if (mCurrentURI &&
8664 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8665 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8666 flags |= LOCATION_CHANGE_HASHCHANGE;
8669 return flags;
8672 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8673 SameDocumentNavigationState& aState) {
8674 MOZ_ASSERT(aLoadState);
8675 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8676 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8677 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8678 LOAD_FLAGS_REPLACE_HISTORY) ||
8679 aLoadState->LoadType() == LOAD_HISTORY ||
8680 aLoadState->LoadType() == LOAD_LINK)) {
8681 return false;
8684 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8686 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8687 if (NS_SUCCEEDED(rvURINew)) {
8688 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8691 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8692 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8693 if (NS_SUCCEEDED(rvURIOld)) {
8694 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8696 if (NS_SUCCEEDED(rvURIOld)) {
8697 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8698 &aState.mSameExceptHashes))) {
8699 aState.mSameExceptHashes = false;
8704 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8705 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8706 nsCOMPtr<nsIURI> currentExposableURI =
8707 nsIOService::CreateExposableURI(currentURI);
8708 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8709 if (NS_SUCCEEDED(rvURIOld)) {
8710 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8712 if (NS_SUCCEEDED(rvURIOld)) {
8713 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8714 aLoadState->URI(), &aState.mSameExceptHashes))) {
8715 aState.mSameExceptHashes = false;
8717 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8718 // have to perform a special check here to avoid an actual navigation. If
8719 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8720 // fact that the new URI is currently http), then set mSameExceptHashes to
8721 // true and only perform a fragment navigation.
8722 if (!aState.mSameExceptHashes) {
8723 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
8724 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8725 if (!docLoadInfo->GetLoadErrorPage() &&
8726 nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8727 currentExposableURI, aLoadState->URI(), docLoadInfo)) {
8728 uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
8729 if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
8730 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
8731 // At this point the requested URI is for sure a fragment
8732 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8733 // enabled. Also it is not interfering the upgrade order of
8734 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8735 // Since we are on an HTTPS site the fragment
8736 // navigation should also be an HTTPS.
8737 // For that reason we should upgrade the URI to HTTPS.
8738 aState.mSecureUpgradeURI = true;
8739 aState.mSameExceptHashes = true;
8747 if (mozilla::SessionHistoryInParent()) {
8748 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8749 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8750 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8752 MOZ_LOG(gSHLog, LogLevel::Debug,
8753 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8754 this, aState.mHistoryNavBetweenSameDoc));
8755 } else {
8756 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8757 // We're doing a history load.
8759 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8760 &aState.mHistoryNavBetweenSameDoc);
8764 // A same document navigation happens when we navigate between two SHEntries
8765 // for the same document. We do a same document navigation under two
8766 // circumstances. Either
8768 // a) we're navigating between two different SHEntries which share a
8769 // document, or
8771 // b) we're navigating to a new shentry whose URI differs from the
8772 // current URI only in its hash, the new hash is non-empty, and
8773 // we're not doing a POST.
8775 // The restriction that the SHEntries in (a) must be different ensures
8776 // that history.go(0) and the like trigger full refreshes, rather than
8777 // same document navigations.
8778 if (!mozilla::SessionHistoryInParent()) {
8779 bool doSameDocumentNavigation =
8780 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8781 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8782 aState.mSameExceptHashes && aState.mNewURIHasRef);
8783 MOZ_LOG(gSHLog, LogLevel::Debug,
8784 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8785 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8786 return doSameDocumentNavigation;
8789 if (aState.mHistoryNavBetweenSameDoc &&
8790 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8791 return true;
8794 MOZ_LOG(
8795 gSHLog, LogLevel::Debug,
8796 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8797 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8798 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8799 !aLoadState->PostDataStream() ? "true" : "false",
8800 aState.mSameExceptHashes ? "true" : "false",
8801 aState.mNewURIHasRef ? "true" : "false"));
8802 return !aLoadState->LoadIsFromSessionHistory() &&
8803 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8804 aState.mNewURIHasRef;
8807 nsresult nsDocShell::HandleSameDocumentNavigation(
8808 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState) {
8809 #ifdef DEBUG
8810 SameDocumentNavigationState state;
8811 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8812 #endif
8814 MOZ_LOG(gSHLog, LogLevel::Debug,
8815 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8816 mCurrentURI->GetSpecOrDefault().get(),
8817 aLoadState->URI()->GetSpecOrDefault().get()));
8819 RefPtr<Document> doc = GetDocument();
8820 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8821 doc->DoNotifyPossibleTitleChange();
8823 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8825 // We need to upgrade the new URI from http: to https:
8826 nsCOMPtr<nsIURI> newURI = aLoadState->URI();
8827 if (aState.mSecureUpgradeURI) {
8828 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
8829 MOZ_LOG(gSHLog, LogLevel::Debug,
8830 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
8833 #ifdef DEBUG
8834 if (aState.mSameExceptHashes) {
8835 bool sameExceptHashes = false;
8836 currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
8837 MOZ_ASSERT(sameExceptHashes);
8839 #endif
8841 // Save the position of the scrollers.
8842 nsPoint scrollPos = GetCurScrollPos();
8844 // Reset mLoadType to its original value once we exit this block, because this
8845 // same document navigation might have started after a normal, network load,
8846 // and we don't want to clobber its load type. See bug 737307.
8847 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8849 // If a non-same-document-navigation (i.e., a network load) is pending, make
8850 // this a replacement load, so that we don't add a SHEntry here and the
8851 // network load goes into the SHEntry it expects to.
8852 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8853 mLoadType = LOAD_NORMAL_REPLACE;
8854 } else {
8855 mLoadType = aLoadState->LoadType();
8858 mURIResultedInDocument = true;
8860 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8862 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8863 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8864 // onLocationChange() notifications to the browser to update back/forward
8865 // buttons.
8866 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8867 Nothing());
8868 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8869 mLoadingEntry.swap(oldLoadingEntry);
8870 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8871 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8872 *aLoadState->GetLoadingSessionHistoryInfo());
8873 mNeedToReportActiveAfterLoadingBecomesActive = false;
8876 // Set the doc's URI according to the new history entry's URI.
8877 doc->SetDocumentURI(newURI);
8879 /* This is a anchor traversal within the same page.
8880 * call OnNewURI() so that, this traversal will be
8881 * recorded in session and global history.
8883 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8884 newURIPartitionedPrincipalToInherit;
8885 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8886 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8887 if (mozilla::SessionHistoryInParent()) {
8888 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8889 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8890 newURIPartitionedPrincipalToInherit =
8891 mActiveEntry->GetPartitionedPrincipalToInherit();
8892 newCsp = mActiveEntry->GetCsp();
8893 } else {
8894 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8895 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8896 newURIPartitionedPrincipalToInherit =
8897 mOSHE->GetPartitionedPrincipalToInherit();
8898 newCsp = mOSHE->GetCsp();
8900 } else {
8901 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8902 newURIPrincipalToInherit = doc->NodePrincipal();
8903 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8904 newCsp = doc->GetCsp();
8907 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
8909 // Pass true for aCloneSHChildren, since we're not
8910 // changing documents here, so all of our subframes are
8911 // still relevant to the new session history entry.
8913 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8914 // flag on firing onLocationChange(...).
8915 // Anyway, aCloneSHChildren param is simply reflecting
8916 // doSameDocumentNavigation in this scope.
8918 // Note: we'll actually fire onLocationChange later, in order to preserve
8919 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8920 // 1668126)
8921 bool locationChangeNeeded = OnNewURI(
8922 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
8923 newURIPartitionedPrincipalToInherit, newCsp, false, true, true);
8925 nsCOMPtr<nsIInputStream> postData;
8926 uint32_t cacheKey = 0;
8928 bool scrollRestorationIsManual = false;
8929 if (!mozilla::SessionHistoryInParent()) {
8930 if (mOSHE) {
8931 /* save current position of scroller(s) (bug 59774) */
8932 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8933 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8934 // Get the postdata and page ident from the current page, if
8935 // the new load is being done via normal means. Note that
8936 // "normal means" can be checked for just by checking for
8937 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8938 // above -- it filters out some LOAD_CMD_NORMAL cases that we
8939 // wouldn't want here.
8940 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8941 postData = mOSHE->GetPostData();
8942 cacheKey = mOSHE->GetCacheKey();
8945 // Link our new SHEntry to the old SHEntry's back/forward
8946 // cache data, since the two SHEntries correspond to the
8947 // same document.
8948 if (mLSHE) {
8949 if (!aLoadState->LoadIsFromSessionHistory()) {
8950 // If we're not doing a history load, scroll restoration
8951 // should be inherited from the previous session history entry.
8952 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8953 scrollRestorationIsManual);
8955 mLSHE->AdoptBFCacheEntry(mOSHE);
8958 } else {
8959 if (mActiveEntry) {
8960 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8961 if (mBrowsingContext) {
8962 CollectWireframe();
8963 if (XRE_IsParentProcess()) {
8964 SessionHistoryEntry* entry =
8965 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8966 if (entry) {
8967 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8969 } else {
8970 mozilla::Unused << ContentChild::GetSingleton()
8971 ->SendSessionHistoryEntryScrollPosition(
8972 mBrowsingContext, scrollPos.x,
8973 scrollPos.y);
8977 if (mLoadingEntry) {
8978 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8979 // If we're not doing a history load, scroll restoration
8980 // should be inherited from the previous session history entry.
8981 // XXX This needs most probably tweaks once fragment navigation is
8982 // fixed to work with session-history-in-parent.
8983 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8984 scrollRestorationIsManual);
8989 // If we're doing a history load, use its scroll restoration state.
8990 if (aLoadState->LoadIsFromSessionHistory()) {
8991 if (mozilla::SessionHistoryInParent()) {
8992 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8993 ->mInfo.GetScrollRestorationIsManual();
8994 } else {
8995 scrollRestorationIsManual =
8996 aLoadState->SHEntry()->GetScrollRestorationIsManual();
9000 /* Assign mLSHE to mOSHE. This will either be a new entry created
9001 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
9002 * loads.
9004 if (!mozilla::SessionHistoryInParent()) {
9005 if (mLSHE) {
9006 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
9007 // Save the postData obtained from the previous page
9008 // in to the session history entry created for the
9009 // anchor page, so that any history load of the anchor
9010 // page will restore the appropriate postData.
9011 if (postData) {
9012 mOSHE->SetPostData(postData);
9015 // Make sure we won't just repost without hitting the
9016 // cache first
9017 if (cacheKey != 0) {
9018 mOSHE->SetCacheKey(cacheKey);
9022 /* Set the title for the SH entry for this target url so that
9023 * SH menus in go/back/forward buttons won't be empty for this.
9024 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
9025 * the code above.
9026 * Note, when session history lives in the parent process, this does not
9027 * update the title there.
9029 SetTitleOnHistoryEntry(false);
9030 } else {
9031 if (aLoadState->LoadIsFromSessionHistory()) {
9032 MOZ_LOG(
9033 gSHLog, LogLevel::Debug,
9034 ("Moving the loading entry to the active entry on nsDocShell %p to "
9035 "%s",
9036 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
9038 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
9039 if (mActiveEntry) {
9040 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
9043 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
9044 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
9045 if (currentLayoutHistoryState) {
9046 // Restore the existing nsILayoutHistoryState object, since it is
9047 // possibly being used by the layout. When doing a new load, the
9048 // shared state is copied from the existing active entry, so this
9049 // special case is needed only with the history loads.
9050 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
9053 if (cacheKey != 0) {
9054 mActiveEntry->SetCacheKey(cacheKey);
9056 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
9057 // does require a non-null uri if this is for a refresh load of the same
9058 // URI, but in that case mCurrentURI won't be null here.
9059 mBrowsingContext->SessionHistoryCommit(
9060 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
9061 true, true,
9062 /* No expiration update on the same document loads*/
9063 false, cacheKey);
9064 // FIXME Need to set postdata.
9066 // Set the title for the SH entry for this target url so that
9067 // SH menus in go/back/forward buttons won't be empty for this.
9068 // Note, when session history lives in the parent process, this does not
9069 // update the title there.
9070 SetTitleOnHistoryEntry(false);
9071 } else {
9072 Maybe<bool> scrollRestorationIsManual;
9073 if (mActiveEntry) {
9074 scrollRestorationIsManual.emplace(
9075 mActiveEntry->GetScrollRestorationIsManual());
9077 // Get the postdata and page ident from the current page, if the new
9078 // load is being done via normal means. Note that "normal means" can be
9079 // checked for just by checking for LOAD_CMD_NORMAL, given the loadType
9080 // and allowScroll check above -- it filters out some LOAD_CMD_NORMAL
9081 // cases that we wouldn't want here.
9082 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
9083 postData = mActiveEntry->GetPostData();
9084 cacheKey = mActiveEntry->GetCacheKey();
9088 MOZ_LOG(gSHLog, LogLevel::Debug,
9089 ("Creating an active entry on nsDocShell %p to %s", this,
9090 newURI->GetSpecOrDefault().get()));
9091 if (mActiveEntry) {
9092 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, newURI);
9093 } else {
9094 mActiveEntry = MakeUnique<SessionHistoryInfo>(
9095 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
9096 newURIPartitionedPrincipalToInherit, newCsp, mContentTypeHint);
9099 // Save the postData obtained from the previous page in to the session
9100 // history entry created for the anchor page, so that any history load of
9101 // the anchor page will restore the appropriate postData.
9102 if (postData) {
9103 mActiveEntry->SetPostData(postData);
9106 // Make sure we won't just repost without hitting the
9107 // cache first
9108 if (cacheKey != 0) {
9109 mActiveEntry->SetCacheKey(cacheKey);
9112 // Set the title for the SH entry for this target url so that
9113 // SH menus in go/back/forward buttons won't be empty for this.
9114 mActiveEntry->SetTitle(mTitle);
9116 if (scrollRestorationIsManual.isSome()) {
9117 mActiveEntry->SetScrollRestorationIsManual(
9118 scrollRestorationIsManual.value());
9121 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9122 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
9123 } else {
9124 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
9125 // FIXME We should probably just compute mChildOffset in the parent
9126 // instead of passing it over IPC here.
9127 mBrowsingContext->SetActiveSessionHistoryEntry(
9128 Some(scrollPos), mActiveEntry.get(), mLoadType, cacheKey);
9129 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9134 if (locationChangeNeeded) {
9135 FireOnLocationChange(this, nullptr, newURI, locationChangeFlags);
9138 /* Restore the original LSHE if we were loading something
9139 * while same document navigation was initiated.
9141 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
9142 mLoadingEntry.swap(oldLoadingEntry);
9144 /* Set the title for the Global History entry for this anchor url.
9146 UpdateGlobalHistoryTitle(newURI);
9148 SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
9150 // Inform the favicon service that the favicon for oldURI also
9151 // applies to newURI.
9152 CopyFavicon(currentURI, newURI, UsePrivateBrowsing());
9154 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9155 nsCOMPtr<nsPIDOMWindowInner> win =
9156 scriptGlobal ? scriptGlobal->GetCurrentInnerWindow() : nullptr;
9158 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9159 // the function decides whether a scroll is appropriate based on the
9160 // arguments it receives. But even if we don't end up scrolling,
9161 // ScrollToAnchor performs other important tasks, such as informing
9162 // the presShell that we have a new hash. See bug 680257.
9163 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
9164 aState.mNewHash, aLoadState->LoadType());
9165 NS_ENSURE_SUCCESS(rv, rv);
9167 /* restore previous position of scroller(s), if we're moving
9168 * back in history (bug 59774)
9170 nscoord bx = 0;
9171 nscoord by = 0;
9172 bool needsScrollPosUpdate = false;
9173 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
9174 (aLoadState->LoadType() == LOAD_HISTORY ||
9175 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
9176 !scrollRestorationIsManual) {
9177 needsScrollPosUpdate = true;
9178 if (mozilla::SessionHistoryInParent()) {
9179 mActiveEntry->GetScrollPosition(&bx, &by);
9180 } else {
9181 mOSHE->GetScrollPosition(&bx, &by);
9185 // Dispatch the popstate and hashchange events, as appropriate.
9187 // The event dispatch below can cause us to re-enter script and
9188 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9189 // reference to avoid null derefs. See bug 914521.
9190 if (win) {
9191 // Fire a hashchange event URIs differ, and only in their hashes.
9192 bool doHashchange = aState.mSameExceptHashes &&
9193 (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
9194 !aState.mCurrentHash.Equals(aState.mNewHash));
9196 if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
9197 win->DispatchSyncPopState();
9200 if (needsScrollPosUpdate && win->HasActiveDocument()) {
9201 SetCurScrollPosEx(bx, by);
9204 if (doHashchange) {
9205 // Note that currentURI hasn't changed because it's on the
9206 // stack, so we can just use it directly as the old URI.
9207 win->DispatchAsyncHashchange(currentURI, newURI);
9211 return NS_OK;
9214 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
9215 nsDocShellLoadState* aLoadState) {
9216 if (!aLoadState->AllowFocusMove()) {
9217 return false;
9219 if (!aLoadState->HasValidUserGestureActivation()) {
9220 return false;
9222 const auto& sourceBC = aLoadState->SourceBrowsingContext();
9223 if (!sourceBC || !sourceBC->IsActive()) {
9224 // If the navigation didn't come from a foreground tab, then we don't steal
9225 // focus.
9226 return false;
9228 auto* bc = aDocShell->GetBrowsingContext();
9229 if (sourceBC.get() == bc) {
9230 // If it comes from the same tab / frame, don't steal focus either.
9231 return false;
9233 auto* fm = nsFocusManager::GetFocusManager();
9234 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
9235 // If we're already on the foreground tab of the foreground window, then we
9236 // don't need to do this. This helps to e.g. not steal focus from the
9237 // browser chrome unnecessarily.
9238 return false;
9240 if (auto* doc = aDocShell->GetExtantDocument()) {
9241 if (doc->IsInitialDocument()) {
9242 // If we're the initial load for the browsing context, the browser
9243 // chrome determines what to focus. This is important because the
9244 // browser chrome may want to e.g focus the url-bar
9245 return false;
9248 // Take loadDivertedInBackground into account so the behavior would be the
9249 // same as how the tab first opened.
9250 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9253 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
9254 BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) {
9255 MOZ_ASSERT(aLoadState->IsFormSubmission());
9257 // https://html.spec.whatwg.org/#form-submission-algorithm
9258 // 22. Let historyHandling be "push".
9259 // 23. If form document equals targetNavigable's active document, and
9260 // form document has not yet completely loaded, then set
9261 // historyHandling to "replace".
9262 return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument
9263 ? LOAD_NORMAL_REPLACE
9264 : LOAD_LINK;
9267 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
9268 Maybe<uint32_t> aCacheKey) {
9269 MOZ_ASSERT(aLoadState, "need a load state!");
9270 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
9271 "need a valid TriggeringPrincipal");
9273 if (!aLoadState->TriggeringPrincipal()) {
9274 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9275 return NS_ERROR_FAILURE;
9277 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
9278 return NS_ERROR_NOT_AVAILABLE;
9281 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
9283 mOriginalUriString.Truncate();
9285 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9286 ("DOCSHELL %p InternalLoad %s\n", this,
9287 aLoadState->URI()->GetSpecOrDefault().get()));
9289 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
9291 // Cancel loads coming from Docshells that are being destroyed.
9292 if (mIsBeingDestroyed) {
9293 return NS_ERROR_NOT_AVAILABLE;
9296 nsresult rv = EnsureScriptEnvironment();
9297 if (NS_FAILED(rv)) {
9298 return rv;
9301 // If we have a target to move to, do that now.
9302 if (!aLoadState->Target().IsEmpty()) {
9303 return PerformRetargeting(aLoadState);
9306 // This is the non-retargeting load path, we've already set the right loadtype
9307 // for form submissions in nsDocShell::OnLinkClickSync.
9308 if (aLoadState->TargetBrowsingContext().IsNull()) {
9309 aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
9312 MOZ_DIAGNOSTIC_ASSERT(
9313 aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
9314 "Load must be targeting this BrowsingContext");
9316 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
9318 // If we don't have a target, we're loading into ourselves, and our load
9319 // delegate may want to intercept that load.
9320 SameDocumentNavigationState sameDocumentNavigationState;
9321 bool sameDocument =
9322 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
9323 !aLoadState->GetPendingRedirectedChannel();
9325 // Note: We do this check both here and in BrowsingContext::
9326 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9327 // available in the process triggering the load, and we don't want the target
9328 // process to have to trust the triggering process to do the appropriate
9329 // checks for the BrowsingContext's sandbox flags.
9330 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
9332 NS_ENSURE_STATE(!HasUnloadedParent());
9334 rv = CheckLoadingPermissions();
9335 if (NS_FAILED(rv)) {
9336 return rv;
9339 if (mFiredUnloadEvent) {
9340 if (IsOKToLoadURI(aLoadState->URI())) {
9341 MOZ_ASSERT(aLoadState->Target().IsEmpty(),
9342 "Shouldn't have a window target here!");
9344 // If this is a replace load, make whatever load triggered
9345 // the unload event also a replace load, so we don't
9346 // create extra history entries.
9347 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
9348 LOAD_FLAGS_REPLACE_HISTORY)) {
9349 mLoadType = LOAD_NORMAL_REPLACE;
9352 // Do this asynchronously
9353 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
9354 return Dispatch(TaskCategory::Other, ev.forget());
9357 // Just ignore this load attempt
9358 return NS_OK;
9361 // If we are loading a URI that should inherit a security context (basically
9362 // javascript: at this point), and the caller has said that principal
9363 // inheritance is allowed, there are a few possible cases:
9365 // 1) We are provided with the principal to inherit. In that case, we just use
9366 // it.
9368 // 2) The load is coming from some other application. In this case we don't
9369 // want to inherit from whatever document we have loaded now, since the
9370 // load is unrelated to it.
9372 // 3) It's a load from our application, but does not provide an explicit
9373 // principal to inherit. In that case, we want to inherit the principal of
9374 // our current document, or of our parent document (if any) if we don't
9375 // have a current document.
9377 bool inherits;
9379 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
9380 !aLoadState->PrincipalToInherit() &&
9381 (aLoadState->HasInternalLoadFlags(
9382 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
9383 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9384 aLoadState->URI(), &inherits)) &&
9385 inherits) {
9386 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
9388 // If principalToInherit is still null (e.g. if some of the conditions of
9389 // were not satisfied), then no inheritance of any sort will happen: the
9390 // load will just get a principal based on the URI being loaded.
9393 // If this docshell is owned by a frameloader, make sure to cancel
9394 // possible frameloader initialization before loading a new page.
9395 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
9396 if (parent) {
9397 RefPtr<Document> doc = parent->GetDocument();
9398 if (doc) {
9399 doc->TryCancelFrameLoaderInitialization(this);
9403 // Before going any further vet loads initiated by external programs.
9404 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9405 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
9407 // Disallow external chrome: loads targetted at content windows
9408 if (SchemeIsChrome(aLoadState->URI())) {
9409 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9410 return NS_ERROR_FAILURE;
9413 // clear the decks to prevent context bleed-through (bug 298255)
9414 rv = CreateAboutBlankContentViewer(nullptr, nullptr, nullptr, nullptr,
9415 /* aIsInitialDocument */ false);
9416 if (NS_FAILED(rv)) {
9417 return NS_ERROR_FAILURE;
9421 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
9422 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
9423 mURIResultedInDocument = false; // reset the clock...
9425 // See if this is actually a load between two history entries for the same
9426 // document. If the process fails, or if we successfully navigate within the
9427 // same document, return.
9428 if (sameDocument) {
9429 nsresult rv =
9430 HandleSameDocumentNavigation(aLoadState, sameDocumentNavigationState);
9431 NS_ENSURE_SUCCESS(rv, rv);
9432 if (shouldTakeFocus) {
9433 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9435 return rv;
9438 // mContentViewer->PermitUnload can destroy |this| docShell, which
9439 // causes the next call of CanSavePresentation to crash.
9440 // Hold onto |this| until we return, to prevent a crash from happening.
9441 // (bug#331040)
9442 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9444 // Don't init timing for javascript:, since it generally doesn't
9445 // actually start a load or anything. If it does, we'll init
9446 // timing then, from OnStateChange.
9448 // XXXbz mTiming should know what channel it's for, so we don't
9449 // need this hackery.
9450 bool toBeReset = false;
9451 bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
9453 if (!isJavaScript) {
9454 toBeReset = MaybeInitTiming();
9456 bool isNotDownload = aLoadState->FileName().IsVoid();
9457 if (mTiming && isNotDownload) {
9458 mTiming->NotifyBeforeUnload();
9460 // Check if the page doesn't want to be unloaded. The javascript:
9461 // protocol handler deals with this for javascript: URLs.
9462 if (!isJavaScript && isNotDownload &&
9463 !aLoadState->NotifiedBeforeUnloadListeners() && mContentViewer) {
9464 bool okToUnload;
9466 // Check if request is exempted from HTTPSOnlyMode and if https-first is
9467 // enabled, if so it means:
9468 // * https-first failed to upgrade request to https
9469 // * we already asked for permission to unload and the user accepted
9470 // otherwise we wouldn't be here.
9471 bool isPrivateWin = GetOriginAttributes().mPrivateBrowsingId > 0;
9472 bool isHistoryOrReload = false;
9473 uint32_t loadType = aLoadState->LoadType();
9475 // Check if request is a reload.
9476 if (loadType == LOAD_RELOAD_NORMAL ||
9477 loadType == LOAD_RELOAD_BYPASS_CACHE ||
9478 loadType == LOAD_RELOAD_BYPASS_PROXY ||
9479 loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
9480 loadType == LOAD_HISTORY) {
9481 isHistoryOrReload = true;
9484 // If it isn't a reload, the request already failed to be upgraded and
9485 // https-first is enabled then don't ask the user again for permission to
9486 // unload and just unload.
9487 if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSOnlyMode() &&
9488 nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
9489 rv = mContentViewer->PermitUnload(
9490 nsIContentViewer::PermitUnloadAction::eDontPromptAndUnload,
9491 &okToUnload);
9492 } else {
9493 rv = mContentViewer->PermitUnload(&okToUnload);
9496 if (NS_SUCCEEDED(rv) && !okToUnload) {
9497 // The user chose not to unload the page, interrupt the
9498 // load.
9499 MaybeResetInitTiming(toBeReset);
9500 return NS_OK;
9504 if (mTiming && isNotDownload) {
9505 mTiming->NotifyUnloadAccepted(mCurrentURI);
9508 // In e10s, in the parent process, we refuse to load anything other than
9509 // "safe" resources that we ship or trust enough to give "special" URLs.
9510 // Similar check will be performed by the ParentProcessDocumentChannel if in
9511 // use.
9512 if (XRE_IsE10sParentProcess() &&
9513 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
9514 !CanLoadInParentProcess(aLoadState->URI())) {
9515 return NS_ERROR_FAILURE;
9518 // Whenever a top-level browsing context is navigated, the user agent MUST
9519 // lock the orientation of the document to the document's default
9520 // orientation. We don't explicitly check for a top-level browsing context
9521 // here because orientation is only set on top-level browsing contexts.
9522 if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) {
9523 MOZ_ASSERT(mBrowsingContext->IsTop());
9524 MOZ_ALWAYS_SUCCEEDS(
9525 mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None));
9526 if (mBrowsingContext->IsActive()) {
9527 ScreenOrientation::UpdateActiveOrientationLock(
9528 hal::ScreenOrientation::None);
9532 // Check for saving the presentation here, before calling Stop().
9533 // This is necessary so that we can catch any pending requests.
9534 // Since the new request has not been created yet, we pass null for the
9535 // new request parameter.
9536 // Also pass nullptr for the document, since it doesn't affect the return
9537 // value for our purposes here.
9538 bool savePresentation =
9539 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr,
9540 /* aReportBFCacheComboTelemetry */ true);
9542 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9543 // separate check for SHIP so that we know if there are ongoing requests
9544 // before calling Stop() below.
9545 if (mozilla::SessionHistoryInParent()) {
9546 Document* document = GetDocument();
9547 uint32_t flags = 0;
9548 if (document && !document->CanSavePresentation(nullptr, flags, true)) {
9549 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9550 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9551 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9552 // Also, we want to report all the flags to the parent process here (and
9553 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9554 // telemetry data correctly.
9555 document->DisallowBFCaching(flags);
9559 // Don't stop current network activity for javascript: URL's since
9560 // they might not result in any data, and thus nothing should be
9561 // stopped in those cases. In the case where they do result in
9562 // data, the javascript: URL channel takes care of stopping
9563 // current network activity.
9564 if (!isJavaScript && isNotDownload) {
9565 // Stop any current network activity.
9566 // Also stop content if this is a zombie doc. otherwise
9567 // the onload will be delayed by other loads initiated in the
9568 // background by the first document that
9569 // didn't fully load before the next load was initiated.
9570 // If not a zombie, don't stop content until data
9571 // starts arriving from the new URI...
9573 if ((mContentViewer && mContentViewer->GetPreviousViewer()) ||
9574 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
9575 rv = Stop(nsIWebNavigation::STOP_ALL);
9576 } else {
9577 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9580 if (NS_FAILED(rv)) {
9581 return rv;
9585 mLoadType = aLoadState->LoadType();
9587 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9588 // been called. But when loading an error page, do not clear the
9589 // mLSHE for the real page.
9590 if (mLoadType != LOAD_ERROR_PAGE) {
9591 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
9592 Nothing());
9593 if (aLoadState->LoadIsFromSessionHistory() &&
9594 !mozilla::SessionHistoryInParent()) {
9595 // We're making history navigation or a reload. Make sure our history ID
9596 // points to the same ID as SHEntry's docshell ID.
9597 nsID historyID = {};
9598 aLoadState->SHEntry()->GetDocshellID(historyID);
9600 Unused << mBrowsingContext->SetHistoryID(historyID);
9604 mSavingOldViewer = savePresentation;
9606 // If we have a saved content viewer in history, restore and show it now.
9607 if (aLoadState->LoadIsFromSessionHistory() &&
9608 (mLoadType & LOAD_CMD_HISTORY)) {
9609 // https://html.spec.whatwg.org/#history-traversal:
9610 // To traverse the history
9611 // "If entry has a different Document object than the current entry, then
9612 // run the following substeps: Remove any tasks queued by the history
9613 // traversal task source..."
9614 // Same document object case was handled already above with
9615 // HandleSameDocumentNavigation call.
9616 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
9617 if (shistory) {
9618 shistory->RemovePendingHistoryNavigations();
9620 if (!mozilla::SessionHistoryInParent()) {
9621 // It's possible that the previous viewer of mContentViewer is the
9622 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9623 // If that's the case, we need to go ahead and force it into its shentry
9624 // so we can restore it.
9625 if (mContentViewer) {
9626 nsCOMPtr<nsIContentViewer> prevViewer =
9627 mContentViewer->GetPreviousViewer();
9628 if (prevViewer) {
9629 #ifdef DEBUG
9630 nsCOMPtr<nsIContentViewer> prevPrevViewer =
9631 prevViewer->GetPreviousViewer();
9632 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9633 #endif
9634 nsCOMPtr<nsISHEntry> viewerEntry;
9635 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9636 if (viewerEntry == aLoadState->SHEntry()) {
9637 // Make sure this viewer ends up in the right place
9638 mContentViewer->SetPreviousViewer(nullptr);
9639 prevViewer->Destroy();
9643 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9644 bool restoring;
9645 rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
9646 if (restoring) {
9647 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
9648 return rv;
9650 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
9652 // We failed to restore the presentation, so clean up.
9653 // Both the old and new history entries could potentially be in
9654 // an inconsistent state.
9655 if (NS_FAILED(rv)) {
9656 if (oldEntry) {
9657 oldEntry->SyncPresentationState();
9660 aLoadState->SHEntry()->SyncPresentationState();
9665 bool isTopLevelDoc = mBrowsingContext->IsTopContent();
9667 OriginAttributes attrs = GetOriginAttributes();
9668 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
9670 PredictorLearn(aLoadState->URI(), nullptr,
9671 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
9672 PredictorPredict(aLoadState->URI(), nullptr,
9673 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
9675 nsCOMPtr<nsIRequest> req;
9676 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
9678 if (NS_SUCCEEDED(rv)) {
9679 if (shouldTakeFocus) {
9680 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9684 if (NS_FAILED(rv)) {
9685 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9686 UnblockEmbedderLoadEventForFailure();
9687 nsCOMPtr<nsIURI> uri = aLoadState->URI();
9688 if (DisplayLoadError(rv, uri, nullptr, chan) &&
9689 // FIXME: At this point code was using internal load flags, but checking
9690 // non-internal load flags?
9691 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
9692 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9695 // We won't report any error if this is an unknown protocol error. The
9696 // reason behind this is that it will allow enumeration of external
9697 // protocols if we report an error for each unknown protocol.
9698 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
9699 return NS_OK;
9703 return rv;
9706 /* static */
9707 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
9708 nsCOMPtr<nsIURI> uri = aURI;
9709 // In e10s, in the parent process, we refuse to load anything other than
9710 // "safe" resources that we ship or trust enough to give "special" URLs.
9711 bool canLoadInParent = false;
9712 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9713 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
9714 canLoadInParent) {
9715 // We allow UI resources.
9716 return true;
9718 // For about: and extension-based URIs, which don't get
9719 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9720 while (uri && uri->SchemeIs("view-source")) {
9721 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
9722 if (nested) {
9723 nested->GetInnerURI(getter_AddRefs(uri));
9724 } else {
9725 break;
9728 // Allow about: URIs, and allow moz-extension ones if we're running
9729 // extension content in the parent process.
9730 if (!uri || uri->SchemeIs("about") ||
9731 (!StaticPrefs::extensions_webextensions_remote() &&
9732 uri->SchemeIs("moz-extension"))) {
9733 return true;
9735 #ifdef MOZ_THUNDERBIRD
9736 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
9737 uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
9738 uri->SchemeIs("snews")) {
9739 return true;
9741 #endif
9742 nsAutoCString scheme;
9743 uri->GetScheme(scheme);
9744 // Allow ext+foo URIs (extension-registered custom protocols). See
9745 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9746 if (StringBeginsWith(scheme, "ext+"_ns) &&
9747 !StaticPrefs::extensions_webextensions_remote()) {
9748 return true;
9750 // Final exception for some legacy automated tests:
9751 if (xpc::IsInAutomation() &&
9752 StaticPrefs::security_allow_unsafe_parent_loads()) {
9753 return true;
9755 return false;
9758 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
9759 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
9760 RefPtr<Document> document;
9761 bool inheritedFromCurrent = false;
9763 if (aConsiderCurrentDocument && mContentViewer) {
9764 document = mContentViewer->GetDocument();
9765 inheritedFromCurrent = true;
9768 if (!document) {
9769 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9770 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9771 if (parentItem) {
9772 document = parentItem->GetDocument();
9776 if (!document) {
9777 if (!aConsiderCurrentDocument) {
9778 return nullptr;
9781 // Make sure we end up with _something_ as the principal no matter
9782 // what.If this fails, we'll just get a null docViewer and bail.
9783 EnsureContentViewer();
9784 if (!mContentViewer) {
9785 return nullptr;
9787 document = mContentViewer->GetDocument();
9790 //-- Get the document's principal
9791 if (document) {
9792 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
9793 ? document->PartitionedPrincipal()
9794 : document->NodePrincipal();
9796 // Don't allow loads in typeContent docShells to inherit the system
9797 // principal from existing documents.
9798 if (inheritedFromCurrent && mItemType == typeContent &&
9799 docPrincipal->IsSystemPrincipal()) {
9800 return nullptr;
9803 return docPrincipal;
9806 return nullptr;
9809 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9810 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9811 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9812 const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9813 nsCOMPtr<nsIChannel> channel;
9814 if (aSrcdoc.IsVoid()) {
9815 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9816 nullptr, // PerformanceStorage
9817 nullptr, // loadGroup
9818 aCallbacks, aLoadFlags));
9820 if (aBaseURI) {
9821 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9822 if (vsc) {
9823 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9826 } else if (SchemeIsViewSource(aURI)) {
9827 // Instantiate view source handler protocol, if it doesn't exist already.
9828 nsCOMPtr<nsIIOService> io(do_GetIOService());
9829 MOZ_ASSERT(io);
9830 nsCOMPtr<nsIProtocolHandler> handler;
9831 nsresult rv =
9832 io->GetProtocolHandler("view-source", getter_AddRefs(handler));
9833 if (NS_FAILED(rv)) {
9834 return rv;
9837 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9838 if (!vsh) {
9839 return NS_ERROR_FAILURE;
9842 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9843 getter_AddRefs(channel)));
9844 } else {
9845 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
9846 aSrcdoc, "text/html"_ns, aLoadInfo,
9847 true));
9848 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9849 MOZ_ASSERT(isc);
9850 isc->SetBaseURI(aBaseURI);
9853 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9854 nsresult rv = channel->SetLoadFlags(aLoadFlags);
9855 NS_ENSURE_SUCCESS(rv, rv);
9858 channel.forget(aChannel);
9859 return NS_OK;
9862 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9863 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9864 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9865 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9866 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9867 nsIChannel** aChannel) {
9868 MOZ_ASSERT(aLoadInfo);
9870 nsString srcdoc = VoidString();
9871 bool isSrcdoc =
9872 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9873 if (isSrcdoc) {
9874 srcdoc = aLoadState->SrcdocData();
9877 aLoadInfo->SetTriggeringRemoteType(
9878 aLoadState->GetEffectiveTriggeringRemoteType());
9880 if (aLoadState->PrincipalToInherit()) {
9881 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9883 aLoadInfo->SetLoadTriggeredFromExternal(
9884 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
9885 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
9886 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9887 aLoadInfo->SetOriginalFrameSrcLoad(
9888 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9890 bool inheritAttrs = false;
9891 if (aLoadState->PrincipalToInherit()) {
9892 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9893 aLoadState->PrincipalToInherit(), aLoadState->URI(),
9894 true, // aInheritForAboutBlank
9895 isSrcdoc);
9898 // Strip the target query parameters before creating the channel.
9899 aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext);
9901 OriginAttributes attrs;
9903 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9904 // true. Otherwise we just use the origin attributes from docshell.
9905 if (inheritAttrs) {
9906 MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9907 "We should have PrincipalToInherit here.");
9908 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9909 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9910 // have the same origin attributes with docshell.
9911 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9912 attrs == aOriginAttributes);
9913 } else {
9914 attrs = aOriginAttributes;
9915 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9916 aLoadState->URI());
9919 aRv = aLoadInfo->SetOriginAttributes(attrs);
9920 if (NS_WARN_IF(NS_FAILED(aRv))) {
9921 return false;
9924 if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9925 aLoadInfo->SetIsFromProcessingFrameAttributes();
9928 // Propagate the IsFormSubmission flag to the loadInfo.
9929 if (aLoadState->IsFormSubmission()) {
9930 aLoadInfo->SetIsFormSubmission(true);
9933 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
9935 nsCOMPtr<nsIChannel> channel;
9936 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9937 aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9938 aLoadState->BaseURI());
9939 NS_ENSURE_SUCCESS(aRv, false);
9941 if (!channel) {
9942 return false;
9945 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9946 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9947 // HTTPS_ONLY_EXEMPT.
9948 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
9950 // hack
9951 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9952 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9953 do_QueryInterface(channel));
9954 nsCOMPtr<nsIURI> referrer;
9955 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9956 if (referrerInfo) {
9957 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9959 if (httpChannelInternal) {
9960 if (aLoadState->HasInternalLoadFlags(
9961 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9962 aRv = httpChannelInternal->SetThirdPartyFlags(
9963 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9964 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9966 if (aLoadState->FirstParty()) {
9967 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9968 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9969 } else {
9970 aRv = httpChannelInternal->SetDocumentURI(referrer);
9971 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9973 aRv = httpChannelInternal->SetRedirectMode(
9974 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9975 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9978 if (httpChannel) {
9979 if (aLoadState->HeadersStream()) {
9980 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9982 // Set the referrer explicitly
9983 // Referrer is currenly only set for link clicks here.
9984 if (referrerInfo) {
9985 aRv = httpChannel->SetReferrerInfo(referrerInfo);
9986 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9989 // Mark the http channel as UrgentStart for top level document loading in
9990 // active tab.
9991 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9992 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9993 if (cos) {
9994 cos->AddClassFlags(nsIClassOfService::UrgentStart);
9999 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
10000 : aLoadState->URI());
10002 const nsACString& typeHint = aLoadState->TypeHint();
10003 if (!typeHint.IsVoid()) {
10004 channel->SetContentType(typeHint);
10007 const nsAString& fileName = aLoadState->FileName();
10008 if (!fileName.IsVoid()) {
10009 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
10010 NS_ENSURE_SUCCESS(aRv, false);
10011 if (!fileName.IsEmpty()) {
10012 aRv = channel->SetContentDispositionFilename(fileName);
10013 NS_ENSURE_SUCCESS(aRv, false);
10017 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
10018 nsCOMPtr<nsIURI> referrer;
10019 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
10020 if (referrerInfo) {
10021 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
10023 // save true referrer for those who need it (e.g. xpinstall whitelisting)
10024 // Currently only http and ftp channels support this.
10025 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
10027 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
10028 props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true);
10032 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
10033 auto loadType = aLoadState->LoadType();
10035 if (loadType == LOAD_RELOAD_NORMAL &&
10036 StaticPrefs::
10037 browser_soft_reload_only_force_validate_top_level_document()) {
10038 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10039 if (cachingChannel) {
10040 cachingChannel->SetForceValidateCacheContent(true);
10044 // figure out if we need to set the post data stream on the channel...
10045 if (aLoadState->PostDataStream()) {
10046 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
10047 do_QueryInterface(channel)) {
10048 // XXX it's a bit of a hack to rewind the postdata stream here but
10049 // it has to be done in case the post data is being reused multiple
10050 // times.
10051 nsCOMPtr<nsISeekableStream> postDataSeekable =
10052 do_QueryInterface(aLoadState->PostDataStream());
10053 if (postDataSeekable) {
10054 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
10055 NS_ENSURE_SUCCESS(aRv, false);
10058 // we really need to have a content type associated with this stream!!
10059 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
10061 // Ownership of the stream has transferred to the channel, clear our
10062 // reference.
10063 aLoadState->SetPostDataStream(nullptr);
10066 /* If there is a valid postdata *and* it is a History Load,
10067 * set up the cache key on the channel, to retrieve the
10068 * data *only* from the cache. If it is a normal reload, the
10069 * cache is free to go to the server for updated postdata.
10071 if (cacheChannel && aCacheKey != 0) {
10072 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
10073 cacheChannel->SetCacheKey(aCacheKey);
10074 uint32_t loadFlags;
10075 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
10076 channel->SetLoadFlags(loadFlags |
10077 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
10079 } else if (loadType == LOAD_RELOAD_NORMAL) {
10080 cacheChannel->SetCacheKey(aCacheKey);
10083 } else {
10084 /* If there is no postdata, set the cache key on the channel, and
10085 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10086 * will be free to get it from net if it is not found in cache.
10087 * New cache may use it creatively on CGI pages with GET
10088 * method and even on those that say "no-cache"
10090 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
10091 loadType == LOAD_RELOAD_CHARSET_CHANGE ||
10092 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
10093 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
10094 if (cacheChannel && aCacheKey != 0) {
10095 cacheChannel->SetCacheKey(aCacheKey);
10100 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
10101 // Allow execution against our context if the principals match
10102 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10105 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
10106 timedChannel->SetTimingEnabled(true);
10108 nsString initiatorType;
10109 switch (aLoadInfo->InternalContentPolicyType()) {
10110 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
10111 initiatorType = u"embed"_ns;
10112 break;
10113 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
10114 initiatorType = u"object"_ns;
10115 break;
10116 default: {
10117 const auto& embedderElementType =
10118 aBrowsingContext->GetEmbedderElementType();
10119 if (embedderElementType) {
10120 initiatorType = *embedderElementType;
10122 break;
10126 if (!initiatorType.IsEmpty()) {
10127 timedChannel->SetInitiatorType(initiatorType);
10131 nsCOMPtr<nsIURI> rpURI;
10132 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10133 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
10134 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
10135 if (originalResultPrincipalURI &&
10136 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
10137 // Unconditionally override, we want the replay to be equal to what has
10138 // been captured.
10139 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
10142 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
10143 // The LOAD_REPLACE flag and its handling here will be removed as part
10144 // of bug 1319110. For now preserve its restoration here to not break
10145 // any code expecting it being set specially on redirected channels.
10146 // If the flag has originally been set to change result of
10147 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10148 // any harm.
10149 uint32_t loadFlags;
10150 aRv = channel->GetLoadFlags(&loadFlags);
10151 NS_ENSURE_SUCCESS(aRv, false);
10152 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10155 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10156 if (csp) {
10157 // Navigational requests that are same origin need to be upgraded in case
10158 // upgrade-insecure-requests is present. Please note that for document
10159 // navigations that bit is re-computed in case we encounter a server
10160 // side redirect so the navigation is not same-origin anymore.
10161 bool upgradeInsecureRequests = false;
10162 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10163 if (upgradeInsecureRequests) {
10164 // only upgrade if the navigation is same origin
10165 nsCOMPtr<nsIPrincipal> resultPrincipal;
10166 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10167 channel, getter_AddRefs(resultPrincipal));
10168 NS_ENSURE_SUCCESS(aRv, false);
10169 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10170 aLoadState->TriggeringPrincipal(), resultPrincipal)) {
10171 aLoadInfo->SetUpgradeInsecureRequests(true);
10175 // For document loads we store the CSP that potentially needs to
10176 // be inherited by the new document, e.g. in case we are loading
10177 // an opaque origin like a data: URI. The actual inheritance
10178 // check happens within Document::InitCSP().
10179 // Please create an actual copy of the CSP (do not share the same
10180 // reference) otherwise a Meta CSP of an opaque origin will
10181 // incorrectly be propagated to the embedding document.
10182 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
10183 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
10184 aLoadInfo->SetCSPToInherit(cspToInherit);
10187 channel.forget(aChannel);
10188 return true;
10191 bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
10192 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
10193 return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
10194 (aPrincipalToInherit == GetInheritedPrincipal(false)) &&
10195 (!mContentViewer || !mContentViewer->GetDocument() ||
10196 mContentViewer->GetDocument()->IsInitialDocument());
10199 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
10200 Maybe<uint32_t> aCacheKey,
10201 nsIRequest** aRequest) {
10202 // Double-check that we're still around to load this URI.
10203 if (mIsBeingDestroyed) {
10204 // Return NS_OK despite not doing anything to avoid throwing exceptions
10205 // from nsLocation::SetHref if the unload handler of the existing page
10206 // tears us down.
10207 return NS_OK;
10210 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
10211 if (NS_WARN_IF(!uriLoader)) {
10212 return NS_ERROR_UNEXPECTED;
10215 // Persist and sync layout history state before we load a new uri, as this
10216 // might be our last chance to do so, in the content process.
10217 PersistLayoutHistoryState();
10218 SynchronizeLayoutHistoryState();
10220 nsresult rv;
10221 nsContentPolicyType contentPolicyType = DetermineContentType();
10223 if (IsSubframe()) {
10224 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10225 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10226 "DoURILoad thinks this is a frame and InternalLoad does not");
10228 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10229 // Only allow URLs able to return data in iframes.
10230 if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) {
10231 // The context to check user-interaction with for the purposes of
10232 // popup-blocking.
10234 // We generally want to check the context that initiated the navigation.
10235 WindowContext* sourceWindowContext = [&] {
10236 const MaybeDiscardedBrowsingContext& sourceBC =
10237 aLoadState->SourceBrowsingContext();
10238 if (!sourceBC.IsNullOrDiscarded()) {
10239 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
10240 return wc;
10243 return mBrowsingContext->GetParentWindowContext();
10244 }();
10246 MOZ_ASSERT(sourceWindowContext);
10247 // FIXME: We can't check user-interaction against an OOP window. This is
10248 // the next best thing we can really do. The load state keeps whether
10249 // the navigation had a user interaction in process
10250 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10251 // consume it, which we want to prevent popup-spamming from the same
10252 // click event.
10253 WindowContext* context =
10254 sourceWindowContext->IsInProcess()
10255 ? sourceWindowContext
10256 : mBrowsingContext->GetCurrentWindowContext();
10257 const bool popupBlocked = [&] {
10258 const bool active = mBrowsingContext->IsActive();
10260 // For same-origin-with-top windows, we grant a single free popup
10261 // without user activation, see bug 1680721.
10263 // We consume the flag now even if there's no user activation.
10264 const bool hasFreePass = [&] {
10265 if (!active ||
10266 !(context->IsInProcess() && context->SameOriginWithTop())) {
10267 return false;
10269 nsGlobalWindowInner* win =
10270 context->TopWindowContext()->GetInnerWindow();
10271 return win && win->TryOpenExternalProtocolIframe();
10272 }();
10274 if (context->IsInProcess() &&
10275 context->ConsumeTransientUserGestureActivation()) {
10276 // If the user has interacted with the page, consume it.
10277 return false;
10280 // TODO(emilio): Can we remove this check? It seems like what prompted
10281 // this code (bug 1514547) should be covered by transient user
10282 // activation, see bug 1514547.
10283 if (active &&
10284 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10285 return false;
10288 if (sourceWindowContext->CanShowPopup()) {
10289 return false;
10292 if (hasFreePass) {
10293 return false;
10296 return true;
10297 }();
10299 // No error must be returned when iframes are blocked.
10300 if (popupBlocked) {
10301 nsAutoString message;
10302 nsresult rv = nsContentUtils::GetLocalizedString(
10303 nsContentUtils::eDOM_PROPERTIES,
10304 "ExternalProtocolFrameBlockedNoUserActivation", message);
10305 if (NS_SUCCEEDED(rv)) {
10306 nsContentUtils::ReportToConsoleByWindowID(
10307 message, nsIScriptError::warningFlag, "DOM"_ns,
10308 context->InnerWindowId());
10310 return NS_OK;
10315 // Only allow view-source scheme in top-level docshells. view-source is
10316 // the only scheme to which this applies at the moment due to potential
10317 // timing attacks to read data from cross-origin iframes. If this widens
10318 // we should add a protocol flag for whether the scheme is allowed in
10319 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10320 nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
10321 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10322 while (nestedURI) {
10323 // view-source should always be an nsINestedURI, loop and check the
10324 // scheme on this and all inner URIs that are also nested URIs.
10325 if (SchemeIsViewSource(tempURI)) {
10326 return NS_ERROR_UNKNOWN_PROTOCOL;
10328 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10329 nestedURI = do_QueryInterface(tempURI);
10331 } else {
10332 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10333 "DoURILoad thinks this is a document and InternalLoad does not");
10336 // We want to inherit aLoadState->PrincipalToInherit() when:
10337 // 1. ChannelShouldInheritPrincipal returns true.
10338 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10339 // configured as unique opaque origin.
10340 bool inheritPrincipal = false;
10342 if (aLoadState->PrincipalToInherit()) {
10343 bool isSrcdoc =
10344 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
10345 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10346 aLoadState->PrincipalToInherit(), aLoadState->URI(),
10347 true, // aInheritForAboutBlank
10348 isSrcdoc);
10350 inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
10353 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
10354 const bool isAboutBlankLoadOntoInitialAboutBlank =
10355 IsAboutBlankLoadOntoInitialAboutBlank(aLoadState->URI(), inheritPrincipal,
10356 aLoadState->PrincipalToInherit());
10358 // FIXME We still have a ton of codepaths that don't pass through
10359 // DocumentLoadListener, so probably need to create session history info
10360 // in more places.
10361 if (aLoadState->GetLoadingSessionHistoryInfo()) {
10362 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
10363 } else if (isAboutBlankLoadOntoInitialAboutBlank &&
10364 mozilla::SessionHistoryInParent()) {
10365 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
10366 // loads have it, and later history behavior depends on it existing.
10367 UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
10368 aLoadState->URI(), aLoadState->TriggeringPrincipal(),
10369 aLoadState->PrincipalToInherit(),
10370 aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
10371 mContentTypeHint);
10372 mozilla::dom::LoadingSessionHistoryInfo info(*entry);
10373 SetLoadingSessionHistoryInfo(info, true);
10376 // open a channel for the url
10378 // If we have a pending channel, use the channel we've already created here.
10379 // We don't need to set up load flags for our channel, as it has already been
10380 // created.
10382 if (nsCOMPtr<nsIChannel> channel =
10383 aLoadState->GetPendingRedirectedChannel()) {
10384 // If we have a request outparameter, shove our channel into it.
10385 if (aRequest) {
10386 nsCOMPtr<nsIRequest> outRequest = channel;
10387 outRequest.forget(aRequest);
10390 return OpenRedirectedChannel(aLoadState);
10393 // There are two cases we care about:
10394 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10395 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10396 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10397 // element for the load. loadingPrincipal is the NodePrincipal of the
10398 // frame element.
10399 nsCOMPtr<nsINode> loadingNode;
10400 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10401 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10402 nsCOMPtr<nsISupports> topLevelLoadingContext;
10404 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10405 loadingNode = nullptr;
10406 loadingPrincipal = nullptr;
10407 loadingWindow = mScriptGlobal;
10408 if (XRE_IsContentProcess()) {
10409 // In e10s the child process doesn't have access to the element that
10410 // contains the browsing context (because that element is in the chrome
10411 // process).
10412 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
10413 topLevelLoadingContext = ToSupports(browserChild);
10414 } else {
10415 // This is for loading non-e10s tabs and toplevel windows of various
10416 // sorts.
10417 // For the toplevel window cases, requestingElement will be null.
10418 nsCOMPtr<Element> requestingElement =
10419 loadingWindow->GetFrameElementInternal();
10420 topLevelLoadingContext = requestingElement;
10422 } else {
10423 loadingWindow = nullptr;
10424 loadingNode = mScriptGlobal->GetFrameElementInternal();
10425 if (loadingNode) {
10426 // If we have a loading node, then use that as our loadingPrincipal.
10427 loadingPrincipal = loadingNode->NodePrincipal();
10428 #ifdef DEBUG
10429 // Get the docshell type for requestingElement.
10430 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
10431 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10432 // requestingElement docshell type = current docshell type.
10433 MOZ_ASSERT(
10434 mItemType == elementDocShell->ItemType(),
10435 "subframes should have the same docshell type as their parent");
10436 #endif
10437 } else {
10438 if (mIsBeingDestroyed) {
10439 // If this isn't a top-level load and mScriptGlobal's frame element is
10440 // null, then the element got removed from the DOM while we were trying
10441 // to load this resource. This docshell is scheduled for destruction
10442 // already, so bail out here.
10443 return NS_OK;
10445 // If we are not being destroyed and we do not have access to the loading
10446 // node, then we are a remote subframe. Set the loading principal
10447 // to be a null principal and then set it correctly in the parent.
10448 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
10452 if (!aLoadState->TriggeringPrincipal()) {
10453 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10454 return NS_ERROR_FAILURE;
10457 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
10458 nsSecurityFlags securityFlags =
10459 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
10461 if (mLoadType == LOAD_ERROR_PAGE) {
10462 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10465 if (inheritPrincipal) {
10466 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10469 // Must never have a parent for TYPE_DOCUMENT loads
10470 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10471 !mBrowsingContext->GetParent());
10472 // Subdocuments must have a parent
10473 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
10474 mBrowsingContext->GetParent());
10475 mBrowsingContext->SetTriggeringAndInheritPrincipals(
10476 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
10477 aLoadState->GetLoadIdentifier());
10478 RefPtr<LoadInfo> loadInfo =
10479 (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10480 ? new LoadInfo(loadingWindow, aLoadState->URI(),
10481 aLoadState->TriggeringPrincipal(),
10482 topLevelLoadingContext, securityFlags, sandboxFlags)
10483 : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
10484 loadingNode, securityFlags, contentPolicyType,
10485 Maybe<mozilla::dom::ClientInfo>(),
10486 Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
10487 sandboxFlags);
10488 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
10490 if (isAboutBlankLoadOntoInitialAboutBlank) {
10491 // Match the DocumentChannel case where the default for third-partiness
10492 // differs from the default in LoadInfo construction here.
10493 // toolkit/components/antitracking/test/browser/browser_aboutblank.js
10494 // fails without this.
10495 BrowsingContext* top = mBrowsingContext->Top();
10496 if (top == mBrowsingContext) {
10497 // If we're at the top, this must be a window.open()ed
10498 // window, and we can't be third-party relative to ourselves.
10499 loadInfo->SetIsThirdPartyContextToTopWindow(false);
10500 } else {
10501 if (Document* topDoc = top->GetDocument()) {
10502 bool thirdParty = false;
10503 mozilla::Unused << topDoc->GetPrincipal()->IsThirdPartyPrincipal(
10504 aLoadState->PrincipalToInherit(), &thirdParty);
10505 loadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
10506 } else {
10507 // If top is in a different process, we have to be third-party relative
10508 // to it.
10509 loadInfo->SetIsThirdPartyContextToTopWindow(true);
10514 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess() &&
10515 context->HasValidTransientUserGestureActivation()) {
10516 aLoadState->SetHasValidUserGestureActivation(true);
10519 // in case this docshell load was triggered by a valid transient user gesture,
10520 // or also the load originates from external, then we pass that information on
10521 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10522 if (aLoadState->HasValidUserGestureActivation() ||
10523 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
10524 loadInfo->SetHasValidUserGestureActivation(true);
10526 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
10527 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
10529 uint32_t cacheKey = 0;
10530 if (aCacheKey) {
10531 cacheKey = *aCacheKey;
10532 } else if (mozilla::SessionHistoryInParent()) {
10533 if (mLoadingEntry) {
10534 cacheKey = mLoadingEntry->mInfo.GetCacheKey();
10535 } else if (mActiveEntry) { // for reload cases
10536 cacheKey = mActiveEntry->GetCacheKey();
10538 } else {
10539 if (mLSHE) {
10540 cacheKey = mLSHE->GetCacheKey();
10541 } else if (mOSHE) { // for reload cases
10542 cacheKey = mOSHE->GetCacheKey();
10546 bool uriModified;
10547 if (mLSHE || mLoadingEntry) {
10548 if (mLoadingEntry) {
10549 uriModified = mLoadingEntry->mInfo.GetURIWasModified();
10550 } else {
10551 uriModified = mLSHE->GetURIWasModified();
10553 } else {
10554 uriModified = false;
10557 bool isXFOError = false;
10558 if (mFailedChannel) {
10559 nsresult status;
10560 mFailedChannel->GetStatus(&status);
10561 isXFOError = status == NS_ERROR_XFO_VIOLATION;
10564 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
10565 mBrowsingContext, Some(uriModified), Some(isXFOError));
10567 nsCOMPtr<nsIChannel> channel;
10568 if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
10569 !isAboutBlankLoadOntoInitialAboutBlank) {
10570 channel = DocumentChannel::CreateForDocument(aLoadState, loadInfo,
10571 loadFlags, this, cacheKey,
10572 uriModified, isXFOError);
10573 MOZ_ASSERT(channel);
10575 // Disable keyword fixup when using DocumentChannel, since
10576 // DocumentLoadListener will handle this for us (in the parent process).
10577 mAllowKeywordFixup = false;
10578 } else if (!CreateAndConfigureRealChannelForLoadState(
10579 mBrowsingContext, aLoadState, loadInfo, this, this,
10580 GetOriginAttributes(), loadFlags, cacheKey, rv,
10581 getter_AddRefs(channel))) {
10582 return rv;
10585 // Make sure to give the caller a channel if we managed to create one
10586 // This is important for correct error page/session history interaction
10587 if (aRequest) {
10588 NS_ADDREF(*aRequest = channel);
10591 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10592 if (csp) {
10593 // Check CSP navigate-to
10594 bool allowsNavigateTo = false;
10595 rv = csp->GetAllowsNavigateTo(aLoadState->URI(),
10596 aLoadState->IsFormSubmission(),
10597 false, /* aWasRedirected */
10598 false, /* aEnforceWhitelist */
10599 &allowsNavigateTo);
10600 NS_ENSURE_SUCCESS(rv, rv);
10602 if (!allowsNavigateTo) {
10603 return NS_ERROR_CSP_NAVIGATE_TO_VIOLATION;
10607 const nsACString& typeHint = aLoadState->TypeHint();
10608 if (!typeHint.IsVoid()) {
10609 mContentTypeHint = typeHint;
10610 } else {
10611 mContentTypeHint.Truncate();
10614 // Load attributes depend on load type...
10615 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10616 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10617 // only want to force cache load for this channel, not the whole
10618 // loadGroup.
10619 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10620 if (cachingChannel) {
10621 cachingChannel->SetAllowStaleCacheContent(true);
10625 uint32_t openFlags =
10626 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
10627 return OpenInitializedChannel(channel, uriLoader, openFlags);
10630 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10631 const char* aFromRawSegment,
10632 uint32_t aToOffset, uint32_t aCount,
10633 uint32_t* aWriteCount) {
10634 // aFromSegment now contains aCount bytes of data.
10636 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10637 buf->Append(aFromRawSegment, aCount);
10639 // Indicate that we have consumed all of aFromSegment
10640 *aWriteCount = aCount;
10641 return NS_OK;
10644 /* static */ nsresult nsDocShell::AddHeadersToChannel(
10645 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
10646 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10647 NS_ENSURE_STATE(httpChannel);
10649 uint32_t numRead;
10650 nsAutoCString headersString;
10651 nsresult rv = aHeadersData->ReadSegments(
10652 AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10653 NS_ENSURE_SUCCESS(rv, rv);
10655 // used during the manipulation of the String from the InputStream
10656 nsAutoCString headerName;
10657 nsAutoCString headerValue;
10658 int32_t crlf;
10659 int32_t colon;
10662 // Iterate over the headersString: for each "\r\n" delimited chunk,
10663 // add the value as a header to the nsIHttpChannel
10666 static const char kWhitespace[] = "\b\t\r\n ";
10667 while (true) {
10668 crlf = headersString.Find("\r\n");
10669 if (crlf == kNotFound) {
10670 return NS_OK;
10673 const nsACString& oneHeader = StringHead(headersString, crlf);
10675 colon = oneHeader.FindChar(':');
10676 if (colon == kNotFound) {
10677 return NS_ERROR_UNEXPECTED;
10680 headerName = StringHead(oneHeader, colon);
10681 headerValue = Substring(oneHeader, colon + 1);
10683 headerName.Trim(kWhitespace);
10684 headerValue.Trim(kWhitespace);
10686 headersString.Cut(0, crlf + 2);
10689 // FINALLY: we can set the header!
10692 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10693 NS_ENSURE_SUCCESS(rv, rv);
10696 MOZ_ASSERT_UNREACHABLE("oops");
10697 return NS_ERROR_UNEXPECTED;
10700 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10701 BrowsingContext* aBrowsingContext, uint32_t aLoadType) {
10702 MOZ_ASSERT(aBrowsingContext);
10704 uint32_t openFlags = 0;
10705 if (aLoadType == LOAD_LINK) {
10706 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10708 if (!aBrowsingContext->GetAllowContentRetargeting()) {
10709 openFlags |= nsIURILoader::DONT_RETARGET;
10712 return openFlags;
10715 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
10716 nsIURILoader* aURILoader,
10717 uint32_t aOpenFlags) {
10718 nsresult rv = NS_OK;
10720 // If anything fails here, make sure to clear our initial ClientSource.
10721 auto cleanupInitialClient =
10722 MakeScopeExit([&] { mInitialClientSource.reset(); });
10724 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10725 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10727 MaybeCreateInitialClientSource();
10729 // Let the client channel helper know if we are using DocumentChannel,
10730 // since redirects get handled in the parent process in that case.
10731 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
10732 if (docChannel && XRE_IsContentProcess()) {
10733 // Tell the content process nsDocumentOpenInfo to not try to do
10734 // any sort of targeting.
10735 aOpenFlags |= nsIURILoader::DONT_RETARGET;
10738 // Since we are loading a document we need to make sure the proper reserved
10739 // and initial client data is stored on the nsILoadInfo. The
10740 // ClientChannelHelper does this and ensures that it is propagated properly
10741 // on redirects. We pass no reserved client here so that the helper will
10742 // create the reserved ClientSource if necessary.
10743 Maybe<ClientInfo> noReservedClient;
10744 if (docChannel) {
10745 // When using DocumentChannel, all redirect handling is done in the parent,
10746 // so we just need the child variant to watch for the internal redirect
10747 // to the final channel.
10748 rv = AddClientChannelHelperInChild(
10749 aChannel, win->EventTargetFor(TaskCategory::Other));
10750 docChannel->SetInitialClientInfo(GetInitialClientInfo());
10751 } else {
10752 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
10753 GetInitialClientInfo(),
10754 win->EventTargetFor(TaskCategory::Other));
10756 NS_ENSURE_SUCCESS(rv, rv);
10758 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
10759 NS_ENSURE_SUCCESS(rv, rv);
10761 // We're about to load a new page and it may take time before necko
10762 // gives back any data, so main thread might have a chance to process a
10763 // collector slice
10764 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
10766 // Success. Keep the initial ClientSource if it exists.
10767 cleanupInitialClient.release();
10769 return NS_OK;
10772 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
10773 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
10774 MOZ_ASSERT(channel);
10776 // If anything fails here, make sure to clear our initial ClientSource.
10777 auto cleanupInitialClient =
10778 MakeScopeExit([&] { mInitialClientSource.reset(); });
10780 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10781 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10783 MaybeCreateInitialClientSource();
10785 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
10787 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
10788 if (loadInfo->GetExternalContentPolicyType() ==
10789 ExtContentPolicy::TYPE_DOCUMENT) {
10790 li->UpdateBrowsingContextID(mBrowsingContext->Id());
10791 } else if (loadInfo->GetExternalContentPolicyType() ==
10792 ExtContentPolicy::TYPE_SUBDOCUMENT) {
10793 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
10795 // TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
10797 // If we did a process switch, then we should have an existing allocated
10798 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10799 CreateReservedSourceIfNeeded(channel,
10800 win->EventTargetFor(TaskCategory::Other));
10802 RefPtr<nsDocumentOpenInfo> loader =
10803 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10804 channel->SetLoadGroup(mLoadGroup);
10806 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10808 nsresult rv = NS_OK;
10809 if (XRE_IsParentProcess()) {
10810 // If we're in the parent, the we don't have an nsIChildChannel, just
10811 // the original channel, which is already open in this process.
10813 // DocumentLoadListener expects to get an nsIParentChannel, so
10814 // we create a wrapper around the channel and nsIStreamListener
10815 // that forwards functionality as needed, and then we register
10816 // it under the provided identifier.
10817 RefPtr<ParentChannelWrapper> wrapper =
10818 new ParentChannelWrapper(channel, loader);
10819 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10821 mLoadGroup->AddRequest(channel, nullptr);
10822 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10823 do_QueryInterface(channel)) {
10824 // Our channel was redirected from another process, so doesn't need to
10825 // be opened again. However, it does need its listener hooked up
10826 // correctly.
10827 rv = childChannel->CompleteRedirectSetup(loader);
10828 } else {
10829 // It's possible for the redirected channel to not implement
10830 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10831 // can just open the local instance and it will work.
10832 rv = channel->AsyncOpen(loader);
10834 if (rv == NS_ERROR_NO_CONTENT) {
10835 return NS_OK;
10837 NS_ENSURE_SUCCESS(rv, rv);
10839 // Success. Keep the initial ClientSource if it exists.
10840 cleanupInitialClient.release();
10841 return NS_OK;
10844 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10845 nsACString& aNewHash, uint32_t aLoadType) {
10846 if (!mCurrentURI) {
10847 return NS_OK;
10850 RefPtr<PresShell> presShell = GetPresShell();
10851 if (!presShell) {
10852 // If we failed to get the shell, or if there is no shell,
10853 // nothing left to do here.
10854 return NS_OK;
10857 nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
10858 if (rootScroll) {
10859 rootScroll->ClearDidHistoryRestore();
10862 // If we have no new anchor, we do not want to scroll, unless there is a
10863 // current anchor and we are doing a history load. So return if we have no
10864 // new anchor, and there is no current anchor or the load is not a history
10865 // load.
10866 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10867 return NS_OK;
10870 // Both the new and current URIs refer to the same page. We can now
10871 // browse to the hash stored in the new URI.
10873 if (!aNewHash.IsEmpty()) {
10874 // anchor is there, but if it's a load from history,
10875 // we don't have any anchor jumping to do
10876 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10878 // We assume that the bytes are in UTF-8, as it says in the
10879 // spec:
10880 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
10882 // We try the UTF-8 string first, and then try the document's
10883 // charset (see below). If the string is not UTF-8,
10884 // conversion will fail and give us an empty Unicode string.
10885 // In that case, we should just fall through to using the
10886 // page's charset.
10887 nsresult rv = NS_ERROR_FAILURE;
10888 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10889 if (!uStr.IsEmpty()) {
10890 rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
10893 if (NS_FAILED(rv)) {
10894 char* str = ToNewCString(aNewHash, mozilla::fallible);
10895 if (!str) {
10896 return NS_ERROR_OUT_OF_MEMORY;
10898 nsUnescape(str);
10899 NS_ConvertUTF8toUTF16 utf16Str(str);
10900 if (!utf16Str.IsEmpty()) {
10901 rv = presShell->GoToAnchor(utf16Str, scroll,
10902 ScrollFlags::ScrollSmoothAuto);
10904 free(str);
10907 // Above will fail if the anchor name is not UTF-8. Need to
10908 // convert from document charset to unicode.
10909 if (NS_FAILED(rv)) {
10910 // Get a document charset
10911 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
10912 Document* doc = mContentViewer->GetDocument();
10913 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
10914 nsAutoCString charset;
10915 doc->GetDocumentCharacterSet()->Name(charset);
10917 nsCOMPtr<nsITextToSubURI> textToSubURI =
10918 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10919 NS_ENSURE_SUCCESS(rv, rv);
10921 // Unescape and convert to unicode
10922 nsAutoString uStr;
10924 rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
10925 NS_ENSURE_SUCCESS(rv, rv);
10927 // Ignore return value of GoToAnchor, since it will return an error
10928 // if there is no such anchor in the document, which is actually a
10929 // success condition for us (we want to update the session history
10930 // with the new URI no matter whether we actually scrolled
10931 // somewhere).
10933 // When aNewHash contains "%00", unescaped string may be empty.
10934 // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10935 presShell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
10936 ScrollFlags::ScrollSmoothAuto);
10938 } else {
10939 // Tell the shell it's at an anchor, without scrolling.
10940 presShell->GoToAnchor(u""_ns, false);
10942 // An empty anchor was found, but if it's a load from history,
10943 // we don't have to jump to the top of the page. Scrollbar
10944 // position will be restored by the caller, based on positions
10945 // stored in session history.
10946 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
10947 return NS_OK;
10949 // An empty anchor. Scroll to the top of the page. Ignore the
10950 // return value; failure to scroll here (e.g. if there is no
10951 // root scrollframe) is not grounds for canceling the load!
10952 SetCurScrollPosEx(0, 0);
10955 return NS_OK;
10958 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10959 nsIPrincipal* aTriggeringPrincipal,
10960 nsIPrincipal* aPrincipalToInherit,
10961 nsIPrincipal* aPartitionedPrincipalToInherit,
10962 nsIContentSecurityPolicy* aCsp,
10963 bool aFireOnLocationChange, bool aAddToGlobalHistory,
10964 bool aCloneSHChildren) {
10965 MOZ_ASSERT(aURI, "uri is null");
10966 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10968 MOZ_ASSERT(!aPrincipalToInherit ||
10969 (aPrincipalToInherit && aTriggeringPrincipal));
10971 #if defined(DEBUG)
10972 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10973 nsAutoCString chanName;
10974 if (aChannel) {
10975 aChannel->GetName(chanName);
10976 } else {
10977 chanName.AssignLiteral("<no channel>");
10980 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10981 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10982 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10984 #endif
10986 bool equalUri = false;
10988 // Get the post data and the HTTP response code from the channel.
10989 uint32_t responseStatus = 0;
10990 nsCOMPtr<nsIInputStream> inputStream;
10991 if (aChannel) {
10992 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10994 // Check if the HTTPChannel is hiding under a multiPartChannel
10995 if (!httpChannel) {
10996 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10999 if (httpChannel) {
11000 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11001 if (uploadChannel) {
11002 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11005 // If the response status indicates an error, unlink this session
11006 // history entry from any entries sharing its document.
11007 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
11008 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
11009 mLSHE->AbandonBFCacheEntry();
11010 // FIXME Do the same for mLoadingEntry
11015 // Determine if this type of load should update history.
11016 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
11018 // We don't update session history on reload unless we're loading
11019 // an iframe in shift-reload case.
11020 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
11022 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
11023 // root browsing context.
11024 // FIXME If session history in the parent is enabled then we only do this if
11025 // the session history object is in process, otherwise we can't really
11026 // use the mLSHE anyway. Once session history is only stored in the
11027 // parent then this code will probably be removed anyway.
11028 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11029 if (!rootSH) {
11030 updateSHistory = false;
11031 updateGHistory = false; // XXX Why global history too?
11034 // Check if the url to be loaded is the same as the one already loaded.
11035 if (mCurrentURI) {
11036 aURI->Equals(mCurrentURI, &equalUri);
11039 #ifdef DEBUG
11040 bool shAvailable = (rootSH != nullptr);
11042 // XXX This log message is almost useless because |updateSHistory|
11043 // and |updateGHistory| are not correct at this point.
11045 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11046 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
11047 " equalURI=%i\n",
11048 shAvailable, updateSHistory, updateGHistory, equalUri));
11049 #endif
11051 /* If the url to be loaded is the same as the one already there,
11052 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
11053 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
11054 * AddToSessionHistory() won't mess with the current SHEntry and
11055 * if this page has any frame children, it also will be handled
11056 * properly. see bug 83684
11058 * NB: If mOSHE is null but we have a current URI, then it probably
11059 * means that we must be at the transient about:blank content viewer;
11060 * we should let the normal load continue, since there's nothing to
11061 * replace. Sometimes this happens after a session restore (eg process
11062 * switch) and mCurrentURI is not about:blank; we assume we can let the load
11063 * continue (Bug 1301399).
11065 * XXX Hopefully changing the loadType at this time will not hurt
11066 * anywhere. The other way to take care of sequentially repeating
11067 * frameset pages is to add new methods to nsIDocShellTreeItem.
11068 * Hopefully I don't have to do that.
11070 if (equalUri &&
11071 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
11072 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
11073 mLoadType == LOAD_STOP_CONTENT) &&
11074 !inputStream) {
11075 mLoadType = LOAD_NORMAL_REPLACE;
11078 // If this is a refresh to the currently loaded url, we don't
11079 // have to update session or global history.
11080 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
11081 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
11084 /* If the user pressed shift-reload, cache will create a new cache key
11085 * for the page. Save the new cacheKey in Session History.
11086 * see bug 90098
11088 if (aChannel && IsForceReloadType(mLoadType)) {
11089 MOZ_ASSERT(!updateSHistory || IsSubframe(),
11090 "We shouldn't be updating session history for forced"
11091 " reloads unless we're in a newly created iframe!");
11093 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
11094 uint32_t cacheKey = 0;
11095 // Get the Cache Key and store it in SH.
11096 if (cacheChannel) {
11097 cacheChannel->GetCacheKey(&cacheKey);
11099 // If we already have a loading history entry, store the new cache key
11100 // in it. Otherwise, since we're doing a reload and won't be updating
11101 // our history entry, store the cache key in our current history entry.
11102 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
11104 if (!mozilla::SessionHistoryInParent()) {
11105 // Since we're force-reloading, clear all the sub frame history.
11106 ClearFrameHistory(mLSHE);
11107 ClearFrameHistory(mOSHE);
11111 if (!mozilla::SessionHistoryInParent()) {
11112 // Clear subframe history on refresh.
11113 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
11114 // this case. One should re-validate after bug 1331865 fixed.
11115 if (mLoadType == LOAD_REFRESH) {
11116 ClearFrameHistory(mLSHE);
11117 ClearFrameHistory(mOSHE);
11120 if (updateSHistory) {
11121 // Update session history if necessary...
11122 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11123 /* This is a fresh page getting loaded for the first time
11124 *.Create a Entry for it and add it to SH, if this is the
11125 * rootDocShell
11127 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11128 aPrincipalToInherit,
11129 aPartitionedPrincipalToInherit, aCsp,
11130 aCloneSHChildren, getter_AddRefs(mLSHE));
11132 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11133 // Even if we don't add anything to SHistory, ensure the current index
11134 // points to the same SHEntry as our mLSHE.
11136 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11137 mLSHE);
11141 // If this is a POST request, we do not want to include this in global
11142 // history.
11143 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11144 !net::ChannelIsPost(aChannel)) {
11145 nsCOMPtr<nsIURI> previousURI;
11146 uint32_t previousFlags = 0;
11148 if (mLoadType & LOAD_CMD_RELOAD) {
11149 // On a reload request, we don't set redirecting flags.
11150 previousURI = aURI;
11151 } else {
11152 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11155 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11158 // If this was a history load or a refresh, or it was a history load but
11159 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11160 // in session history.
11161 if (!mozilla::SessionHistoryInParent() && rootSH &&
11162 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11163 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11164 mPreviousEntryIndex = rootSH->Index();
11165 if (!mozilla::SessionHistoryInParent()) {
11166 rootSH->LegacySHistory()->UpdateIndex();
11168 mLoadedEntryIndex = rootSH->Index();
11169 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11170 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11171 mLoadedEntryIndex));
11174 // aCloneSHChildren exactly means "we are not loading a new document".
11175 uint32_t locationFlags =
11176 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11178 bool onLocationChangeNeeded =
11179 SetCurrentURI(aURI, aChannel, aFireOnLocationChange,
11180 /* aIsInitialAboutBlank */ false, locationFlags);
11181 // Make sure to store the referrer from the channel, if any
11182 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11183 if (httpChannel) {
11184 mReferrerInfo = httpChannel->GetReferrerInfo();
11186 return onLocationChangeNeeded;
11189 Maybe<Wireframe> nsDocShell::GetWireframe() {
11190 const bool collectWireFrame =
11191 mozilla::SessionHistoryInParent() &&
11192 StaticPrefs::browser_history_collectWireframes() &&
11193 mBrowsingContext->IsTopContent() && mActiveEntry;
11195 if (!collectWireFrame) {
11196 return Nothing();
11199 RefPtr<Document> doc = mContentViewer->GetDocument();
11200 Nullable<Wireframe> wireframe;
11201 doc->GetWireframeWithoutFlushing(false, wireframe);
11202 if (wireframe.IsNull()) {
11203 return Nothing();
11205 return Some(wireframe.Value());
11208 bool nsDocShell::CollectWireframe() {
11209 Maybe<Wireframe> wireframe = GetWireframe();
11210 if (wireframe.isNothing()) {
11211 return false;
11214 if (XRE_IsParentProcess()) {
11215 SessionHistoryEntry* entry =
11216 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11217 if (entry) {
11218 entry->SetWireframe(wireframe);
11220 } else {
11221 mozilla::Unused
11222 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11223 mBrowsingContext, wireframe.ref());
11226 return true;
11229 //*****************************************************************************
11230 // nsDocShell: Session History
11231 //*****************************************************************************
11233 NS_IMETHODIMP
11234 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11235 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11236 MOZ_LOG(gSHLog, LogLevel::Debug,
11237 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11238 NS_ConvertUTF16toUTF8(aTitle).get(),
11239 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11240 // Implements History.pushState and History.replaceState
11242 // Here's what we do, roughly in the order specified by HTML5. The specific
11243 // steps we are executing are at
11244 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11245 // and
11246 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11247 // This function basically implements #dom-history-pushstate and
11248 // UpdateURLAndHistory implements #url-and-history-update-steps.
11250 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11251 // step 5.
11252 // B. If the third argument is present, #dom-history-pushstate step 7.
11253 // 7.1. Resolve the url, relative to our document.
11254 // 7.2. If (a) fails, raise a SECURITY_ERR
11255 // 7.4. Compare the resulting absolute URL to the document's address. If
11256 // any part of the URLs difer other than the <path>, <query>, and
11257 // <fragment> components, raise a SECURITY_ERR and abort.
11258 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11259 // Remove from the session history all entries after the current entry,
11260 // as we would after a regular navigation, and save the current
11261 // entry's scroll position (bug 590573).
11262 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11263 // either add a state object entry to the session history after the
11264 // current entry with the following properties, or modify the current
11265 // session history entry to set
11266 // a. cloned data as the state object,
11267 // b. if the third argument was present, the absolute URL found in
11268 // step 2
11269 // Also clear the new history entry's POST data (see bug 580069).
11270 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11271 // replaceState), notify bfcache that we've navigated to a new page.
11272 // F. If the third argument is present, set the document's current address
11273 // to the absolute URL found in step B. This is
11274 // #url-and-history-update-steps step 4.
11276 // It's important that this function not run arbitrary scripts after step A
11277 // and before completing step E. For example, if a script called
11278 // history.back() before we completed step E, bfcache might destroy an
11279 // active content viewer. Since EvictOutOfRangeContentViewers at the end of
11280 // step E might run script, we can't just put a script blocker around the
11281 // critical section.
11283 // Note that we completely ignore the aTitle parameter.
11285 nsresult rv;
11287 // Don't clobber the load type of an existing network load.
11288 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11290 // pushState effectively becomes replaceState when we've started a network
11291 // load but haven't adopted its document yet. This mirrors what we do with
11292 // changes to the hash at this stage of the game.
11293 if (JustStartedNetworkLoad()) {
11294 aReplace = true;
11297 RefPtr<Document> document = GetDocument();
11298 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11300 // Step A: Serialize aData using structured clone.
11301 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11302 // step 5.
11303 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11305 // scContainer->Init might cause arbitrary JS to run, and this code might
11306 // navigate the page we're on, potentially to a different origin! (bug
11307 // 634834) To protect against this, we abort if our principal changes due
11308 // to the InitFromJSVal() call.
11310 RefPtr<Document> origDocument = GetDocument();
11311 if (!origDocument) {
11312 return NS_ERROR_DOM_SECURITY_ERR;
11314 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11316 scContainer = new nsStructuredCloneContainer();
11317 rv = scContainer->InitFromJSVal(aData, aCx);
11318 NS_ENSURE_SUCCESS(rv, rv);
11320 RefPtr<Document> newDocument = GetDocument();
11321 if (!newDocument) {
11322 return NS_ERROR_DOM_SECURITY_ERR;
11324 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11326 bool principalsEqual = false;
11327 origPrincipal->Equals(newPrincipal, &principalsEqual);
11328 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11331 // Check that the state object isn't too long.
11332 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11333 if (maxStateObjSize < 0) {
11334 maxStateObjSize = 0;
11337 uint64_t scSize;
11338 rv = scContainer->GetSerializedNBytes(&scSize);
11339 NS_ENSURE_SUCCESS(rv, rv);
11341 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11343 // Step B: Resolve aURL.
11344 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11345 // step 7.
11346 bool equalURIs = true;
11347 nsCOMPtr<nsIURI> currentURI;
11348 if (mCurrentURI) {
11349 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11350 } else {
11351 currentURI = mCurrentURI;
11353 nsCOMPtr<nsIURI> newURI;
11354 if (aURL.Length() == 0) {
11355 newURI = currentURI;
11356 } else {
11357 // 7.1: Resolve aURL relative to mURI
11359 nsIURI* docBaseURI = document->GetDocBaseURI();
11360 if (!docBaseURI) {
11361 return NS_ERROR_FAILURE;
11364 nsAutoCString spec;
11365 docBaseURI->GetSpec(spec);
11367 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11368 document->GetDocumentCharacterSet(), docBaseURI);
11370 // 7.2: If 2a fails, raise a SECURITY_ERR
11371 if (NS_FAILED(rv)) {
11372 return NS_ERROR_DOM_SECURITY_ERR;
11375 // 7.4 and 7.5: Same-origin check.
11376 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11377 // In addition to checking that the security manager says that
11378 // the new URI has the same origin as our current URI, we also
11379 // check that the two URIs have the same userpass. (The
11380 // security manager says that |http://foo.com| and
11381 // |http://me@foo.com| have the same origin.) currentURI
11382 // won't contain the password part of the userpass, so this
11383 // means that it's never valid to specify a password in a
11384 // pushState or replaceState URI.
11386 nsCOMPtr<nsIScriptSecurityManager> secMan =
11387 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11388 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11390 // It's very important that we check that newURI is of the same
11391 // origin as currentURI, not docBaseURI, because a page can
11392 // set docBaseURI arbitrarily to any domain.
11393 nsAutoCString currentUserPass, newUserPass;
11394 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11395 NS_ERROR_FAILURE);
11396 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11397 bool isPrivateWin =
11398 document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
11400 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11401 isPrivateWin)) ||
11402 !currentUserPass.Equals(newUserPass)) {
11403 return NS_ERROR_DOM_SECURITY_ERR;
11405 } else {
11406 // It's a file:// URI
11407 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11409 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11410 newURI, false, document->InnerWindowID()))) {
11411 return NS_ERROR_DOM_SECURITY_ERR;
11415 if (currentURI) {
11416 currentURI->Equals(newURI, &equalURIs);
11417 } else {
11418 equalURIs = false;
11421 } // end of same-origin check
11423 // Step 8: call "URL and history update steps"
11424 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11425 currentURI, equalURIs);
11426 NS_ENSURE_SUCCESS(rv, rv);
11428 return NS_OK;
11431 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11432 nsIStructuredCloneContainer* aData,
11433 const nsAString& aTitle, bool aReplace,
11434 nsIURI* aCurrentURI, bool aEqualURIs) {
11435 // Implements
11436 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11438 // If we have a pending title change, handle it before creating a new entry.
11439 aDocument->DoNotifyPossibleTitleChange();
11441 // Step 2, if aReplace is false: Create a new entry in the session
11442 // history. This will erase all SHEntries after the new entry and make this
11443 // entry the current one. This operation may modify mOSHE, which we need
11444 // later, so we keep a reference here.
11445 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11446 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11448 // If this push/replaceState changed the document's current URI and the new
11449 // URI differs from the old URI in more than the hash, or if the old
11450 // SHEntry's URI was modified in this way by a push/replaceState call
11451 // set URIWasModified to true for the current SHEntry (bug 669671).
11452 bool sameExceptHashes = true;
11453 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11454 bool uriWasModified;
11455 if (sameExceptHashes) {
11456 if (mozilla::SessionHistoryInParent()) {
11457 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11458 } else {
11459 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11461 } else {
11462 uriWasModified = true;
11465 mLoadType = LOAD_PUSHSTATE;
11467 nsCOMPtr<nsISHEntry> newSHEntry;
11468 if (!aReplace) {
11469 // Step 2.
11471 // Step 2.2, "Remove any tasks queued by the history traversal task
11472 // source that are associated with any Document objects in the
11473 // top-level browsing context's document family." This is very hard in
11474 // SessionHistoryInParent since we can't synchronously access the
11475 // pending navigations that are already sent to the parent. We can
11476 // abort any AsyncGo navigations that are waiting to be sent. If we
11477 // send a message to the parent, it would be processed after any
11478 // navigations previously sent. So long as we consider the "history
11479 // traversal task source" to be the list in this process we match the
11480 // spec. If we move the entire list to the parent, we can handle the
11481 // aborting of loads there, but we don't have a way to synchronously
11482 // remove entries as we do here for non-SHIP.
11483 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11484 if (shistory) {
11485 shistory->RemovePendingHistoryNavigations();
11488 nsPoint scrollPos = GetCurScrollPos();
11490 bool scrollRestorationIsManual;
11491 if (mozilla::SessionHistoryInParent()) {
11492 // FIXME Need to save the current scroll position on mActiveEntry.
11493 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11494 } else {
11495 // Save the current scroll position (bug 590573). Step 2.3.
11496 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11498 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11501 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11503 if (mozilla::SessionHistoryInParent()) {
11504 MOZ_LOG(gSHLog, LogLevel::Debug,
11505 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11506 nsString title(mActiveEntry->GetTitle());
11507 UpdateActiveEntry(false,
11508 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11509 /* aOriginalURI = */ nullptr,
11510 /* aReferrerInfo = */ nullptr,
11511 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11512 csp, title, scrollRestorationIsManual, aData,
11513 uriWasModified);
11514 } else {
11515 // Since we're not changing which page we have loaded, pass
11516 // true for aCloneChildren.
11517 nsresult rv = AddToSessionHistory(
11518 aNewURI, nullptr,
11519 aDocument->NodePrincipal(), // triggeringPrincipal
11520 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11521 NS_ENSURE_SUCCESS(rv, rv);
11523 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11525 // Session history entries created by pushState inherit scroll restoration
11526 // mode from the current entry.
11527 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11529 nsString title;
11530 mOSHE->GetTitle(title);
11532 // Set the new SHEntry's title (bug 655273).
11533 newSHEntry->SetTitle(title);
11535 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11536 // two entries correspond to the same document.
11537 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11538 NS_ERROR_FAILURE);
11540 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11541 // we'll just set mOSHE here.
11542 mOSHE = newSHEntry;
11544 } else if (mozilla::SessionHistoryInParent()) {
11545 MOZ_LOG(gSHLog, LogLevel::Debug,
11546 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11547 this, mActiveEntry.get()));
11548 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11549 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11550 // in our case. We could also set it to aNewURI, with the same result.
11551 // We don't use aTitle here, see bug 544535.
11552 nsString title;
11553 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11554 if (mActiveEntry) {
11555 title = mActiveEntry->GetTitle();
11556 referrerInfo = mActiveEntry->GetReferrerInfo();
11557 } else {
11558 referrerInfo = nullptr;
11560 UpdateActiveEntry(
11561 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11562 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
11563 aDocument->GetCsp(), title,
11564 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11565 uriWasModified);
11566 } else {
11567 // Step 3.
11568 newSHEntry = mOSHE;
11570 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11571 // Since we're not changing which page we have loaded, pass
11572 // true for aCloneChildren.
11573 if (!newSHEntry) {
11574 nsresult rv = AddToSessionHistory(
11575 aNewURI, nullptr,
11576 aDocument->NodePrincipal(), // triggeringPrincipal
11577 nullptr, nullptr, aDocument->GetCsp(), true,
11578 getter_AddRefs(newSHEntry));
11579 NS_ENSURE_SUCCESS(rv, rv);
11580 mOSHE = newSHEntry;
11583 newSHEntry->SetURI(aNewURI);
11584 newSHEntry->SetOriginalURI(aNewURI);
11585 // We replaced the URI of the entry, clear the unstripped URI as it
11586 // shouldn't be used for reloads anymore.
11587 newSHEntry->SetUnstrippedURI(nullptr);
11588 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11589 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11590 // in our case. We could also set it to aNewURI, with the same result.
11591 newSHEntry->SetResultPrincipalURI(nullptr);
11592 newSHEntry->SetLoadReplace(false);
11595 if (!mozilla::SessionHistoryInParent()) {
11596 // Step 2.4 and 3: Modify new/original session history entry and clear its
11597 // POST data, if there is any.
11598 newSHEntry->SetStateData(aData);
11599 newSHEntry->SetPostData(nullptr);
11601 newSHEntry->SetURIWasModified(uriWasModified);
11603 // Step E as described at the top of AddState: If aReplace is false,
11604 // indicating that we're doing a pushState rather than a replaceState,
11605 // notify bfcache that we've added a page to the history so it can evict
11606 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11607 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11608 // root session history if this call is coming from a document.open() in a
11609 // docshell subtree that disables session history.
11610 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11611 if (rootSH) {
11612 rootSH->LegacySHistory()->EvictContentViewersOrReplaceEntry(newSHEntry,
11613 aReplace);
11617 // Step 4: If the document's URI changed, update document's URI and update
11618 // global history.
11620 // We need to call FireOnLocationChange so that the browser's address bar
11621 // gets updated and the back button is enabled, but we only need to
11622 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11623 // since SetCurrentURI will call FireOnLocationChange for us.
11625 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11626 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11627 // notification is allowed only when we know docshell is not loading a new
11628 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11629 // FireOnLocationChange(...) breaks security UI.
11631 // If the docshell is shutting down, don't update the document URI, as we
11632 // can't load into a docshell that is being destroyed.
11633 if (!aEqualURIs && !mIsBeingDestroyed) {
11634 aDocument->SetDocumentURI(aNewURI);
11635 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11636 /* aIsInitialAboutBlank */ false,
11637 GetSameDocumentNavigationFlags(aNewURI));
11639 AddURIVisit(aNewURI, aCurrentURI, 0);
11641 // AddURIVisit doesn't set the title for the new URI in global history,
11642 // so do that here.
11643 UpdateGlobalHistoryTitle(aNewURI);
11645 // Inform the favicon service that our old favicon applies to this new
11646 // URI.
11647 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11648 } else {
11649 FireDummyOnLocationChange();
11651 aDocument->SetStateObject(aData);
11653 return NS_OK;
11656 NS_IMETHODIMP
11657 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11658 if (mozilla::SessionHistoryInParent()) {
11659 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11660 return NS_OK;
11663 *aIsManual = false;
11664 if (mOSHE) {
11665 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11668 return NS_OK;
11671 NS_IMETHODIMP
11672 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11673 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11675 return NS_OK;
11678 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11679 nsISHEntry* aSHEntry, bool aIsManual) {
11680 if (aSHEntry) {
11681 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11684 if (mActiveEntry && mBrowsingContext) {
11685 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11686 if (XRE_IsParentProcess()) {
11687 SessionHistoryEntry* entry =
11688 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11689 if (entry) {
11690 entry->SetScrollRestorationIsManual(aIsManual);
11692 } else {
11693 mozilla::Unused << ContentChild::GetSingleton()
11694 ->SendSessionHistoryEntryScrollRestorationIsManual(
11695 mBrowsingContext, aIsManual);
11700 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11701 uint32_t aCacheKey) {
11702 if (aSHEntry) {
11703 aSHEntry->SetCacheKey(aCacheKey);
11706 if (mActiveEntry && mBrowsingContext) {
11707 mActiveEntry->SetCacheKey(aCacheKey);
11708 if (XRE_IsParentProcess()) {
11709 SessionHistoryEntry* entry =
11710 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11711 if (entry) {
11712 entry->SetCacheKey(aCacheKey);
11714 } else {
11715 mozilla::Unused
11716 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11717 mBrowsingContext, aCacheKey);
11722 /* static */
11723 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11724 // I believe none of the about: urls should go in the history. But then
11725 // that could just be me... If the intent is only deny about:blank then we
11726 // should just do a spec compare, rather than two gets of the scheme and
11727 // then the path. -Gagan
11728 nsresult rv;
11729 nsAutoCString buf;
11731 rv = aURI->GetScheme(buf);
11732 if (NS_FAILED(rv)) {
11733 return false;
11736 if (buf.EqualsLiteral("about")) {
11737 rv = aURI->GetPathQueryRef(buf);
11738 if (NS_FAILED(rv)) {
11739 return false;
11742 if (buf.EqualsLiteral("blank")) {
11743 return false;
11745 // We only want to add about:newtab if it's not privileged, and
11746 // if it is not configured to show the blank page.
11747 if (buf.EqualsLiteral("newtab")) {
11748 if (!StaticPrefs::browser_newtabpage_enabled()) {
11749 return false;
11752 NS_ENSURE_TRUE(aChannel, false);
11753 nsCOMPtr<nsIPrincipal> resultPrincipal;
11754 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11755 aChannel, getter_AddRefs(resultPrincipal));
11756 NS_ENSURE_SUCCESS(rv, false);
11757 return !resultPrincipal->IsSystemPrincipal();
11761 return true;
11764 nsresult nsDocShell::AddToSessionHistory(
11765 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11766 nsIPrincipal* aPrincipalToInherit,
11767 nsIPrincipal* aPartitionedPrincipalToInherit,
11768 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11769 nsISHEntry** aNewEntry) {
11770 MOZ_ASSERT(aURI, "uri is null");
11771 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11772 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11774 #if defined(DEBUG)
11775 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11776 nsAutoCString chanName;
11777 if (aChannel) {
11778 aChannel->GetName(chanName);
11779 } else {
11780 chanName.AssignLiteral("<no channel>");
11783 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11784 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11785 aURI->GetSpecOrDefault().get(), chanName.get()));
11787 #endif
11789 nsresult rv = NS_OK;
11790 nsCOMPtr<nsISHEntry> entry;
11793 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11794 * the existing SH entry in the page and replace the url and
11795 * other vitalities.
11797 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11798 !mBrowsingContext->IsTop()) {
11799 // This is a subframe
11800 entry = mOSHE;
11801 if (entry) {
11802 entry->ClearEntry();
11806 // Create a new entry if necessary.
11807 if (!entry) {
11808 entry = new nsSHEntry();
11811 // Get the post data & referrer
11812 nsCOMPtr<nsIInputStream> inputStream;
11813 nsCOMPtr<nsIURI> originalURI;
11814 nsCOMPtr<nsIURI> resultPrincipalURI;
11815 nsCOMPtr<nsIURI> unstrippedURI;
11816 bool loadReplace = false;
11817 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11818 uint32_t cacheKey = 0;
11819 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11820 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11821 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11822 aPartitionedPrincipalToInherit;
11823 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11824 bool expired = false; // by default the page is not expired
11825 bool discardLayoutState = false;
11826 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11827 bool userActivation = false;
11829 if (aChannel) {
11830 cacheChannel = do_QueryInterface(aChannel);
11832 /* If there is a caching channel, get the Cache Key and store it
11833 * in SH.
11835 if (cacheChannel) {
11836 cacheChannel->GetCacheKey(&cacheKey);
11838 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11840 // Check if the httpChannel is hiding under a multipartChannel
11841 if (!httpChannel) {
11842 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11844 if (httpChannel) {
11845 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11846 if (uploadChannel) {
11847 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11849 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11850 uint32_t loadFlags;
11851 aChannel->GetLoadFlags(&loadFlags);
11852 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11853 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11854 MOZ_ASSERT(NS_SUCCEEDED(rv));
11856 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11859 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11860 if (!triggeringPrincipal) {
11861 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11863 if (!csp) {
11864 csp = loadInfo->GetCspToInherit();
11867 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11869 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
11871 userActivation = loadInfo->GetHasValidUserGestureActivation();
11873 // For now keep storing just the principal in the SHEntry.
11874 if (!principalToInherit) {
11875 if (loadInfo->GetLoadingSandboxed()) {
11876 if (loadInfo->GetLoadingPrincipal()) {
11877 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11878 loadInfo->GetLoadingPrincipal());
11879 } else {
11880 // get the OriginAttributes
11881 OriginAttributes attrs;
11882 loadInfo->GetOriginAttributes(&attrs);
11883 principalToInherit = NullPrincipal::Create(attrs);
11885 } else {
11886 principalToInherit = loadInfo->PrincipalToInherit();
11890 if (!partitionedPrincipalToInherit) {
11891 // XXXehsan is it correct to fall back to the principal to inherit in all
11892 // cases? For example, what about the cases where we are using the load
11893 // info's principal to inherit? Do we need to add a similar concept to
11894 // load info for partitioned principal?
11895 partitionedPrincipalToInherit = principalToInherit;
11899 nsAutoString srcdoc;
11900 bool srcdocEntry = false;
11901 nsCOMPtr<nsIURI> baseURI;
11903 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11904 if (inStrmChan) {
11905 bool isSrcdocChannel;
11906 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11907 if (isSrcdocChannel) {
11908 inStrmChan->GetSrcdocData(srcdoc);
11909 srcdocEntry = true;
11910 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11911 } else {
11912 srcdoc.SetIsVoid(true);
11915 /* If cache got a 'no-store', ask SH not to store
11916 * HistoryLayoutState. By default, SH will set this
11917 * flag to true and save HistoryLayoutState.
11919 bool saveLayoutState = !discardLayoutState;
11921 if (cacheChannel) {
11922 // Check if the page has expired from cache
11923 uint32_t expTime = 0;
11924 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11925 uint32_t now = PRTimeToSeconds(PR_Now());
11926 if (expTime <= now) {
11927 expired = true;
11931 // Title is set in nsDocShell::SetTitle()
11932 entry->Create(aURI, // uri
11933 u""_ns, // Title
11934 inputStream, // Post data stream
11935 cacheKey, // CacheKey
11936 mContentTypeHint, // Content-type
11937 triggeringPrincipal, // Channel or provided principal
11938 principalToInherit, partitionedPrincipalToInherit, csp,
11939 HistoryID(), GetCreatedDynamically(), originalURI,
11940 resultPrincipalURI, unstrippedURI, loadReplace, referrerInfo,
11941 srcdoc, srcdocEntry, baseURI, saveLayoutState, expired,
11942 userActivation);
11944 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11945 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11946 Maybe<int32_t> previousEntryIndex;
11947 Maybe<int32_t> loadedEntryIndex;
11948 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11949 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11950 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11952 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11953 if (previousEntryIndex.isSome()) {
11954 mPreviousEntryIndex = previousEntryIndex.value();
11956 if (loadedEntryIndex.isSome()) {
11957 mLoadedEntryIndex = loadedEntryIndex.value();
11960 // aCloneChildren implies that we are retaining the same document, thus we
11961 // need to signal to the top WC that the new SHEntry may receive a fresh
11962 // user interaction flag.
11963 if (aCloneChildren) {
11964 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11965 if (topWc && !topWc->IsDiscarded()) {
11966 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11969 } else {
11970 // This is a subframe, make sure that this new SHEntry will be
11971 // marked with user interaction.
11972 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11973 if (topWc && !topWc->IsDiscarded()) {
11974 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11976 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11977 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11978 aCloneChildren);
11982 // Return the new SH entry...
11983 if (aNewEntry) {
11984 *aNewEntry = nullptr;
11985 if (NS_SUCCEEDED(rv)) {
11986 entry.forget(aNewEntry);
11990 return rv;
11993 void nsDocShell::UpdateActiveEntry(
11994 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11995 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
11996 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
11997 const nsAString& aTitle, bool aScrollRestorationIsManual,
11998 nsIStructuredCloneContainer* aData, bool aURIWasModified) {
11999 MOZ_ASSERT(mozilla::SessionHistoryInParent());
12000 MOZ_ASSERT(aURI, "uri is null");
12001 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
12002 "This code only deals with pushState");
12003 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
12005 MOZ_LOG(gSHLog, LogLevel::Debug,
12006 ("Creating an active entry on nsDocShell %p to %s", this,
12007 aURI->GetSpecOrDefault().get()));
12009 // Even if we're replacing an existing entry we create new a
12010 // SessionHistoryInfo. In the parent process we'll keep the existing
12011 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
12012 // entry keeps identity but its data is replaced.
12013 bool replace = aReplace && mActiveEntry;
12015 if (!replace) {
12016 CollectWireframe();
12019 if (mActiveEntry) {
12020 // Link this entry to the previous active entry.
12021 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
12022 } else {
12023 mActiveEntry = MakeUnique<SessionHistoryInfo>(
12024 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
12026 mActiveEntry->SetOriginalURI(aOriginalURI);
12027 mActiveEntry->SetUnstrippedURI(nullptr);
12028 mActiveEntry->SetReferrerInfo(aReferrerInfo);
12029 mActiveEntry->SetTitle(aTitle);
12030 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
12031 mActiveEntry->SetURIWasModified(aURIWasModified);
12032 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
12034 if (replace) {
12035 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
12036 } else {
12037 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
12038 // FIXME We should probably just compute mChildOffset in the parent
12039 // instead of passing it over IPC here.
12040 mBrowsingContext->SetActiveSessionHistoryEntry(
12041 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
12042 /* aCacheKey = */ 0);
12043 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
12047 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
12048 bool aUserActivation) {
12049 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
12051 nsresult rv;
12052 RefPtr<nsDocShellLoadState> loadState;
12053 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
12054 NS_ENSURE_SUCCESS(rv, rv);
12056 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
12057 // that's the only thing holding a ref to aEntry that will cause aEntry to
12058 // die while we're loading it. So hold a strong ref to aEntry here, just
12059 // in case.
12060 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
12062 loadState->SetHasValidUserGestureActivation(
12063 loadState->HasValidUserGestureActivation() || aUserActivation);
12065 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
12068 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
12069 uint32_t aLoadType,
12070 bool aUserActivation) {
12071 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
12072 loadState->SetHasValidUserGestureActivation(
12073 loadState->HasValidUserGestureActivation() || aUserActivation);
12075 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
12078 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
12079 uint32_t aLoadType,
12080 bool aLoadingCurrentEntry) {
12081 if (!IsNavigationAllowed()) {
12082 return NS_OK;
12085 // We are setting load type afterwards so we don't have to
12086 // send it in an IPC message
12087 aLoadState->SetLoadType(aLoadType);
12089 nsresult rv;
12090 if (SchemeIsJavascript(aLoadState->URI())) {
12091 // We're loading a URL that will execute script from inside asyncOpen.
12092 // Replace the current document with about:blank now to prevent
12093 // anything from the current document from leaking into any JavaScript
12094 // code in the URL.
12095 // Don't cache the presentation if we're going to just reload the
12096 // current entry. Caching would lead to trying to save the different
12097 // content viewers in the same nsISHEntry object.
12098 rv = CreateAboutBlankContentViewer(
12099 aLoadState->PrincipalToInherit(),
12100 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
12101 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
12103 if (NS_FAILED(rv)) {
12104 // The creation of the intermittent about:blank content
12105 // viewer failed for some reason (potentially because the
12106 // user prevented it). Interrupt the history load.
12107 return NS_OK;
12110 if (!aLoadState->TriggeringPrincipal()) {
12111 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
12112 // URIs will pick it up from the about:blank page we just loaded,
12113 // and we don't really want even that in this case.
12114 nsCOMPtr<nsIPrincipal> principal =
12115 NullPrincipal::Create(GetOriginAttributes());
12116 aLoadState->SetTriggeringPrincipal(principal);
12120 /* If there is a valid postdata *and* the user pressed
12121 * reload or shift-reload, take user's permission before we
12122 * repost the data to the server.
12124 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
12125 bool repost;
12126 rv = ConfirmRepost(&repost);
12127 if (NS_FAILED(rv)) {
12128 return rv;
12131 // If the user pressed cancel in the dialog, return. We're done here.
12132 if (!repost) {
12133 return NS_BINDING_ABORTED;
12137 // If there is no valid triggeringPrincipal, we deny the load
12138 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
12139 "need a valid triggeringPrincipal to load from history");
12140 if (!aLoadState->TriggeringPrincipal()) {
12141 return NS_ERROR_FAILURE;
12144 return InternalLoad(aLoadState); // No nsIRequest
12147 NS_IMETHODIMP
12148 nsDocShell::PersistLayoutHistoryState() {
12149 nsresult rv = NS_OK;
12151 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12152 bool scrollRestorationIsManual;
12153 if (mozilla::SessionHistoryInParent()) {
12154 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12155 } else {
12156 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12158 nsCOMPtr<nsILayoutHistoryState> layoutState;
12159 if (RefPtr<PresShell> presShell = GetPresShell()) {
12160 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12161 } else if (scrollRestorationIsManual) {
12162 // Even if we don't have layout anymore, we may want to reset the
12163 // current scroll state in layout history.
12164 GetLayoutHistoryState(getter_AddRefs(layoutState));
12167 if (scrollRestorationIsManual && layoutState) {
12168 layoutState->ResetScrollState();
12172 return rv;
12175 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12176 nsISHEntry* aNewEntry) {
12177 if (aOldEntry == mOSHE) {
12178 mOSHE = aNewEntry;
12181 if (aOldEntry == mLSHE) {
12182 mLSHE = aNewEntry;
12186 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12187 const Maybe<nsISHEntry*>& aOSHE) {
12188 // We want to hold on to the reference in mLSHE before we update it.
12189 // Otherwise, SetHistoryEntry could release the last reference to
12190 // the entry while aOSHE is pointing to it.
12191 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12192 if (aLSHE.isSome()) {
12193 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12194 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12196 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12197 if (aOSHE.isSome()) {
12198 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12199 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12203 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12204 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12205 // We need to sync up the docshell and session history trees for
12206 // subframe navigation. If the load was in a subframe, we forward up to
12207 // the root docshell, which will then recursively sync up all docshells
12208 // to their corresponding entries in the new session history tree.
12209 // If we don't do this, then we can cache a content viewer on the wrong
12210 // cloned entry, and subsequently restore it at the wrong time.
12211 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12212 if (topBC->IsDiscarded()) {
12213 topBC = nullptr;
12215 RefPtr<BrowsingContext> currBC =
12216 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12217 if (topBC && *aPtr) {
12218 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12220 nsCOMPtr<nsISHEntry> entry(aEntry);
12221 entry.swap(*aPtr);
12222 return entry.forget();
12225 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12226 RefPtr<ChildSHistory> childSHistory =
12227 mBrowsingContext->Top()->GetChildSessionHistory();
12228 return childSHistory.forget();
12231 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12232 nsIHttpChannel** aReturn) {
12233 NS_ENSURE_ARG_POINTER(aReturn);
12234 if (!aChannel) {
12235 return NS_ERROR_FAILURE;
12238 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12239 if (multiPartChannel) {
12240 nsCOMPtr<nsIChannel> baseChannel;
12241 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12242 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12243 *aReturn = httpChannel;
12244 NS_IF_ADDREF(*aReturn);
12246 return NS_OK;
12249 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12250 // By default layout State will be saved.
12251 if (!aChannel) {
12252 return false;
12255 // figure out if SH should be saving layout state
12256 bool noStore = false;
12257 Unused << aChannel->IsNoStoreResponse(&noStore);
12258 return noStore;
12261 NS_IMETHODIMP
12262 nsDocShell::GetEditor(nsIEditor** aEditor) {
12263 NS_ENSURE_ARG_POINTER(aEditor);
12264 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12265 htmlEditor.forget(aEditor);
12266 return NS_OK;
12269 NS_IMETHODIMP
12270 nsDocShell::SetEditor(nsIEditor* aEditor) {
12271 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12272 // If TextEditor comes, throw an error.
12273 if (aEditor && !htmlEditor) {
12274 return NS_ERROR_INVALID_ARG;
12276 return SetHTMLEditorInternal(htmlEditor);
12279 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12280 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12283 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12284 if (!aHTMLEditor && !mEditorData) {
12285 return NS_OK;
12288 nsresult rv = EnsureEditorData();
12289 if (NS_FAILED(rv)) {
12290 return rv;
12293 return mEditorData->SetHTMLEditor(aHTMLEditor);
12296 NS_IMETHODIMP
12297 nsDocShell::GetEditable(bool* aEditable) {
12298 NS_ENSURE_ARG_POINTER(aEditable);
12299 *aEditable = mEditorData && mEditorData->GetEditable();
12300 return NS_OK;
12303 NS_IMETHODIMP
12304 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12305 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12307 if (mEditorData) {
12308 *aHasEditingSession = !!mEditorData->GetEditingSession();
12309 } else {
12310 *aHasEditingSession = false;
12313 return NS_OK;
12316 NS_IMETHODIMP
12317 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12318 nsresult rv = EnsureEditorData();
12319 if (NS_FAILED(rv)) {
12320 return rv;
12323 return mEditorData->MakeEditable(aInWaitForUriLoad);
12326 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12327 bool needToAddURIVisit = true;
12328 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12329 if (props) {
12330 mozilla::Unused << props->GetPropertyAsBool(
12331 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12334 return needToAddURIVisit;
12337 /* static */ void nsDocShell::ExtractLastVisit(
12338 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12339 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12340 if (!props) {
12341 return;
12344 nsresult rv;
12345 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12346 if (NS_SUCCEEDED(rv)) {
12347 uri.forget(aURI);
12349 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12350 aChannelRedirectFlags);
12352 NS_WARNING_ASSERTION(
12353 NS_SUCCEEDED(rv),
12354 "Could not fetch previous flags, URI will be treated like referrer");
12356 } else {
12357 // There is no last visit for this channel, so this must be the first
12358 // link. Link the visit to the referrer of this request, if any.
12359 // Treat referrer as null if there is an error getting it.
12360 NS_GetReferrerFromChannel(aChannel, aURI);
12364 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12365 uint32_t aChannelRedirectFlags) {
12366 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12367 if (!props || !aURI) {
12368 return;
12371 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12372 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12373 aChannelRedirectFlags);
12376 /* static */ void nsDocShell::InternalAddURIVisit(
12377 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12378 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12379 nsIWidget* aWidget, uint32_t aLoadType) {
12380 MOZ_ASSERT(aURI, "Visited URI is null!");
12381 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12382 "Do not add error or bypass pages to global history");
12384 bool usePrivateBrowsing = false;
12385 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12387 // Only content-type docshells save URI visits. Also don't do
12388 // anything here if we're not supposed to use global history.
12389 if (!aBrowsingContext->IsContent() ||
12390 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12391 return;
12394 nsCOMPtr<IHistory> history = components::History::Service();
12396 if (history) {
12397 uint32_t visitURIFlags = 0;
12399 if (aBrowsingContext->IsTop()) {
12400 visitURIFlags |= IHistory::TOP_LEVEL;
12403 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12404 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12405 } else if (aChannelRedirectFlags &
12406 nsIChannelEventSink::REDIRECT_PERMANENT) {
12407 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12408 } else {
12409 MOZ_ASSERT(!aChannelRedirectFlags,
12410 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12411 "if any flags in aChannelRedirectFlags is set.");
12414 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12415 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12416 if (aResponseStatus == 301 || aResponseStatus == 308) {
12417 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12420 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12421 // simple retry attempt by the user is unlikely to solve them.
12422 // 408 is special cased, since may actually indicate a temporary
12423 // connection problem.
12424 else if (aResponseStatus != 408 &&
12425 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12426 aResponseStatus == 505)) {
12427 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12430 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12431 visitURIFlags,
12432 aBrowsingContext->BrowserId());
12436 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12437 uint32_t aChannelRedirectFlags,
12438 uint32_t aResponseStatus) {
12439 nsPIDOMWindowOuter* outer = GetWindow();
12440 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12442 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12443 aResponseStatus, mBrowsingContext, widget, mLoadType);
12446 //*****************************************************************************
12447 // nsDocShell: Helper Routines
12448 //*****************************************************************************
12450 NS_IMETHODIMP
12451 nsDocShell::SetLoadType(uint32_t aLoadType) {
12452 mLoadType = aLoadType;
12453 return NS_OK;
12456 NS_IMETHODIMP
12457 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12458 *aLoadType = mLoadType;
12459 return NS_OK;
12462 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12463 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12464 *aRepost = true;
12465 return NS_OK;
12468 nsCOMPtr<nsIPromptCollection> prompter =
12469 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12470 if (!prompter) {
12471 return NS_ERROR_NOT_AVAILABLE;
12474 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12477 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12478 nsIStringBundle** aStringBundle) {
12479 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12480 NS_ERROR_FAILURE);
12482 nsCOMPtr<nsIStringBundleService> stringBundleService =
12483 mozilla::components::StringBundle::Service();
12484 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12486 NS_ENSURE_SUCCESS(
12487 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12488 NS_ERROR_FAILURE);
12490 return NS_OK;
12493 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12494 PresShell* presShell = GetPresShell();
12495 NS_ENSURE_TRUE(presShell, nullptr);
12497 return presShell->GetRootScrollFrameAsScrollable();
12500 nsresult nsDocShell::EnsureScriptEnvironment() {
12501 if (mScriptGlobal) {
12502 return NS_OK;
12505 if (mIsBeingDestroyed) {
12506 return NS_ERROR_NOT_AVAILABLE;
12509 #ifdef DEBUG
12510 NS_ASSERTION(!mInEnsureScriptEnv,
12511 "Infinite loop! Calling EnsureScriptEnvironment() from "
12512 "within EnsureScriptEnvironment()!");
12514 // Yeah, this isn't re-entrant safe, but that's ok since if we
12515 // re-enter this method, we'll infinitely loop...
12516 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12517 mInEnsureScriptEnv = true;
12518 #endif
12520 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12521 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12523 uint32_t chromeFlags;
12524 browserChrome->GetChromeFlags(&chromeFlags);
12526 // If our window is modal and we're not opened as chrome, make
12527 // this window a modal content window.
12528 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12529 MOZ_ASSERT(mScriptGlobal);
12531 // Ensure the script object is set up to run script.
12532 return mScriptGlobal->EnsureScriptEnvironment();
12535 nsresult nsDocShell::EnsureEditorData() {
12536 MOZ_ASSERT(!mIsBeingDestroyed);
12538 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12539 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12540 // We shouldn't recreate the editor data if it already exists, or
12541 // we're shutting down, or we already have a detached editor data
12542 // stored in the session history. We should only have one editordata
12543 // per docshell.
12544 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12547 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12550 nsresult nsDocShell::EnsureFind() {
12551 if (!mFind) {
12552 mFind = new nsWebBrowserFind();
12555 // we promise that the nsIWebBrowserFind that we return has been set
12556 // up to point to the focused, or content window, so we have to
12557 // set that up each time.
12559 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12560 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12562 // default to our window
12563 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12564 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12565 nsFocusManager::GetFocusedDescendant(ourWindow,
12566 nsFocusManager::eIncludeAllDescendants,
12567 getter_AddRefs(windowToSearch));
12569 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12570 if (!findInFrames) {
12571 return NS_ERROR_NO_INTERFACE;
12574 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12575 if (NS_FAILED(rv)) {
12576 return rv;
12578 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12579 if (NS_FAILED(rv)) {
12580 return rv;
12583 return NS_OK;
12586 NS_IMETHODIMP
12587 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12588 NS_ENSURE_ARG(aDoomed);
12589 *aDoomed = mIsBeingDestroyed;
12590 return NS_OK;
12593 NS_IMETHODIMP
12594 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12595 NS_ENSURE_ARG(aResult);
12596 *aResult = mIsExecutingOnLoadHandler;
12597 return NS_OK;
12600 NS_IMETHODIMP
12601 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12602 nsCOMPtr<nsILayoutHistoryState> state;
12603 if (mozilla::SessionHistoryInParent()) {
12604 if (mActiveEntry) {
12605 state = mActiveEntry->GetLayoutHistoryState();
12607 } else {
12608 if (mOSHE) {
12609 state = mOSHE->GetLayoutHistoryState();
12612 state.forget(aLayoutHistoryState);
12613 return NS_OK;
12616 NS_IMETHODIMP
12617 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12618 if (mOSHE) {
12619 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12621 if (mActiveEntry) {
12622 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12624 return NS_OK;
12627 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12628 nsIInterfaceRequestor* aRequestor) {
12629 if (aRequestor) {
12630 mWeakPtr = do_GetWeakReference(aRequestor);
12634 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12635 mWeakPtr = nullptr;
12638 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12640 NS_IMETHODIMP
12641 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12642 void** aSink) {
12643 NS_ENSURE_ARG_POINTER(aSink);
12644 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12645 if (ifReq) {
12646 return ifReq->GetInterface(aIID, aSink);
12648 *aSink = nullptr;
12649 return NS_NOINTERFACE;
12652 //*****************************************************************************
12653 // nsDocShell::nsIAuthPromptProvider
12654 //*****************************************************************************
12656 NS_IMETHODIMP
12657 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12658 void** aResult) {
12659 // a priority prompt request will override a false mAllowAuth setting
12660 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12662 if (!mAllowAuth && !priorityPrompt) {
12663 return NS_ERROR_NOT_AVAILABLE;
12666 // we're either allowing auth, or it's a proxy request
12667 nsresult rv;
12668 nsCOMPtr<nsIPromptFactory> wwatch =
12669 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12670 NS_ENSURE_SUCCESS(rv, rv);
12672 rv = EnsureScriptEnvironment();
12673 NS_ENSURE_SUCCESS(rv, rv);
12675 // Get the an auth prompter for our window so that the parenting
12676 // of the dialogs works as it should when using tabs.
12678 return wwatch->GetPrompt(mScriptGlobal, aIID,
12679 reinterpret_cast<void**>(aResult));
12682 //*****************************************************************************
12683 // nsDocShell::nsILoadContext
12684 //*****************************************************************************
12686 NS_IMETHODIMP
12687 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12688 CallGetInterface(this, aWindow);
12689 return NS_OK;
12692 NS_IMETHODIMP
12693 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12694 return mBrowsingContext->GetTopWindow(aWindow);
12697 NS_IMETHODIMP
12698 nsDocShell::GetTopFrameElement(Element** aElement) {
12699 return mBrowsingContext->GetTopFrameElement(aElement);
12702 NS_IMETHODIMP
12703 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12704 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12707 NS_IMETHODIMP
12708 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12709 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12712 NS_IMETHODIMP
12713 nsDocShell::GetIsContent(bool* aIsContent) {
12714 *aIsContent = (mItemType == typeContent);
12715 return NS_OK;
12718 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12719 MOZ_ASSERT(aURI, "Must have a URI!");
12721 if (!mFiredUnloadEvent) {
12722 return true;
12725 if (!mLoadingURI) {
12726 return false;
12729 bool isPrivateWin = false;
12730 Document* doc = GetDocument();
12731 if (doc) {
12732 isPrivateWin =
12733 doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
12736 nsCOMPtr<nsIScriptSecurityManager> secMan =
12737 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12738 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12739 aURI, mLoadingURI, false, isPrivateWin));
12743 // Routines for selection and clipboard
12745 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12746 nsIController** aResult) {
12747 NS_ENSURE_ARG_POINTER(aResult);
12748 *aResult = nullptr;
12750 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12752 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12753 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12755 return root->GetControllerForCommand(aCommand, false /* for any window */,
12756 aResult);
12759 NS_IMETHODIMP
12760 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12761 NS_ENSURE_ARG_POINTER(aResult);
12762 *aResult = false;
12764 nsresult rv = NS_ERROR_FAILURE;
12766 nsCOMPtr<nsIController> controller;
12767 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12768 if (controller) {
12769 rv = controller->IsCommandEnabled(aCommand, aResult);
12772 return rv;
12775 NS_IMETHODIMP
12776 nsDocShell::DoCommand(const char* aCommand) {
12777 nsresult rv = NS_ERROR_FAILURE;
12779 nsCOMPtr<nsIController> controller;
12780 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12781 if (controller) {
12782 rv = controller->DoCommand(aCommand);
12785 return rv;
12788 NS_IMETHODIMP
12789 nsDocShell::DoCommandWithParams(const char* aCommand,
12790 nsICommandParams* aParams) {
12791 nsCOMPtr<nsIController> controller;
12792 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12793 if (NS_WARN_IF(NS_FAILED(rv))) {
12794 return rv;
12797 nsCOMPtr<nsICommandController> commandController =
12798 do_QueryInterface(controller, &rv);
12799 if (NS_WARN_IF(NS_FAILED(rv))) {
12800 return rv;
12803 return commandController->DoCommandWithParams(aCommand, aParams);
12806 nsresult nsDocShell::EnsureCommandHandler() {
12807 if (!mCommandManager) {
12808 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12809 mCommandManager = new nsCommandManager(domWindow);
12812 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12815 // link handling
12817 class OnLinkClickEvent : public Runnable {
12818 public:
12819 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12820 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12821 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12823 NS_IMETHOD Run() override {
12824 AutoPopupStatePusher popupStatePusher(mPopupState);
12826 // We need to set up an AutoJSAPI here for the following reason: When we
12827 // do OnLinkClickSync we'll eventually end up in
12828 // nsGlobalWindow::OpenInternal which only does popup blocking if
12829 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12830 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12831 // concerned.
12832 AutoJSAPI jsapi;
12833 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12834 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12835 mTriggeringPrincipal);
12837 return NS_OK;
12840 private:
12841 RefPtr<nsDocShell> mHandler;
12842 nsCOMPtr<nsIContent> mContent;
12843 RefPtr<nsDocShellLoadState> mLoadState;
12844 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12845 PopupBlocker::PopupControlState mPopupState;
12846 bool mNoOpenerImplied;
12847 bool mIsTrusted;
12850 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12851 nsDocShellLoadState* aLoadState,
12852 bool aNoOpenerImplied, bool aIsTrusted,
12853 nsIPrincipal* aTriggeringPrincipal)
12854 : mozilla::Runnable("OnLinkClickEvent"),
12855 mHandler(aHandler),
12856 mContent(aContent),
12857 mLoadState(aLoadState),
12858 mTriggeringPrincipal(aTriggeringPrincipal),
12859 mPopupState(PopupBlocker::GetPopupControlState()),
12860 mNoOpenerImplied(aNoOpenerImplied),
12861 mIsTrusted(aIsTrusted) {}
12863 nsresult nsDocShell::OnLinkClick(
12864 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12865 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12866 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12867 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12868 #ifndef ANDROID
12869 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12870 #endif
12871 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12873 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12874 return NS_OK;
12877 // On history navigation through Back/Forward buttons, don't execute
12878 // automatic JavaScript redirection such as |anchorElement.click()| or
12879 // |formElement.submit()|.
12881 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12882 // nsDocShell::OnLinkClickSync(...) instead.
12883 if (ShouldBlockLoadingForBackButton()) {
12884 return NS_OK;
12887 if (aContent->IsEditable()) {
12888 return NS_OK;
12891 Document* ownerDoc = aContent->OwnerDoc();
12892 if (nsContentUtils::IsExternalProtocol(aURI)) {
12893 ownerDoc->EnsureNotEnteringAndExitFullscreen();
12896 bool noOpenerImplied = false;
12897 nsAutoString target(aTargetSpec);
12898 if (aFileName.IsVoid() &&
12899 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
12900 target = u"_blank";
12901 if (!aTargetSpec.Equals(target)) {
12902 noOpenerImplied = true;
12906 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12907 loadState->SetTarget(target);
12908 loadState->SetFileName(aFileName);
12909 loadState->SetPostDataStream(aPostDataStream);
12910 loadState->SetHeadersStream(aHeadersDataStream);
12911 loadState->SetFirstParty(true);
12912 loadState->SetTriggeringPrincipal(
12913 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12914 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12915 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12916 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12918 nsCOMPtr<nsIRunnable> ev =
12919 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12920 aIsTrusted, aTriggeringPrincipal);
12921 return Dispatch(TaskCategory::UI, ev.forget());
12924 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12925 nsIURI* aLinkURI, nsIContent* aContent,
12926 bool aIsUserTriggered) {
12927 if (net::SchemeIsJavascript(aLinkURI)) {
12928 return false;
12931 // External links from within app tabs should always open in new tabs
12932 // instead of replacing the app tab's page (Bug 575561)
12933 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12934 // get either host, just return false to use the original target.
12935 nsAutoCString linkHost;
12936 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12937 return false;
12940 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12941 // privileged code to change the default targeting behaviour. In particular,
12942 // if a user-initiated link click for the (or targetting the) top-level frame
12943 // is detected, we default the target to "_blank" to give it a new
12944 // top-level BrowsingContext.
12945 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
12946 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
12947 aOriginalTarget == u"_top"_ns)) {
12948 return true;
12951 // Don't modify non-default targets.
12952 if (!aOriginalTarget.IsEmpty()) {
12953 return false;
12956 // Only check targets that are in extension panels or app tabs.
12957 // (isAppTab will be false for app tab subframes).
12958 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12959 if (!mmGroup.EqualsLiteral("webext-browsers") &&
12960 !mBrowsingContext->IsAppTab()) {
12961 return false;
12964 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12965 if (!docURI) {
12966 return false;
12969 nsAutoCString docHost;
12970 if (NS_FAILED(docURI->GetHost(docHost))) {
12971 return false;
12974 if (linkHost.Equals(docHost)) {
12975 return false;
12978 // Special case: ignore "www" prefix if it is part of host string
12979 return linkHost.Length() < docHost.Length()
12980 ? !docHost.Equals("www."_ns + linkHost)
12981 : !linkHost.Equals("www."_ns + docHost);
12984 static bool ElementCanHaveNoopener(nsIContent* aContent) {
12985 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12986 // the HTML, XHTML, or SVG namespace.
12987 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
12988 nsGkAtoms::form) ||
12989 aContent->IsSVGElement(nsGkAtoms::a);
12992 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12993 nsDocShellLoadState* aLoadState,
12994 bool aNoOpenerImplied,
12995 nsIPrincipal* aTriggeringPrincipal) {
12996 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12997 return NS_OK;
13000 // XXX When the linking node was HTMLFormElement, it is synchronous event.
13001 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
13002 // but |HTMLFormElement::SubmitSubmission(...)|.
13003 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
13004 ShouldBlockLoadingForBackButton()) {
13005 return NS_OK;
13008 if (aContent->IsEditable()) {
13009 return NS_OK;
13012 // if the triggeringPrincipal is not passed explicitly, then we
13013 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
13014 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
13015 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
13018 // defer to an external protocol handler if necessary...
13019 nsCOMPtr<nsIExternalProtocolService> extProtService =
13020 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
13021 if (extProtService) {
13022 nsAutoCString scheme;
13023 aLoadState->URI()->GetScheme(scheme);
13024 if (!scheme.IsEmpty()) {
13025 // if the URL scheme does not correspond to an exposed protocol, then
13026 // we need to hand this link click over to the external protocol
13027 // handler.
13028 bool isExposed;
13029 nsresult rv =
13030 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
13031 if (NS_SUCCEEDED(rv) && !isExposed) {
13032 return extProtService->LoadURI(
13033 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
13034 /* aTriggeredExternally */
13035 false,
13036 /* aHasValidUserGestureActivation */
13037 aContent->OwnerDoc()->HasValidTransientUserGestureActivation());
13042 uint32_t triggeringSandboxFlags = 0;
13043 if (mBrowsingContext) {
13044 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
13047 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
13048 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
13049 bool triggeringPrincipalIsSystemPrincipal =
13050 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
13051 if (elementCanHaveNoopener) {
13052 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
13053 nsAutoString relString;
13054 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
13055 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
13056 relString);
13058 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
13059 bool explicitOpenerSet = false;
13061 // The opener behaviour follows a hierarchy, such that if a higher
13062 // priority behaviour is specified, it always takes priority. That
13063 // priority is currently: norefrerer > noopener > opener > default
13065 while (tok.hasMoreTokens()) {
13066 const nsAString& token = tok.nextToken();
13067 if (token.LowerCaseEqualsLiteral("noreferrer")) {
13068 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
13069 INTERNAL_LOAD_FLAGS_NO_OPENER;
13070 // noreferrer cannot be overwritten by a 'rel=opener'.
13071 explicitOpenerSet = true;
13072 break;
13075 if (token.LowerCaseEqualsLiteral("noopener")) {
13076 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13077 explicitOpenerSet = true;
13080 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
13081 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
13082 explicitOpenerSet = true;
13086 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
13087 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
13088 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13091 if (aNoOpenerImplied) {
13092 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13096 // Get the owner document of the link that was clicked, this will be
13097 // the document that the link is in, or the last document that the
13098 // link was in. From that document, we'll get the URI to use as the
13099 // referrer, since the current URI in this docshell may be a
13100 // new document that we're in the process of loading.
13101 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
13103 // Now check that the referrerDoc's inner window is the current inner
13104 // window for mScriptGlobal. If it's not, then we don't want to
13105 // follow this link.
13106 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
13107 if (!mScriptGlobal || !referrerInner ||
13108 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
13109 // We're no longer the current inner window
13110 return NS_OK;
13113 // referrer could be null here in some odd cases, but that's ok,
13114 // we'll just load the link w/o sending a referrer in those cases.
13116 // If this is an anchor element, grab its type property to use as a hint
13117 nsAutoString typeHint;
13118 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13119 if (anchor) {
13120 anchor->GetType(typeHint);
13121 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13122 nsAutoCString type, dummy;
13123 NS_ParseRequestContentType(utf8Hint, type, dummy);
13124 CopyUTF8toUTF16(type, typeHint);
13127 uint32_t loadType = LOAD_LINK;
13128 if (aLoadState->IsFormSubmission()) {
13129 if (aLoadState->Target().IsEmpty()) {
13130 // We set the right load type here for form submissions with an empty
13131 // target. Form submission with a non-empty target are handled in
13132 // nsDocShell::PerformRetargeting after we've selected the correct target
13133 // BC.
13134 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
13136 } else {
13137 // Link click can be triggered inside an onload handler, and we don't want
13138 // to add history entry in this case.
13139 bool inOnLoadHandler = false;
13140 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13141 if (inOnLoadHandler) {
13142 loadType = LOAD_NORMAL_REPLACE;
13146 nsCOMPtr<nsIReferrerInfo> referrerInfo =
13147 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
13148 : new ReferrerInfo(*referrerDoc);
13149 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
13151 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
13152 aLoadState->SetReferrerInfo(referrerInfo);
13153 aLoadState->SetInternalLoadFlags(flags);
13154 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
13155 aLoadState->SetLoadType(loadType);
13156 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
13157 aLoadState->SetHasValidUserGestureActivation(
13158 context && context->HasValidTransientUserGestureActivation());
13160 nsresult rv = InternalLoad(aLoadState);
13162 if (NS_SUCCEEDED(rv)) {
13163 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
13164 referrerInfo);
13167 return rv;
13170 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13171 const nsAString& aTargetSpec) {
13172 if (aContent->IsEditable()) {
13173 return NS_OK;
13176 nsresult rv = NS_ERROR_FAILURE;
13178 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13179 if (!browserChrome) {
13180 return rv;
13183 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13184 nsAutoCString spec;
13185 rv = exposableURI->GetDisplaySpec(spec);
13186 NS_ENSURE_SUCCESS(rv, rv);
13188 NS_ConvertUTF8toUTF16 uStr(spec);
13190 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13191 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13193 rv = browserChrome->SetLinkStatus(uStr);
13194 return rv;
13197 nsresult nsDocShell::OnLeaveLink() {
13198 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13199 nsresult rv = NS_ERROR_FAILURE;
13201 if (browserChrome) {
13202 rv = browserChrome->SetLinkStatus(u""_ns);
13204 return rv;
13207 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13208 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13209 UserActivation::IsHandlingUserInput() ||
13210 !Preferences::GetBool("accessibility.blockjsredirection")) {
13211 return false;
13214 bool canGoForward = false;
13215 GetCanGoForward(&canGoForward);
13216 return canGoForward;
13219 bool nsDocShell::PluginsAllowedInCurrentDoc() {
13220 if (!mContentViewer) {
13221 return false;
13224 Document* doc = mContentViewer->GetDocument();
13225 if (!doc) {
13226 return false;
13229 return doc->GetAllowPlugins();
13232 //----------------------------------------------------------------------
13233 // Web Shell Services API
13235 // This functions is only called when a new charset is detected in loading a
13236 // document.
13237 nsresult nsDocShell::CharsetChangeReloadDocument(
13238 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13239 // XXX hack. keep the aCharset and aSource wait to pick it up
13240 nsCOMPtr<nsIContentViewer> cv;
13241 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
13242 if (cv) {
13243 int32_t source;
13244 Unused << cv->GetReloadEncodingAndSource(&source);
13245 if (aSource > source) {
13246 cv->SetReloadEncodingAndSource(aEncoding, aSource);
13247 if (eCharsetReloadRequested != mCharsetReloadState) {
13248 mCharsetReloadState = eCharsetReloadRequested;
13249 switch (mLoadType) {
13250 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13251 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13252 LOAD_FLAGS_BYPASS_PROXY);
13253 case LOAD_RELOAD_BYPASS_CACHE:
13254 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13255 default:
13256 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13261 // return failure if this request is not accepted due to mCharsetReloadState
13262 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13265 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13266 if (eCharsetReloadRequested != mCharsetReloadState) {
13267 Stop(nsIWebNavigation::STOP_ALL);
13268 return NS_OK;
13270 // return failer if this request is not accepted due to mCharsetReloadState
13271 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13274 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13275 #if NS_PRINT_PREVIEW
13276 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mContentViewer);
13277 return viewer->ExitPrintPreview();
13278 #else
13279 return NS_OK;
13280 #endif
13283 /* [infallible] */
13284 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13285 bool* aIsTopLevelContentDocShell) {
13286 *aIsTopLevelContentDocShell = false;
13288 if (mItemType == typeContent) {
13289 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13292 return NS_OK;
13295 // Implements nsILoadContext.originAttributes
13296 NS_IMETHODIMP
13297 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13298 JS::MutableHandle<JS::Value> aVal) {
13299 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13302 // Implements nsIDocShell.GetOriginAttributes()
13303 NS_IMETHODIMP
13304 nsDocShell::GetOriginAttributes(JSContext* aCx,
13305 JS::MutableHandle<JS::Value> aVal) {
13306 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13309 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13310 nsIURI* aURI) {
13311 MOZ_ASSERT(aPrincipal);
13312 MOZ_ASSERT(aURI);
13314 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13315 return false;
13318 nsCOMPtr<nsIDocShellTreeItem> parent;
13319 GetInProcessSameTypeParent(getter_AddRefs(parent));
13320 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13321 nsPIDOMWindowInner* parentInner =
13322 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13324 StorageAccess storage =
13325 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13327 // If the partitioned service worker is enabled, service worker is allowed to
13328 // control the window if partition is enabled.
13329 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13330 RefPtr<Document> doc = parentInner->GetExtantDoc();
13332 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13333 return true;
13337 return storage == StorageAccess::eAllow;
13340 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13341 MOZ_ASSERT(!mIsBeingDestroyed);
13342 return mBrowsingContext->SetOriginAttributes(aAttrs);
13345 NS_IMETHODIMP
13346 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13347 RefPtr<nsDocShell> self = this;
13348 RefPtr<ChildProcessChannelListener> cpcl =
13349 ChildProcessChannelListener::GetSingleton();
13351 // Call into InternalLoad with the pending channel when it is received.
13352 cpcl->RegisterCallback(
13353 aIdentifier, [self, aHistoryIndex](
13354 nsDocShellLoadState* aLoadState,
13355 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13356 aStreamFilterEndpoints,
13357 nsDOMNavigationTiming* aTiming) {
13358 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13359 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13360 aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
13361 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
13362 return NS_BINDING_ABORTED;
13365 self->mLoadType = aLoadState->LoadType();
13366 nsCOMPtr<nsIURI> previousURI;
13367 uint32_t previousFlags = 0;
13368 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13369 getter_AddRefs(previousURI), &previousFlags);
13370 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13371 previousURI, previousFlags);
13373 if (aTiming) {
13374 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13375 self->mBlankTiming = false;
13378 // If we're performing a history load, locate the correct history entry,
13379 // and set the relevant bits on our loadState.
13380 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13381 !mozilla::SessionHistoryInParent()) {
13382 nsCOMPtr<nsISHistory> legacySHistory =
13383 self->GetSessionHistory()->LegacySHistory();
13385 nsCOMPtr<nsISHEntry> entry;
13386 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13387 getter_AddRefs(entry));
13388 if (NS_SUCCEEDED(rv)) {
13389 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13390 aLoadState->SetLoadType(LOAD_HISTORY);
13391 aLoadState->SetSHEntry(entry);
13395 self->InternalLoad(aLoadState);
13397 if (aLoadState->GetOriginalURIString().isSome()) {
13398 // Save URI string in case it's needed later when
13399 // sending to search engine service in EndPageLoad()
13400 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13403 for (auto& endpoint : aStreamFilterEndpoints) {
13404 extensions::StreamFilterParent::Attach(
13405 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13408 // If the channel isn't pending, then it means that InternalLoad
13409 // never connected it, and we shouldn't try to continue. This
13410 // can happen even if InternalLoad returned NS_OK.
13411 bool pending = false;
13412 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13413 NS_ASSERTION(pending, "We should have connected the pending channel!");
13414 if (!pending) {
13415 return NS_BINDING_ABORTED;
13417 return NS_OK;
13419 return NS_OK;
13422 NS_IMETHODIMP
13423 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13424 JSContext* aCx) {
13425 OriginAttributes attrs;
13426 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13427 return NS_ERROR_INVALID_ARG;
13430 return SetOriginAttributes(attrs);
13433 NS_IMETHODIMP
13434 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13435 if (PresShell* presShell = GetPresShell()) {
13436 *aOut = presShell->AsyncPanZoomEnabled();
13437 return NS_OK;
13440 // If we don't have a presShell, fall back to the default platform value of
13441 // whether or not APZ is enabled.
13442 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13443 return NS_OK;
13446 bool nsDocShell::HasUnloadedParent() {
13447 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13448 wc = wc->GetParentWindowContext()) {
13449 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13450 wc->GetBrowsingContext()->IsDiscarded()) {
13451 // If a parent is OOP and the parent WindowContext is no
13452 // longer current, we can assume the parent was unloaded.
13453 return true;
13456 if (wc->GetBrowsingContext()->IsInProcess() &&
13457 (!wc->GetBrowsingContext()->GetDocShell() ||
13458 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13459 return true;
13462 return false;
13465 /* static */
13466 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13467 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13468 aLoadType & LOAD_CMD_HISTORY);
13471 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13472 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13473 return;
13476 // Global history is interested into sub-frame visits only for link-coloring
13477 // purposes, thus title updates are skipped for those.
13479 // Moreover, some iframe documents (such as the ones created via
13480 // document.open()) inherit the document uri of the caller, which would cause
13481 // us to override a previously set page title with one from the subframe.
13482 if (IsSubframe()) {
13483 return;
13486 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13487 history->SetURITitle(aURI, mTitle);
13491 bool nsDocShell::IsInvisible() { return mInvisible; }
13493 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13495 // The caller owns |aAsyncCause| here.
13496 void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
13497 const nsAString& aFunctionName,
13498 const nsAString& aFilename,
13499 const uint32_t aLineNumber,
13500 JS::Handle<JS::Value> aAsyncStack,
13501 const char* aAsyncCause) {
13502 // If first start, mark interval start.
13503 if (mJSRunToCompletionDepth == 0 && TimelineConsumers::HasConsumer(this)) {
13504 TimelineConsumers::AddMarkerForDocShell(
13505 this, mozilla::MakeUnique<JavascriptTimelineMarker>(
13506 aReason, aFunctionName, aFilename, aLineNumber,
13507 MarkerTracingType::START, aAsyncStack, aAsyncCause));
13510 mJSRunToCompletionDepth++;
13513 void nsDocShell::NotifyJSRunToCompletionStop() {
13514 mJSRunToCompletionDepth--;
13516 // If last stop, mark interval end.
13517 if (mJSRunToCompletionDepth == 0 && TimelineConsumers::HasConsumer(this)) {
13518 TimelineConsumers::AddMarkerForDocShell(this, "Javascript",
13519 MarkerTracingType::END);
13523 /* static */
13524 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13525 const nsString& aKeyword) {
13526 if (aProvider.IsEmpty()) {
13527 return;
13529 nsresult rv;
13530 nsCOMPtr<nsISupportsString> isupportsString =
13531 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13532 NS_ENSURE_SUCCESS_VOID(rv);
13534 rv = isupportsString->SetData(aProvider);
13535 NS_ENSURE_SUCCESS_VOID(rv);
13537 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13538 if (obsSvc) {
13539 // Note that "keyword-search" refers to a search via the url
13540 // bar, not a bookmarks keyword search.
13541 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13545 NS_IMETHODIMP
13546 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13547 bool* aShouldIntercept) {
13548 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13549 aShouldIntercept);
13552 NS_IMETHODIMP
13553 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13554 return mInterceptController->ChannelIntercepted(aChannel);
13557 bool nsDocShell::InFrameSwap() {
13558 RefPtr<nsDocShell> shell = this;
13559 do {
13560 if (shell->mInFrameSwap) {
13561 return true;
13563 shell = shell->GetInProcessParentDocshell();
13564 } while (shell);
13565 return false;
13568 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13569 return std::move(mInitialClientSource);
13572 NS_IMETHODIMP
13573 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13574 if (!NS_SUCCEEDED(EnsureEditorData())) {
13575 return NS_ERROR_FAILURE;
13578 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13579 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13582 NS_IMETHODIMP
13583 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13584 *aBrowserChild = GetBrowserChild().take();
13585 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13588 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13589 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13590 return tc.forget();
13593 nsCommandManager* nsDocShell::GetCommandManager() {
13594 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13595 return mCommandManager;
13598 NS_IMETHODIMP_(void)
13599 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13600 mBrowsingContext->GetOriginAttributes(aAttrs);
13603 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13604 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13605 return docShell->GetHTMLEditorInternal();
13608 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13609 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13610 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13613 #define MATRIX_LENGTH 20
13615 NS_IMETHODIMP
13616 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13617 if (aMatrix.Length() == MATRIX_LENGTH) {
13618 mColorMatrix.reset(new gfx::Matrix5x4());
13619 static_assert(
13620 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13621 "Size mismatch for our memcpy");
13622 memcpy(mColorMatrix->components, aMatrix.Elements(),
13623 sizeof(mColorMatrix->components));
13624 } else if (aMatrix.Length() == 0) {
13625 mColorMatrix.reset();
13626 } else {
13627 return NS_ERROR_INVALID_ARG;
13630 PresShell* presShell = GetPresShell();
13631 if (!presShell) {
13632 return NS_ERROR_FAILURE;
13635 nsIFrame* frame = presShell->GetRootFrame();
13636 if (!frame) {
13637 return NS_ERROR_FAILURE;
13640 frame->SchedulePaint();
13642 return NS_OK;
13645 NS_IMETHODIMP
13646 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13647 if (mColorMatrix) {
13648 aMatrix.SetLength(MATRIX_LENGTH);
13649 static_assert(
13650 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13651 "Size mismatch for our memcpy");
13652 memcpy(aMatrix.Elements(), mColorMatrix->components,
13653 MATRIX_LENGTH * sizeof(float));
13656 return NS_OK;
13659 #undef MATRIX_LENGTH
13661 NS_IMETHODIMP
13662 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13663 *aForceReload = IsForceReloading();
13664 return NS_OK;
13667 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13669 NS_IMETHODIMP
13670 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13671 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13672 return NS_OK;
13675 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13677 bool nsDocShell::GetIsAttemptingToNavigate() {
13678 // XXXbz the document.open spec says to abort even if there's just a
13679 // queued navigation task, sort of. It's not clear whether browsers
13680 // actually do that, and we didn't use to do it, so for now let's
13681 // not do that.
13682 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13683 if (mDocumentRequest) {
13684 // There's definitely a navigation in progress.
13685 return true;
13688 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13689 // until the script runs, which means they're not sending loadgroup
13690 // notifications and hence not getting set as mDocumentRequest. Look through
13691 // our loadgroup for document-level javascript: loads.
13692 if (!mLoadGroup) {
13693 return false;
13696 nsCOMPtr<nsISimpleEnumerator> requests;
13697 mLoadGroup->GetRequests(getter_AddRefs(requests));
13698 bool hasMore = false;
13699 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13700 nsCOMPtr<nsISupports> elem;
13701 requests->GetNext(getter_AddRefs(elem));
13702 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13703 if (!scriptChannel) {
13704 continue;
13707 if (scriptChannel->GetIsDocumentLoad()) {
13708 // This is a javascript: load that might lead to a new document,
13709 // hence a navigation.
13710 return true;
13714 return mCheckingSessionHistory;
13717 void nsDocShell::SetLoadingSessionHistoryInfo(
13718 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
13719 bool aNeedToReportActiveAfterLoadingBecomesActive) {
13720 // FIXME Would like to assert this, but can't yet.
13721 // MOZ_ASSERT(!mLoadingEntry);
13722 MOZ_LOG(gSHLog, LogLevel::Debug,
13723 ("Setting the loading entry on nsDocShell %p to %s", this,
13724 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13725 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13726 mNeedToReportActiveAfterLoadingBecomesActive =
13727 aNeedToReportActiveAfterLoadingBecomesActive;
13730 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13731 uint32_t aCacheKey,
13732 nsIURI* aPreviousURI) {
13733 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13735 MOZ_LOG(gSHLog, LogLevel::Debug,
13736 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13738 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
13739 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13740 mActiveEntryIsLoadingFromSessionHistory =
13741 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13742 if (mLoadingEntry) {
13743 MOZ_LOG(gSHLog, LogLevel::Debug,
13744 ("Moving the loading entry to the active entry on nsDocShell %p "
13745 "to %s",
13746 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13747 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13748 mLoadingEntry.swap(loadingEntry);
13749 if (!mActiveEntryIsLoadingFromSessionHistory) {
13750 if (mNeedToReportActiveAfterLoadingBecomesActive) {
13751 // Needed to pass various history length WPTs.
13752 mBrowsingContext->SetActiveSessionHistoryEntry(
13753 mozilla::Nothing(), mActiveEntry.get(), mLoadType,
13754 /* aUpdatedCacheKey = */ 0, false);
13756 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13759 mNeedToReportActiveAfterLoadingBecomesActive = false;
13761 if (mActiveEntry) {
13762 if (aCacheKey != 0) {
13763 mActiveEntry->SetCacheKey(aCacheKey);
13765 MOZ_ASSERT(loadingEntry);
13766 uint32_t loadType =
13767 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13769 if (loadingEntry->mLoadId != UINT64_MAX) {
13770 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13771 // does require a non-null uri if this is for a refresh load of the same
13772 // URI, but in that case mCurrentURI won't be null here.
13773 mBrowsingContext->SessionHistoryCommit(
13774 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
13775 aPersist, false, aExpired, aCacheKey);
13780 static bool IsFaviconLoad(nsIRequest* aRequest) {
13781 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13782 if (!channel) {
13783 return false;
13786 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13787 return li && li->InternalContentPolicyType() ==
13788 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13791 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13792 nsIRequest* aRequest) {
13793 // Ignore favicon loads, they don't need to block caching.
13794 if (IsFaviconLoad(aRequest)) {
13795 return;
13798 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13800 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13802 if (mBrowsingContext->GetCurrentWindowContext()) {
13803 // We have three states: no request, one request with an id and
13804 // eiher one request without an id or multiple requests. Nothing() is no
13805 // request, Some(non-zero) is one request with an id and Some(0) is one
13806 // request without an id or multiple requests.
13807 Maybe<uint64_t> singleChannelId;
13808 if (mRequestForBlockingFromBFCacheCount > 1) {
13809 singleChannelId = Some(0);
13810 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13811 nsCOMPtr<nsIIdentChannel> identChannel;
13812 if (aStartRequest) {
13813 identChannel = do_QueryInterface(aRequest);
13814 } else {
13815 // aChannel is the channel that's being removed, but we need to check if
13816 // the remaining channel in the loadgroup has an id.
13817 nsCOMPtr<nsISimpleEnumerator> requests;
13818 mLoadGroup->GetRequests(getter_AddRefs(requests));
13819 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13820 if (!IsFaviconLoad(request) &&
13821 !!(identChannel = do_QueryInterface(request))) {
13822 break;
13827 if (identChannel) {
13828 singleChannelId = Some(identChannel->ChannelId());
13829 } else {
13830 singleChannelId = Some(0);
13832 } else {
13833 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13834 singleChannelId = Nothing();
13837 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13838 nsAutoCString uri("[no uri]");
13839 if (mCurrentURI) {
13840 uri = mCurrentURI->GetSpecOrDefault();
13842 if (singleChannelId.isNothing()) {
13843 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13844 ("Loadgroup for %s doesn't have any requests relevant for "
13845 "blocking BFCache",
13846 uri.get()));
13847 } else if (singleChannelId.value() == 0) {
13848 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13849 ("Loadgroup for %s has multiple requests relevant for blocking "
13850 "BFCache",
13851 uri.get()));
13852 } else {
13853 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13854 ("Loadgroup for %s has one request with id %" PRIu64
13855 " relevant for blocking BFCache",
13856 uri.get(), singleChannelId.value()));
13860 if (mSingleChannelId != singleChannelId) {
13861 mSingleChannelId = singleChannelId;
13862 WindowGlobalChild* wgc =
13863 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13864 if (wgc) {
13865 wgc->SendSetSingleChannelId(singleChannelId);
13871 NS_IMETHODIMP
13872 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13873 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13874 nsAutoCString uri("[no uri]");
13875 if (mCurrentURI) {
13876 uri = mCurrentURI->GetSpecOrDefault();
13878 nsAutoCString name;
13879 aRequest->GetName(name);
13880 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13881 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13883 RecordSingleChannelId(true, aRequest);
13884 return nsDocLoader::OnStartRequest(aRequest);
13887 NS_IMETHODIMP
13888 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13889 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13890 nsAutoCString uri("[no uri]");
13891 if (mCurrentURI) {
13892 uri = mCurrentURI->GetSpecOrDefault();
13894 nsAutoCString name;
13895 aRequest->GetName(name);
13896 MOZ_LOG(
13897 gSHIPBFCacheLog, LogLevel::Verbose,
13898 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13900 RecordSingleChannelId(false, aRequest);
13901 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13904 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13905 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13907 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13908 nsCOMPtr<nsISimpleEnumerator> requests;
13909 mLoadGroup->GetRequests(getter_AddRefs(requests));
13910 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13911 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13912 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13913 static_cast<DocumentChannelChild*>(channel.get())
13914 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13917 mChannelToDisconnectOnPageHide = 0;