Bug 1773094 - Remove security.alternate_certificate_error_page pref r=keeler,geckovie...
[gecko.git] / docshell / base / nsDocShell.cpp
blob543fa3af44aa8110b8a581414514d628089f6417
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 "nsGlobalWindow.h"
209 #include "nsJSEnvironment.h"
210 #include "nsNetCID.h"
211 #include "nsNetUtil.h"
212 #include "nsObjectLoadingContent.h"
213 #include "nsPingListener.h"
214 #include "nsPoint.h"
215 #include "nsQueryObject.h"
216 #include "nsQueryActor.h"
217 #include "nsRect.h"
218 #include "nsRefreshTimer.h"
219 #include "nsSandboxFlags.h"
220 #include "nsSHEntry.h"
221 #include "nsSHistory.h"
222 #include "nsSHEntry.h"
223 #include "nsStructuredCloneContainer.h"
224 #include "nsSubDocumentFrame.h"
225 #include "nsURILoader.h"
226 #include "nsURLHelper.h"
227 #include "nsView.h"
228 #include "nsViewManager.h"
229 #include "nsViewSourceHandler.h"
230 #include "nsWebBrowserFind.h"
231 #include "nsWhitespaceTokenizer.h"
232 #include "nsWidgetsCID.h"
233 #include "nsXULAppAPI.h"
235 #include "ThirdPartyUtil.h"
236 #include "GeckoProfiler.h"
237 #include "mozilla/NullPrincipal.h"
238 #include "Navigator.h"
239 #include "prenv.h"
240 #include "mozilla/ipc/URIUtils.h"
241 #include "sslerr.h"
242 #include "mozpkix/pkix.h"
243 #include "NSSErrorsService.h"
245 #include "timeline/JavascriptTimelineMarker.h"
246 #include "nsDocShellTelemetryUtils.h"
248 #ifdef MOZ_PLACES
249 # include "nsIFaviconService.h"
250 # include "mozIPlacesPendingOperation.h"
251 #endif
253 #if NS_PRINT_PREVIEW
254 # include "nsIDocumentViewerPrint.h"
255 # include "nsIWebBrowserPrint.h"
256 #endif
258 using namespace mozilla;
259 using namespace mozilla::dom;
260 using namespace mozilla::net;
262 using mozilla::ipc::Endpoint;
264 // Threshold value in ms for META refresh based redirects
265 #define REFRESH_REDIRECT_TIMER 15000
267 static mozilla::LazyLogModule gCharsetMenuLog("CharsetMenu");
269 #define LOGCHARSETMENU(args) \
270 MOZ_LOG(gCharsetMenuLog, mozilla::LogLevel::Debug, args)
272 #ifdef DEBUG
273 unsigned long nsDocShell::gNumberOfDocShells = 0;
274 static uint64_t gDocshellIDCounter = 0;
276 static mozilla::LazyLogModule gDocShellLog("nsDocShell");
277 static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
278 "DocShellAndDOMWindowLeak");
279 #endif
280 static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
281 extern mozilla::LazyLogModule gPageCacheLog;
282 mozilla::LazyLogModule gSHLog("SessionHistory");
283 extern mozilla::LazyLogModule gSHIPBFCacheLog;
285 const char kAppstringsBundleURL[] =
286 "chrome://global/locale/appstrings.properties";
288 static bool IsTopLevelDoc(BrowsingContext* aBrowsingContext,
289 nsILoadInfo* aLoadInfo) {
290 MOZ_ASSERT(aBrowsingContext);
291 MOZ_ASSERT(aLoadInfo);
293 if (aLoadInfo->GetExternalContentPolicyType() !=
294 ExtContentPolicy::TYPE_DOCUMENT) {
295 return false;
298 return aBrowsingContext->IsTopContent();
301 // True if loading for top level document loading in active tab.
302 static bool IsUrgentStart(BrowsingContext* aBrowsingContext,
303 nsILoadInfo* aLoadInfo, uint32_t aLoadType) {
304 MOZ_ASSERT(aBrowsingContext);
305 MOZ_ASSERT(aLoadInfo);
307 if (!IsTopLevelDoc(aBrowsingContext, aLoadInfo)) {
308 return false;
311 if (aLoadType &
312 (nsIDocShell::LOAD_CMD_NORMAL | nsIDocShell::LOAD_CMD_HISTORY)) {
313 return true;
316 return aBrowsingContext->IsActive();
319 nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
320 uint64_t aContentWindowID)
321 : nsDocLoader(true),
322 mContentWindowID(aContentWindowID),
323 mBrowsingContext(aBrowsingContext),
324 mParentCharset(nullptr),
325 mTreeOwner(nullptr),
326 mScrollbarPref(ScrollbarPreference::Auto),
327 mCharsetReloadState(eCharsetReloadInit),
328 mParentCharsetSource(0),
329 mFrameMargins(-1, -1),
330 mItemType(aBrowsingContext->IsContent() ? typeContent : typeChrome),
331 mPreviousEntryIndex(-1),
332 mLoadedEntryIndex(-1),
333 mBusyFlags(BUSY_FLAGS_NONE),
334 mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
335 mLoadType(0),
336 mFailedLoadType(0),
337 mJSRunToCompletionDepth(0),
338 mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
339 mChannelToDisconnectOnPageHide(0),
340 mCreatingDocument(false),
341 #ifdef DEBUG
342 mInEnsureScriptEnv(false),
343 #endif
344 mInitialized(false),
345 mAllowSubframes(true),
346 mAllowMetaRedirects(true),
347 mAllowImages(true),
348 mAllowMedia(true),
349 mAllowDNSPrefetch(true),
350 mAllowWindowControl(true),
351 mCSSErrorReportingEnabled(false),
352 mAllowAuth(mItemType == typeContent),
353 mAllowKeywordFixup(false),
354 mDisableMetaRefreshWhenInactive(false),
355 mWindowDraggingAllowed(false),
356 mInFrameSwap(false),
357 mFiredUnloadEvent(false),
358 mEODForCurrentDocument(false),
359 mURIResultedInDocument(false),
360 mIsBeingDestroyed(false),
361 mIsExecutingOnLoadHandler(false),
362 mSavingOldViewer(false),
363 mInvisible(false),
364 mHasLoadedNonBlankURI(false),
365 mBlankTiming(false),
366 mTitleValidForCurrentURI(false),
367 mWillChangeProcess(false),
368 mIsNavigating(false),
369 mForcedAutodetection(false),
370 mCheckingSessionHistory(false),
371 mNeedToReportActiveAfterLoadingBecomesActive(false) {
372 // If no outer window ID was provided, generate a new one.
373 if (aContentWindowID == 0) {
374 mContentWindowID = nsContentUtils::GenerateWindowId();
377 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
379 #ifdef DEBUG
380 mDocShellID = gDocshellIDCounter++;
381 // We're counting the number of |nsDocShells| to help find leaks
382 ++gNumberOfDocShells;
383 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
384 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this,
385 gNumberOfDocShells, getpid(), mDocShellID));
386 #endif
389 nsDocShell::~nsDocShell() {
390 MOZ_ASSERT(!mObserved);
392 // Avoid notifying observers while we're in the dtor.
393 mIsBeingDestroyed = true;
395 Destroy();
397 if (mContentViewer) {
398 mContentViewer->Close(nullptr);
399 mContentViewer->Destroy();
400 mContentViewer = nullptr;
403 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
405 #ifdef DEBUG
406 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
407 nsAutoCString url;
408 if (mLastOpenedURI) {
409 url = mLastOpenedURI->GetSpecOrDefault();
411 // Data URLs can be very long, so truncate to avoid flooding the log.
412 const uint32_t maxURLLength = 1000;
413 if (url.Length() > maxURLLength) {
414 url.Truncate(maxURLLength);
418 // We're counting the number of |nsDocShells| to help find leaks
419 --gNumberOfDocShells;
420 MOZ_LOG(
421 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
422 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n",
423 (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get()));
425 #endif
428 bool nsDocShell::Initialize() {
429 if (mInitialized) {
430 // We've already been initialized.
431 return true;
434 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
435 "Unexpected item type in docshell");
437 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
438 mInitialized = true;
440 mDisableMetaRefreshWhenInactive =
441 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
442 mDisableMetaRefreshWhenInactive);
444 if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) {
445 const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
446 : NS_CHROME_WEBNAVIGATION_CREATE;
447 serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr);
450 return true;
453 /* static */
454 already_AddRefed<nsDocShell> nsDocShell::Create(
455 BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
456 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
458 nsresult rv;
459 RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
461 // Initialize the underlying nsDocLoader.
462 rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
463 if (NS_WARN_IF(NS_FAILED(rv))) {
464 return nullptr;
467 // Create our ContentListener
468 ds->mContentListener = new nsDSURIContentListener(ds);
470 // We enable if we're in the parent process in order to support non-e10s
471 // configurations.
472 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
473 // constructor.
474 if (XRE_IsParentProcess()) {
475 ds->mInterceptController = new ServiceWorkerInterceptController();
478 // We want to hold a strong ref to the loadgroup, so it better hold a weak
479 // ref to us... use an InterfaceRequestorProxy to do this.
480 nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
481 ds->mLoadGroup->SetNotificationCallbacks(proxy);
483 // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
484 // It could be nice to directly set up our DocLoader tree?
485 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
486 if (NS_WARN_IF(NS_FAILED(rv))) {
487 return nullptr;
490 // Add |ds| as a progress listener to itself. A little weird, but simpler
491 // than reproducing all the listener-notification logic in overrides of the
492 // various methods via which nsDocLoader can be notified. Note that this
493 // holds an nsWeakPtr to |ds|, so it's ok.
494 rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
495 nsIWebProgress::NOTIFY_STATE_NETWORK |
496 nsIWebProgress::NOTIFY_LOCATION);
497 if (NS_WARN_IF(NS_FAILED(rv))) {
498 return nullptr;
501 // If our BrowsingContext has private browsing enabled, update the number of
502 // private browsing docshells.
503 if (aBrowsingContext->UsePrivateBrowsing()) {
504 ds->NotifyPrivateBrowsingChanged();
507 // If our parent window is present in this process, set up our parent now.
508 RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext();
509 if (parentWC && parentWC->IsInProcess()) {
510 // If we don't have a parent element anymore, we can't finish this load!
511 // How'd we get here?
512 RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement();
513 if (!parentElement) {
514 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
515 return nullptr;
518 // We have an in-process parent window, but don't have a parent nsDocShell?
519 // How'd we get here!
520 nsCOMPtr<nsIDocShell> parentShell =
521 parentElement->OwnerDoc()->GetDocShell();
522 if (!parentShell) {
523 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
524 return nullptr;
526 parentShell->AddChild(ds);
529 // Make |ds| the primary DocShell for the given context.
530 aBrowsingContext->SetDocShell(ds);
532 // Set |ds| default load flags on load group.
533 ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
535 if (XRE_IsParentProcess()) {
536 aBrowsingContext->Canonical()->MaybeAddAsProgressListener(ds);
539 return ds.forget();
542 void nsDocShell::DestroyChildren() {
543 for (auto* child : mChildList.ForwardRange()) {
544 nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child);
545 NS_ASSERTION(shell, "docshell has null child");
547 if (shell) {
548 shell->SetTreeOwner(nullptr);
552 nsDocLoader::DestroyChildren();
555 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
556 mScriptGlobal, mInitialClientSource,
557 mBrowsingContext,
558 mChromeEventHandler)
560 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
561 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
563 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
564 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
565 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
566 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
567 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
568 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
569 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
570 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
571 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
572 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
573 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
574 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
575 mInterceptController)
576 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
578 NS_IMETHODIMP
579 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
580 MOZ_ASSERT(aSink, "null out param");
582 *aSink = nullptr;
584 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
585 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
586 *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
587 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
588 *aSink = mContentListener;
589 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
590 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
591 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
592 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
593 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
594 NS_SUCCEEDED(EnsureScriptEnvironment())) {
595 return mScriptGlobal->QueryInterface(aIID, aSink);
596 } else if (aIID.Equals(NS_GET_IID(Document)) &&
597 NS_SUCCEEDED(EnsureContentViewer())) {
598 RefPtr<Document> doc = mContentViewer->GetDocument();
599 doc.forget(aSink);
600 return *aSink ? NS_OK : NS_NOINTERFACE;
601 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
602 NS_SUCCEEDED(EnsureScriptEnvironment())) {
603 nsresult rv;
604 nsCOMPtr<nsIWindowWatcher> wwatch =
605 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
606 NS_ENSURE_SUCCESS(rv, rv);
608 // Get the an auth prompter for our window so that the parenting
609 // of the dialogs works as it should when using tabs.
610 nsIPrompt* prompt;
611 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
612 NS_ENSURE_SUCCESS(rv, rv);
614 *aSink = prompt;
615 return NS_OK;
616 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
617 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
618 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
619 ? NS_OK
620 : NS_NOINTERFACE;
621 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
622 // This is deprecated, you should instead directly get
623 // ChildSHistory from the browsing context.
624 MOZ_DIAGNOSTIC_ASSERT(
625 false, "Do not try to get a nsISHistory interface from nsIDocShell");
626 return NS_NOINTERFACE;
627 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
628 nsresult rv = EnsureFind();
629 if (NS_FAILED(rv)) {
630 return rv;
633 *aSink = mFind;
634 NS_ADDREF((nsISupports*)*aSink);
635 return NS_OK;
636 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
637 if (PresShell* presShell = GetPresShell()) {
638 return presShell->QueryInterface(aIID, aSink);
640 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
641 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
642 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
643 if (NS_SUCCEEDED(rv) && treeOwner) {
644 return treeOwner->QueryInterface(aIID, aSink);
646 } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
647 *aSink = GetBrowserChild().take();
648 return *aSink ? NS_OK : NS_ERROR_FAILURE;
649 } else {
650 return nsDocLoader::GetInterface(aIID, aSink);
653 NS_IF_ADDREF(((nsISupports*)*aSink));
654 return *aSink ? NS_OK : NS_NOINTERFACE;
657 NS_IMETHODIMP
658 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
659 // Note: this gets called fairly early (before a pageload actually starts).
660 // We could probably defer this even longer.
661 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
662 static_cast<BrowserChild*>(browserChild.get())
663 ->SetCancelContentJSEpoch(aEpoch);
664 return NS_OK;
667 nsresult nsDocShell::CheckDisallowedJavascriptLoad(
668 nsDocShellLoadState* aLoadState) {
669 if (!net::SchemeIsJavascript(aLoadState->URI())) {
670 return NS_OK;
673 if (nsCOMPtr<nsIPrincipal> targetPrincipal =
674 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
675 if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) {
676 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
678 return NS_OK;
680 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
683 NS_IMETHODIMP
684 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
685 return LoadURI(aLoadState, aSetNavigating, false);
688 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
689 bool aSetNavigating,
690 bool aContinueHandlingSubframeHistory) {
691 MOZ_ASSERT(aLoadState, "Must have a valid load state!");
692 // NOTE: This comparison between what appears to be internal/external load
693 // flags is intentional, as it's ensuring that the caller isn't using any of
694 // the flags reserved for implementations by the `nsIWebNavigation` interface.
695 // In the future, this check may be dropped.
696 MOZ_ASSERT(
697 (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
698 "Should not have these flags set");
699 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
700 "Targeting doesn't occur until InternalLoad");
702 if (!aLoadState->TriggeringPrincipal()) {
703 MOZ_ASSERT(false, "LoadURI must have a triggering principal");
704 return NS_ERROR_FAILURE;
707 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
709 bool oldIsNavigating = mIsNavigating;
710 auto cleanupIsNavigating =
711 MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
712 if (aSetNavigating) {
713 mIsNavigating = true;
716 PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden;
717 if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) {
718 popupState = PopupBlocker::openAllowed;
719 // If we allow popups as part of the navigation, ensure we fake a user
720 // interaction, so that popups can, in fact, be allowed to open.
721 if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) {
722 wc->NotifyUserGestureActivation();
726 AutoPopupStatePusher statePusher(popupState);
728 if (aLoadState->GetCancelContentJSEpoch().isSome()) {
729 SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
732 // Note: we allow loads to get through here even if mFiredUnloadEvent is
733 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
734 // so we pass false as the second parameter to IsNavigationAllowed.
735 // However, we don't allow the page to change location *in the middle of*
736 // firing beforeunload, so we do need to check if *beforeunload* is currently
737 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
738 if (!IsNavigationAllowed(true, false)) {
739 return NS_OK; // JS may not handle returning of an error code
742 nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
743 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) {
744 defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
745 } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) {
746 defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
749 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags));
751 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
752 mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
753 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
756 // LoadType used to be set to a default value here, if no LoadInfo/LoadState
757 // object was passed in. That functionality has been removed as of bug
758 // 1492648. LoadType should now be set up by the caller at the time they
759 // create their nsDocShellLoadState object to pass into LoadURI.
761 MOZ_LOG(
762 gDocShellLeakLog, LogLevel::Debug,
763 ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
764 aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
766 if ((!aLoadState->LoadIsFromSessionHistory() &&
767 !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
768 LOAD_FLAGS_REPLACE_HISTORY)) ||
769 aContinueHandlingSubframeHistory) {
770 // This is possibly a subframe, so handle it accordingly.
772 // If history exists, it will be loaded into the aLoadState object, and the
773 // LoadType will be changed.
774 if (MaybeHandleSubframeHistory(aLoadState,
775 aContinueHandlingSubframeHistory)) {
776 // MaybeHandleSubframeHistory returns true if we need to continue loading
777 // asynchronously.
778 return NS_OK;
782 if (aLoadState->LoadIsFromSessionHistory()) {
783 MOZ_LOG(gSHLog, LogLevel::Debug,
784 ("nsDocShell[%p]: loading from session history", this));
786 if (!mozilla::SessionHistoryInParent()) {
787 return LoadHistoryEntry(aLoadState->SHEntry(), aLoadState->LoadType(),
788 aLoadState->HasValidUserGestureActivation());
791 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
792 return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(),
793 aLoadState->LoadType(),
794 aLoadState->HasValidUserGestureActivation());
797 // On history navigation via Back/Forward buttons, don't execute
798 // automatic JavaScript redirection such as |location.href = ...| or
799 // |window.open()|
801 // LOAD_NORMAL: window.open(...) etc.
802 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
803 if ((aLoadState->LoadType() == LOAD_NORMAL ||
804 aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
805 ShouldBlockLoadingForBackButton()) {
806 return NS_OK;
809 BrowsingContext::Type bcType = mBrowsingContext->GetType();
811 // Set up the inheriting principal in LoadState.
812 nsresult rv = aLoadState->SetupInheritingPrincipal(
813 bcType, mBrowsingContext->OriginAttributesRef());
814 NS_ENSURE_SUCCESS(rv, rv);
816 rv = aLoadState->SetupTriggeringPrincipal(
817 mBrowsingContext->OriginAttributesRef());
818 NS_ENSURE_SUCCESS(rv, rv);
820 aLoadState->CalculateLoadURIFlags();
822 MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
823 "Typehint should be null when calling InternalLoad from LoadURI");
824 MOZ_ASSERT(aLoadState->FileName().IsVoid(),
825 "FileName should be null when calling InternalLoad from LoadURI");
826 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(),
827 "Shouldn't be loading from an entry when calling InternalLoad "
828 "from LoadURI");
830 // If we have a system triggering principal, we can assume that this load was
831 // triggered by some UI in the browser chrome, such as the URL bar or
832 // bookmark bar. This should count as a user interaction for the current sh
833 // entry, so that the user may navigate back to the current entry, from the
834 // entry that is going to be added as part of this load.
835 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
836 aLoadState->TriggeringPrincipal();
837 if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) {
838 if (mozilla::SessionHistoryInParent()) {
839 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
840 if (topWc && !topWc->IsDiscarded()) {
841 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
843 } else {
844 bool oshe = false;
845 nsCOMPtr<nsISHEntry> currentSHEntry;
846 GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe);
847 if (currentSHEntry) {
848 currentSHEntry->SetHasUserInteraction(true);
853 rv = InternalLoad(aLoadState);
854 NS_ENSURE_SUCCESS(rv, rv);
856 if (aLoadState->GetOriginalURIString().isSome()) {
857 // Save URI string in case it's needed later when
858 // sending to search engine service in EndPageLoad()
859 mOriginalUriString = *aLoadState->GetOriginalURIString();
862 return NS_OK;
865 bool nsDocShell::IsLoadingFromSessionHistory() {
866 return mActiveEntryIsLoadingFromSessionHistory;
869 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
870 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
871 // detect Cancel calls.
872 class StopDetector final : public nsIRequest {
873 public:
874 StopDetector() = default;
876 NS_DECL_ISUPPORTS
877 NS_DECL_NSIREQUEST
879 bool Canceled() { return mCanceled; }
881 private:
882 ~StopDetector() = default;
884 bool mCanceled = false;
887 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
889 NS_IMETHODIMP
890 StopDetector::GetName(nsACString& aResult) {
891 aResult.AssignLiteral("about:stop-detector");
892 return NS_OK;
895 NS_IMETHODIMP
896 StopDetector::IsPending(bool* aRetVal) {
897 *aRetVal = true;
898 return NS_OK;
901 NS_IMETHODIMP
902 StopDetector::GetStatus(nsresult* aStatus) {
903 *aStatus = NS_OK;
904 return NS_OK;
907 NS_IMETHODIMP StopDetector::SetCanceledReason(const nsACString& aReason) {
908 return SetCanceledReasonImpl(aReason);
911 NS_IMETHODIMP StopDetector::GetCanceledReason(nsACString& aReason) {
912 return GetCanceledReasonImpl(aReason);
915 NS_IMETHODIMP StopDetector::CancelWithReason(nsresult aStatus,
916 const nsACString& aReason) {
917 return CancelWithReasonImpl(aStatus, aReason);
920 NS_IMETHODIMP
921 StopDetector::Cancel(nsresult aStatus) {
922 mCanceled = true;
923 return NS_OK;
926 NS_IMETHODIMP
927 StopDetector::Suspend(void) { return NS_OK; }
928 NS_IMETHODIMP
929 StopDetector::Resume(void) { return NS_OK; }
931 NS_IMETHODIMP
932 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
933 *aLoadGroup = nullptr;
934 return NS_OK;
937 NS_IMETHODIMP
938 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
940 NS_IMETHODIMP
941 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
942 *aLoadFlags = nsIRequest::LOAD_NORMAL;
943 return NS_OK;
946 NS_IMETHODIMP
947 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
948 return GetTRRModeImpl(aTRRMode);
951 NS_IMETHODIMP
952 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
953 return SetTRRModeImpl(aTRRMode);
956 NS_IMETHODIMP
957 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
959 bool nsDocShell::MaybeHandleSubframeHistory(
960 nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
961 // First, verify if this is a subframe.
962 // Note, it is ok to rely on docshell here and not browsing context since when
963 // an iframe is created, it has first in-process docshell.
964 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
965 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
966 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
968 if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
969 if (mBrowsingContext && mBrowsingContext->IsTop()) {
970 // This is the root docshell. If we got here while
971 // executing an onLoad Handler,this load will not go
972 // into session history.
973 // XXX Why is this code in a method which deals with iframes!
974 if (aLoadState->IsFormSubmission()) {
975 #ifdef DEBUG
976 if (!mEODForCurrentDocument) {
977 const MaybeDiscarded<BrowsingContext>& targetBC =
978 aLoadState->TargetBrowsingContext();
979 MOZ_ASSERT_IF(GetBrowsingContext() == targetBC.get(),
980 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
982 #endif
983 } else {
984 bool inOnLoadHandler = false;
985 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
986 if (inOnLoadHandler) {
987 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
991 return false;
994 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
995 * loaded through a history mechanism, then get the SH entry for the child
996 * from the parent. This is done to restore frameset navigation while going
997 * back/forward. If the parent was loaded through any other loadType, set the
998 * child's loadType too accordingly, so that session history does not get
999 * confused.
1002 // Get the parent's load type
1003 uint32_t parentLoadType;
1004 parentDS->GetLoadType(&parentLoadType);
1006 if (!aContinueHandlingSubframeHistory) {
1007 if (mozilla::SessionHistoryInParent()) {
1008 if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
1009 !GetCreatedDynamically()) {
1010 if (XRE_IsContentProcess()) {
1011 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
1012 nsCOMPtr<nsILoadGroup> loadGroup;
1013 GetLoadGroup(getter_AddRefs(loadGroup));
1014 if (contentChild && loadGroup && !mCheckingSessionHistory) {
1015 RefPtr<Document> parentDoc = parentDS->GetDocument();
1016 parentDoc->BlockOnload();
1017 RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
1018 Maybe<uint64_t> currentLoadIdentifier =
1019 mBrowsingContext->GetCurrentLoadIdentifier();
1020 RefPtr<nsDocShellLoadState> loadState = aLoadState;
1021 bool isNavigating = mIsNavigating;
1022 RefPtr<StopDetector> stopDetector = new StopDetector();
1023 loadGroup->AddRequest(stopDetector, nullptr);
1024 // Need to set mCheckingSessionHistory so that
1025 // GetIsAttemptingToNavigate() returns true.
1026 mCheckingSessionHistory = true;
1028 auto resolve =
1029 [currentLoadIdentifier, browsingContext, parentDoc, loadState,
1030 isNavigating, loadGroup, stopDetector](
1031 mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
1032 RefPtr<nsDocShell> docShell =
1033 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1034 auto unblockParent = MakeScopeExit(
1035 [loadGroup, stopDetector, parentDoc, docShell]() {
1036 if (docShell) {
1037 docShell->mCheckingSessionHistory = false;
1039 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1040 parentDoc->UnblockOnload(false);
1043 if (!docShell || !docShell->mCheckingSessionHistory) {
1044 return;
1047 if (stopDetector->Canceled()) {
1048 return;
1050 if (currentLoadIdentifier ==
1051 browsingContext->GetCurrentLoadIdentifier() &&
1052 aResult.isSome()) {
1053 loadState->SetLoadingSessionHistoryInfo(aResult.value());
1054 // This is an initial subframe load from the session
1055 // history, index doesn't need to be updated.
1056 loadState->SetLoadIsFromSessionHistory(0, false);
1059 // We got the results back from the parent process, call
1060 // LoadURI again with the possibly updated data.
1061 docShell->LoadURI(loadState, isNavigating, true);
1063 auto reject = [loadGroup, stopDetector, browsingContext,
1064 parentDoc](mozilla::ipc::ResponseRejectReason) {
1065 RefPtr<nsDocShell> docShell =
1066 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1067 if (docShell) {
1068 docShell->mCheckingSessionHistory = false;
1070 // In practise reject shouldn't be called ever.
1071 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1072 parentDoc->UnblockOnload(false);
1074 contentChild->SendGetLoadingSessionHistoryInfoFromParent(
1075 mBrowsingContext, std::move(resolve), std::move(reject));
1076 return true;
1078 } else {
1079 Maybe<LoadingSessionHistoryInfo> info;
1080 mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
1081 info);
1082 if (info.isSome()) {
1083 aLoadState->SetLoadingSessionHistoryInfo(info.value());
1084 // This is an initial subframe load from the session
1085 // history, index doesn't need to be updated.
1086 aLoadState->SetLoadIsFromSessionHistory(0, false);
1090 } else {
1091 // Get the ShEntry for the child from the parent
1092 nsCOMPtr<nsISHEntry> currentSH;
1093 bool oshe = false;
1094 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1095 bool dynamicallyAddedChild = GetCreatedDynamically();
1097 if (!dynamicallyAddedChild && !oshe && currentSH) {
1098 // Only use the old SHEntry, if we're sure enough that
1099 // it wasn't originally for some other frame.
1100 nsCOMPtr<nsISHEntry> shEntry;
1101 currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
1102 mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry));
1103 if (shEntry) {
1104 aLoadState->SetSHEntry(shEntry);
1110 // Make some decisions on the child frame's loadType based on the
1111 // parent's loadType, if the subframe hasn't loaded anything into it.
1113 // In some cases privileged scripts may try to get the DOMWindow
1114 // reference of this docshell before the loading starts, causing the
1115 // initial about:blank content viewer being created and mCurrentURI being
1116 // set. To handle this case we check if mCurrentURI is about:blank and
1117 // currentSHEntry is null.
1118 bool oshe = false;
1119 nsCOMPtr<nsISHEntry> currentChildEntry;
1120 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
1122 if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry ||
1123 mLoadingEntry || mActiveEntry)) {
1124 // This is a pre-existing subframe. If
1125 // 1. The load of this frame was not originally initiated by session
1126 // history directly (i.e. (!shEntry) condition succeeded, but it can
1127 // still be a history load on parent which causes this frame being
1128 // loaded), which we checked with the above assert, and
1129 // 2. mCurrentURI is not null, nor the initial about:blank,
1130 // it is possible that a parent's onLoadHandler or even self's
1131 // onLoadHandler is loading a new page in this child. Check parent's and
1132 // self's busy flag and if it is set, we don't want this onLoadHandler
1133 // load to get in to session history.
1134 BusyFlags parentBusy = parentDS->GetBusyFlags();
1135 BusyFlags selfBusy = GetBusyFlags();
1137 if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
1138 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1139 aLoadState->ClearLoadIsFromSessionHistory();
1141 return false;
1144 // This is a newly created frame. Check for exception cases first.
1145 // By default the subframe will inherit the parent's loadType.
1146 if (aLoadState->LoadIsFromSessionHistory() &&
1147 (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) {
1148 // The parent was loaded normally. In this case, this *brand new*
1149 // child really shouldn't have a SHEntry. If it does, it could be
1150 // because the parent is replacing an existing frame with a new frame,
1151 // in the onLoadHandler. We don't want this url to get into session
1152 // history. Clear off shEntry, and set load type to
1153 // LOAD_BYPASS_HISTORY.
1154 bool inOnLoadHandler = false;
1155 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1156 if (inOnLoadHandler) {
1157 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1158 aLoadState->ClearLoadIsFromSessionHistory();
1160 } else if (parentLoadType == LOAD_REFRESH) {
1161 // Clear shEntry. For refresh loads, we have to load
1162 // what comes through the pipe, not what's in history.
1163 aLoadState->ClearLoadIsFromSessionHistory();
1164 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1165 (aLoadState->LoadIsFromSessionHistory() &&
1166 ((parentLoadType & LOAD_CMD_HISTORY) ||
1167 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1168 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
1169 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
1170 (parentLoadType ==
1171 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
1172 // If the parent url, bypassed history or was loaded from
1173 // history, pass on the parent's loadType to the new child
1174 // frame too, so that the child frame will also
1175 // avoid getting into history.
1176 aLoadState->SetLoadType(parentLoadType);
1177 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1178 // If the parent document is an error page, we don't
1179 // want to update global/session history. However,
1180 // this child frame is not an error page.
1181 aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
1182 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1183 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1184 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1185 // the new frame should inherit the parent's load type so that it also
1186 // bypasses the cache and/or proxy
1187 aLoadState->SetLoadType(parentLoadType);
1190 return false;
1194 * Reset state to a new content model within the current document and the
1195 * document viewer. Called by the document before initiating an out of band
1196 * document.write().
1198 NS_IMETHODIMP
1199 nsDocShell::PrepareForNewContentModel() {
1200 // Clear out our form control state, because the state of controls
1201 // in the pre-open() document should not affect the state of
1202 // controls that are now going to be written.
1203 SetLayoutHistoryState(nullptr);
1204 mEODForCurrentDocument = false;
1205 return NS_OK;
1208 NS_IMETHODIMP
1209 nsDocShell::FirePageHideNotification(bool aIsUnload) {
1210 FirePageHideNotificationInternal(aIsUnload, false);
1211 return NS_OK;
1214 void nsDocShell::FirePageHideNotificationInternal(
1215 bool aIsUnload, bool aSkipCheckingDynEntries) {
1216 if (mContentViewer && !mFiredUnloadEvent) {
1217 // Keep an explicit reference since calling PageHide could release
1218 // mContentViewer
1219 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1220 mFiredUnloadEvent = true;
1222 if (mTiming) {
1223 mTiming->NotifyUnloadEventStart();
1226 contentViewer->PageHide(aIsUnload);
1228 if (mTiming) {
1229 mTiming->NotifyUnloadEventEnd();
1232 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1233 uint32_t n = mChildList.Length();
1234 kids.SetCapacity(n);
1235 for (uint32_t i = 0; i < n; i++) {
1236 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1239 n = kids.Length();
1240 for (uint32_t i = 0; i < n; ++i) {
1241 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1242 if (child) {
1243 // Skip checking dynamic subframe entries in our children.
1244 child->FirePageHideNotificationInternal(aIsUnload, true);
1248 // If the document is unloading, remove all dynamic subframe entries.
1249 if (aIsUnload && !aSkipCheckingDynEntries) {
1250 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
1251 if (rootSH) {
1252 MOZ_LOG(
1253 gSHLog, LogLevel::Debug,
1254 ("nsDocShell %p unloading, remove dynamic subframe entries", this));
1255 if (mozilla::SessionHistoryInParent()) {
1256 if (mActiveEntry) {
1257 mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry();
1259 MOZ_LOG(gSHLog, LogLevel::Debug,
1260 ("nsDocShell %p unloading, no active entries", this));
1261 } else if (mOSHE) {
1262 int32_t index = rootSH->Index();
1263 rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
1268 // Now make sure our editor, if any, is detached before we go
1269 // any farther.
1270 DetachEditorFromWindow();
1274 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) {
1275 MOZ_ASSERT(mozilla::BFCacheInParent());
1277 if (!mScriptGlobal) {
1278 return;
1281 RefPtr<nsGlobalWindowInner> inner =
1282 mScriptGlobal->GetCurrentInnerWindowInternal();
1283 if (inner) {
1284 if (aThaw) {
1285 inner->Thaw(false);
1286 } else {
1287 inner->Freeze(false);
1292 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
1293 MOZ_ASSERT(mozilla::BFCacheInParent());
1295 if (!mContentViewer) {
1296 return;
1299 // Emulate what non-SHIP BFCache does too. In pageshow case
1300 // add and remove a request and before that call SetCurrentURI to get
1301 // the location change notification.
1302 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
1303 nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
1304 if (aShow) {
1305 contentViewer->SetIsHidden(false);
1306 mRefreshURIList = std::move(mBFCachedRefreshURIList);
1307 RefreshURIFromQueue();
1308 mFiredUnloadEvent = false;
1309 RefPtr<Document> doc = contentViewer->GetDocument();
1310 if (doc) {
1311 doc->NotifyActivityChanged();
1312 RefPtr<nsGlobalWindowInner> inner =
1313 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal()
1314 : 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 mCurrentURI = 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 NS_IMETHODIMP
1580 nsDocShell::GetCharset(nsACString& aCharset) {
1581 aCharset.Truncate();
1583 PresShell* presShell = GetPresShell();
1584 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1585 Document* doc = presShell->GetDocument();
1586 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1587 doc->GetDocumentCharacterSet()->Name(aCharset);
1588 return NS_OK;
1591 NS_IMETHODIMP
1592 nsDocShell::ForceEncodingDetection() {
1593 nsCOMPtr<nsIContentViewer> viewer;
1594 GetContentViewer(getter_AddRefs(viewer));
1595 if (!viewer) {
1596 return NS_OK;
1599 Document* doc = viewer->GetDocument();
1600 if (!doc || doc->WillIgnoreCharsetOverride()) {
1601 return NS_OK;
1604 mForcedAutodetection = true;
1606 nsIURI* url = doc->GetOriginalURI();
1607 bool isFileURL = url && SchemeIsFile(url);
1609 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1610 auto encoding = doc->GetDocumentCharacterSet();
1611 // AsHTMLDocument is valid, because we called
1612 // WillIgnoreCharsetOverride() above.
1613 if (doc->AsHTMLDocument()->IsPlainText()) {
1614 switch (charsetSource) {
1615 case kCharsetFromInitialAutoDetectionASCII:
1616 // Deliberately no final version
1617 LOGCHARSETMENU(("TEXT:UnlabeledAscii"));
1618 Telemetry::AccumulateCategorical(
1619 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledAscii);
1620 break;
1621 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1622 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1623 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1624 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1625 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1626 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1627 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8"));
1628 Telemetry::AccumulateCategorical(
1629 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1630 UnlabeledNonUtf8);
1631 break;
1632 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1633 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1634 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1635 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD"));
1636 Telemetry::AccumulateCategorical(
1637 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1638 UnlabeledNonUtf8TLD);
1639 break;
1640 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1641 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1642 LOGCHARSETMENU(("TEXT:UnlabeledUtf8"));
1643 Telemetry::AccumulateCategorical(
1644 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledUtf8);
1645 break;
1646 case kCharsetFromChannel:
1647 if (encoding == UTF_8_ENCODING) {
1648 LOGCHARSETMENU(("TEXT:ChannelUtf8"));
1649 Telemetry::AccumulateCategorical(
1650 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::ChannelUtf8);
1651 } else {
1652 LOGCHARSETMENU(("TEXT:ChannelNonUtf8"));
1653 Telemetry::AccumulateCategorical(
1654 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1655 ChannelNonUtf8);
1657 break;
1658 default:
1659 LOGCHARSETMENU(("TEXT:Bug"));
1660 Telemetry::AccumulateCategorical(
1661 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::Bug);
1662 break;
1664 } else {
1665 switch (charsetSource) {
1666 case kCharsetFromInitialAutoDetectionASCII:
1667 // Deliberately no final version
1668 LOGCHARSETMENU(("HTML:UnlabeledAscii"));
1669 Telemetry::AccumulateCategorical(
1670 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledAscii);
1671 break;
1672 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1673 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1674 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1675 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1676 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1677 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1678 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8"));
1679 Telemetry::AccumulateCategorical(
1680 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1681 UnlabeledNonUtf8);
1682 break;
1683 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1684 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1685 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1686 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD"));
1687 Telemetry::AccumulateCategorical(
1688 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1689 UnlabeledNonUtf8TLD);
1690 break;
1691 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1692 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1693 LOGCHARSETMENU(("HTML:UnlabeledUtf8"));
1694 Telemetry::AccumulateCategorical(
1695 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledUtf8);
1696 break;
1697 case kCharsetFromChannel:
1698 if (encoding == UTF_8_ENCODING) {
1699 LOGCHARSETMENU(("HTML:ChannelUtf8"));
1700 Telemetry::AccumulateCategorical(
1701 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::ChannelUtf8);
1702 } else {
1703 LOGCHARSETMENU(("HTML:ChannelNonUtf8"));
1704 Telemetry::AccumulateCategorical(
1705 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1706 ChannelNonUtf8);
1708 break;
1709 case kCharsetFromXmlDeclaration:
1710 case kCharsetFromMetaTag:
1711 if (isFileURL) {
1712 LOGCHARSETMENU(("HTML:LocalLabeled"));
1713 Telemetry::AccumulateCategorical(
1714 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::LocalLabeled);
1715 } else if (encoding == UTF_8_ENCODING) {
1716 LOGCHARSETMENU(("HTML:MetaUtf8"));
1717 Telemetry::AccumulateCategorical(
1718 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::InternalUtf8);
1719 } else {
1720 LOGCHARSETMENU(("HTML:MetaNonUtf8"));
1721 Telemetry::AccumulateCategorical(
1722 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1723 InternalNonUtf8);
1725 break;
1726 default:
1727 LOGCHARSETMENU(("HTML:Bug"));
1728 Telemetry::AccumulateCategorical(
1729 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::Bug);
1730 break;
1733 return NS_OK;
1736 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1737 int32_t aCharsetSource,
1738 nsIPrincipal* aPrincipal) {
1739 mParentCharset = aCharset;
1740 mParentCharsetSource = aCharsetSource;
1741 mParentCharsetPrincipal = aPrincipal;
1744 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1745 int32_t* aCharsetSource,
1746 nsIPrincipal** aPrincipal) {
1747 aCharset = mParentCharset;
1748 *aCharsetSource = mParentCharsetSource;
1749 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1752 NS_IMETHODIMP
1753 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
1754 MOZ_ASSERT(aPromise);
1756 ErrorResult rv;
1757 RefPtr<Document> doc(GetDocument());
1758 RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
1759 if (NS_WARN_IF(rv.Failed())) {
1760 return rv.StealNSResult();
1763 // Retrieve the document's content blocking events from the parent process.
1764 RefPtr<Document::GetContentBlockingEventsPromise> promise =
1765 doc->GetContentBlockingEvents();
1766 if (promise) {
1767 promise->Then(
1768 GetCurrentSerialEventTarget(), __func__,
1769 [retPromise](const Document::GetContentBlockingEventsPromise::
1770 ResolveOrRejectValue& aValue) {
1771 if (aValue.IsResolve()) {
1772 bool has = aValue.ResolveValue() &
1773 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
1774 retPromise->MaybeResolve(has);
1775 } else {
1776 retPromise->MaybeResolve(false);
1779 } else {
1780 retPromise->MaybeResolve(false);
1783 retPromise.forget(aPromise);
1784 return NS_OK;
1787 NS_IMETHODIMP
1788 nsDocShell::GetAllowPlugins(bool* aAllowPlugins) {
1789 NS_ENSURE_ARG_POINTER(aAllowPlugins);
1791 *aAllowPlugins = mBrowsingContext->GetAllowPlugins();
1792 return NS_OK;
1795 NS_IMETHODIMP
1796 nsDocShell::SetAllowPlugins(bool aAllowPlugins) {
1797 // XXX should enable or disable a plugin host
1798 return mBrowsingContext->SetAllowPlugins(aAllowPlugins);
1801 NS_IMETHODIMP
1802 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
1803 MOZ_ASSERT(aEnabled);
1804 *aEnabled = mCSSErrorReportingEnabled;
1805 return NS_OK;
1808 NS_IMETHODIMP
1809 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1810 mCSSErrorReportingEnabled = aEnabled;
1811 return NS_OK;
1814 NS_IMETHODIMP
1815 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1816 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1817 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1820 void nsDocShell::NotifyPrivateBrowsingChanged() {
1821 MOZ_ASSERT(!mIsBeingDestroyed);
1823 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1824 while (iter.HasMore()) {
1825 nsWeakPtr ref = iter.GetNext();
1826 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1827 if (!obs) {
1828 iter.Remove();
1829 } else {
1830 obs->PrivateModeChanged(UsePrivateBrowsing());
1835 NS_IMETHODIMP
1836 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1837 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1840 NS_IMETHODIMP
1841 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1842 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1845 NS_IMETHODIMP
1846 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1847 NS_ENSURE_ARG_POINTER(aResult);
1849 *aResult = mHasLoadedNonBlankURI;
1850 return NS_OK;
1853 NS_IMETHODIMP
1854 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1855 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1856 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1859 NS_IMETHODIMP
1860 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1861 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1864 NS_IMETHODIMP
1865 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1866 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1867 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1870 NS_IMETHODIMP
1871 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1872 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1875 NS_IMETHODIMP
1876 nsDocShell::AddWeakPrivacyTransitionObserver(
1877 nsIPrivacyTransitionObserver* aObserver) {
1878 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1879 if (!weakObs) {
1880 return NS_ERROR_NOT_AVAILABLE;
1882 mPrivacyObservers.AppendElement(weakObs);
1883 return NS_OK;
1886 NS_IMETHODIMP
1887 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1888 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1889 if (!weakObs) {
1890 return NS_ERROR_FAILURE;
1892 mReflowObservers.AppendElement(weakObs);
1893 return NS_OK;
1896 NS_IMETHODIMP
1897 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1898 nsWeakPtr obs = do_GetWeakReference(aObserver);
1899 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1902 NS_IMETHODIMP
1903 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1904 DOMHighResTimeStamp aStart,
1905 DOMHighResTimeStamp aEnd) {
1906 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1907 while (iter.HasMore()) {
1908 nsWeakPtr ref = iter.GetNext();
1909 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1910 if (!obs) {
1911 iter.Remove();
1912 } else if (aInterruptible) {
1913 obs->ReflowInterruptible(aStart, aEnd);
1914 } else {
1915 obs->Reflow(aStart, aEnd);
1918 return NS_OK;
1921 NS_IMETHODIMP
1922 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1923 NS_ENSURE_ARG_POINTER(aReturn);
1925 *aReturn = mAllowMetaRedirects;
1926 return NS_OK;
1929 NS_IMETHODIMP
1930 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1931 mAllowMetaRedirects = aValue;
1932 return NS_OK;
1935 NS_IMETHODIMP
1936 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1937 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1939 *aAllowSubframes = mAllowSubframes;
1940 return NS_OK;
1943 NS_IMETHODIMP
1944 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1945 mAllowSubframes = aAllowSubframes;
1946 return NS_OK;
1949 NS_IMETHODIMP
1950 nsDocShell::GetAllowImages(bool* aAllowImages) {
1951 NS_ENSURE_ARG_POINTER(aAllowImages);
1953 *aAllowImages = mAllowImages;
1954 return NS_OK;
1957 NS_IMETHODIMP
1958 nsDocShell::SetAllowImages(bool aAllowImages) {
1959 mAllowImages = aAllowImages;
1960 return NS_OK;
1963 NS_IMETHODIMP
1964 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1965 *aAllowMedia = mAllowMedia;
1966 return NS_OK;
1969 NS_IMETHODIMP
1970 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1971 mAllowMedia = aAllowMedia;
1973 // Mute or unmute audio contexts attached to the inner window.
1974 if (mScriptGlobal) {
1975 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1976 if (aAllowMedia) {
1977 innerWin->UnmuteAudioContexts();
1978 } else {
1979 innerWin->MuteAudioContexts();
1984 return NS_OK;
1987 NS_IMETHODIMP
1988 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1989 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1990 return NS_OK;
1993 NS_IMETHODIMP
1994 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
1995 mAllowDNSPrefetch = aAllowDNSPrefetch;
1996 return NS_OK;
1999 NS_IMETHODIMP
2000 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
2001 *aAllowWindowControl = mAllowWindowControl;
2002 return NS_OK;
2005 NS_IMETHODIMP
2006 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
2007 mAllowWindowControl = aAllowWindowControl;
2008 return NS_OK;
2011 NS_IMETHODIMP
2012 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
2013 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
2014 return NS_OK;
2017 NS_IMETHODIMP
2018 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
2019 BrowsingContext::Transaction txn;
2020 txn.SetAllowContentRetargeting(aAllowContentRetargeting);
2021 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
2022 return txn.Commit(mBrowsingContext);
2025 NS_IMETHODIMP
2026 nsDocShell::GetAllowContentRetargetingOnChildren(
2027 bool* aAllowContentRetargetingOnChildren) {
2028 *aAllowContentRetargetingOnChildren =
2029 mBrowsingContext->GetAllowContentRetargetingOnChildren();
2030 return NS_OK;
2033 NS_IMETHODIMP
2034 nsDocShell::SetAllowContentRetargetingOnChildren(
2035 bool aAllowContentRetargetingOnChildren) {
2036 return mBrowsingContext->SetAllowContentRetargetingOnChildren(
2037 aAllowContentRetargetingOnChildren);
2040 NS_IMETHODIMP
2041 nsDocShell::GetMayEnableCharacterEncodingMenu(
2042 bool* aMayEnableCharacterEncodingMenu) {
2043 *aMayEnableCharacterEncodingMenu = false;
2044 if (!mContentViewer) {
2045 return NS_OK;
2047 Document* doc = mContentViewer->GetDocument();
2048 if (!doc) {
2049 return NS_OK;
2051 if (doc->WillIgnoreCharsetOverride()) {
2052 return NS_OK;
2055 *aMayEnableCharacterEncodingMenu = true;
2056 return NS_OK;
2059 NS_IMETHODIMP
2060 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
2061 DocShellEnumeratorDirection aDirection,
2062 nsTArray<RefPtr<nsIDocShell>>& aResult) {
2063 aResult.Clear();
2065 nsDocShellEnumerator docShellEnum(
2066 (aDirection == ENUMERATE_FORWARDS)
2067 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2068 : nsDocShellEnumerator::EnumerationDirection::Backwards,
2069 aItemType, *this);
2071 nsresult rv = docShellEnum.BuildDocShellArray(aResult);
2072 if (NS_FAILED(rv)) {
2073 return rv;
2076 return NS_OK;
2079 NS_IMETHODIMP
2080 nsDocShell::GetAppType(AppType* aAppType) {
2081 *aAppType = mAppType;
2082 return NS_OK;
2085 NS_IMETHODIMP
2086 nsDocShell::SetAppType(AppType aAppType) {
2087 mAppType = aAppType;
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2093 *aAllowAuth = mAllowAuth;
2094 return NS_OK;
2097 NS_IMETHODIMP
2098 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2099 mAllowAuth = aAllowAuth;
2100 return NS_OK;
2103 NS_IMETHODIMP
2104 nsDocShell::GetZoom(float* aZoom) {
2105 NS_ENSURE_ARG_POINTER(aZoom);
2106 *aZoom = 1.0f;
2107 return NS_OK;
2110 NS_IMETHODIMP
2111 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2113 NS_IMETHODIMP
2114 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
2115 NS_ENSURE_ARG_POINTER(aBusyFlags);
2117 *aBusyFlags = mBusyFlags;
2118 return NS_OK;
2121 NS_IMETHODIMP
2122 nsDocShell::TabToTreeOwner(bool aForward, bool aForDocumentNavigation,
2123 bool* aTookFocus) {
2124 NS_ENSURE_ARG_POINTER(aTookFocus);
2126 nsCOMPtr<nsIWebBrowserChromeFocus> chromeFocus = do_GetInterface(mTreeOwner);
2127 if (chromeFocus) {
2128 if (aForward) {
2129 *aTookFocus =
2130 NS_SUCCEEDED(chromeFocus->FocusNextElement(aForDocumentNavigation));
2131 } else {
2132 *aTookFocus =
2133 NS_SUCCEEDED(chromeFocus->FocusPrevElement(aForDocumentNavigation));
2135 } else {
2136 *aTookFocus = false;
2139 return NS_OK;
2142 NS_IMETHODIMP
2143 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2144 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
2145 delegate.forget(aLoadURIDelegate);
2146 return NS_OK;
2149 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
2150 if (nsCOMPtr<nsILoadURIDelegate> result =
2151 do_QueryActor("LoadURIDelegate", GetDocument())) {
2152 return result.forget();
2155 return nullptr;
2158 NS_IMETHODIMP
2159 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2160 *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
2161 return NS_OK;
2164 NS_IMETHODIMP
2165 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2166 return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
2169 NS_IMETHODIMP
2170 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
2171 *aPreviousEntryIndex = mPreviousEntryIndex;
2172 return NS_OK;
2175 NS_IMETHODIMP
2176 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
2177 *aLoadedEntryIndex = mLoadedEntryIndex;
2178 return NS_OK;
2181 NS_IMETHODIMP
2182 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2183 // These indices are used for fastback cache eviction, to determine
2184 // which session history entries are candidates for content viewer
2185 // eviction. We need to adjust by the number of entries that we
2186 // just purged from history, so that we look at the right session history
2187 // entries during eviction.
2188 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
2189 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
2191 for (auto* child : mChildList.ForwardRange()) {
2192 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2193 if (shell) {
2194 shell->HistoryPurged(aNumEntries);
2198 return NS_OK;
2201 void nsDocShell::TriggerParentCheckDocShellIsEmpty() {
2202 if (RefPtr<nsDocShell> parent = GetInProcessParentDocshell()) {
2203 parent->DocLoaderIsEmpty(true);
2205 if (GetBrowsingContext()->IsContentSubframe() &&
2206 !GetBrowsingContext()->GetParent()->IsInProcess()) {
2207 if (BrowserChild* browserChild = BrowserChild::GetFrom(this)) {
2208 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
2209 EmbedderElementEventType::NoEvent);
2214 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2215 // These indices are used for fastback cache eviction, to determine
2216 // which session history entries are candidates for content viewer
2217 // eviction. We need to adjust by the number of entries that we
2218 // just purged from history, so that we look at the right session history
2219 // entries during eviction.
2220 if (aIndex == mPreviousEntryIndex) {
2221 mPreviousEntryIndex = -1;
2222 } else if (aIndex < mPreviousEntryIndex) {
2223 --mPreviousEntryIndex;
2225 if (mLoadedEntryIndex == aIndex) {
2226 mLoadedEntryIndex = 0;
2227 } else if (aIndex < mLoadedEntryIndex) {
2228 --mLoadedEntryIndex;
2231 for (auto* child : mChildList.ForwardRange()) {
2232 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2233 if (shell) {
2234 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2238 return NS_OK;
2241 NS_IMETHODIMP
2242 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue) {
2243 bool currentValue = nsIDocShell::GetRecordProfileTimelineMarkers();
2244 if (currentValue == aValue) {
2245 return NS_OK;
2248 if (aValue) {
2249 MOZ_ASSERT(!TimelineConsumers::HasConsumer(this));
2250 TimelineConsumers::AddConsumer(this);
2251 MOZ_ASSERT(TimelineConsumers::HasConsumer(this));
2252 UseEntryScriptProfiling();
2253 } else {
2254 MOZ_ASSERT(TimelineConsumers::HasConsumer(this));
2255 TimelineConsumers::RemoveConsumer(this);
2256 MOZ_ASSERT(!TimelineConsumers::HasConsumer(this));
2257 UnuseEntryScriptProfiling();
2260 return NS_OK;
2263 NS_IMETHODIMP
2264 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue) {
2265 *aValue = !!mObserved;
2266 return NS_OK;
2269 nsresult nsDocShell::PopProfileTimelineMarkers(
2270 JSContext* aCx, JS::MutableHandle<JS::Value> aOut) {
2271 nsTArray<dom::ProfileTimelineMarker> store;
2272 SequenceRooter<dom::ProfileTimelineMarker> rooter(aCx, &store);
2274 TimelineConsumers::PopMarkers(this, aCx, store);
2276 if (!ToJSValue(aCx, store, aOut)) {
2277 JS_ClearPendingException(aCx);
2278 return NS_ERROR_UNEXPECTED;
2281 return NS_OK;
2284 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2285 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2286 return NS_OK;
2289 NS_IMETHODIMP
2290 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2291 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2292 if (!aValue && mItemType == typeChrome && !parent) {
2293 // Window dragging is always allowed for top level
2294 // chrome docshells.
2295 return NS_ERROR_FAILURE;
2297 mWindowDraggingAllowed = aValue;
2298 return NS_OK;
2301 NS_IMETHODIMP
2302 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2303 // window dragging regions in CSS (-moz-window-drag:drag)
2304 // can be slow. Default behavior is to only allow it for
2305 // chrome top level windows.
2306 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2307 if (mItemType == typeChrome && !parent) {
2308 // Top level chrome window
2309 *aValue = true;
2310 } else {
2311 *aValue = mWindowDraggingAllowed;
2313 return NS_OK;
2316 NS_IMETHODIMP
2317 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2318 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2319 return NS_OK;
2322 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2323 if (mContentViewer) {
2324 Document* doc = mContentViewer->GetDocument();
2325 if (doc) {
2326 return doc->GetChannel();
2329 return nullptr;
2332 NS_IMETHODIMP
2333 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2334 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2335 if (!weakObs) {
2336 return NS_ERROR_FAILURE;
2338 mScrollObservers.AppendElement(weakObs);
2339 return NS_OK;
2342 NS_IMETHODIMP
2343 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2344 nsWeakPtr obs = do_GetWeakReference(aObserver);
2345 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2348 void nsDocShell::NotifyAsyncPanZoomStarted() {
2349 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2350 while (iter.HasMore()) {
2351 nsWeakPtr ref = iter.GetNext();
2352 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2353 if (obs) {
2354 obs->AsyncPanZoomStarted();
2355 } else {
2356 iter.Remove();
2361 void nsDocShell::NotifyAsyncPanZoomStopped() {
2362 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2363 while (iter.HasMore()) {
2364 nsWeakPtr ref = iter.GetNext();
2365 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2366 if (obs) {
2367 obs->AsyncPanZoomStopped();
2368 } else {
2369 iter.Remove();
2374 NS_IMETHODIMP
2375 nsDocShell::NotifyScrollObservers() {
2376 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2377 while (iter.HasMore()) {
2378 nsWeakPtr ref = iter.GetNext();
2379 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2380 if (obs) {
2381 obs->ScrollPositionChanged();
2382 } else {
2383 iter.Remove();
2386 return NS_OK;
2389 //*****************************************************************************
2390 // nsDocShell::nsIDocShellTreeItem
2391 //*****************************************************************************
2393 NS_IMETHODIMP
2394 nsDocShell::GetName(nsAString& aName) {
2395 aName = mBrowsingContext->Name();
2396 return NS_OK;
2399 NS_IMETHODIMP
2400 nsDocShell::SetName(const nsAString& aName) {
2401 return mBrowsingContext->SetName(aName);
2404 NS_IMETHODIMP
2405 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2406 NS_ENSURE_ARG_POINTER(aResult);
2407 *aResult = mBrowsingContext->NameEquals(aName);
2408 return NS_OK;
2411 NS_IMETHODIMP
2412 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2413 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2414 return NS_OK;
2417 NS_IMETHODIMP
2418 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2419 if (mWillChangeProcess) {
2420 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2421 return NS_ERROR_FAILURE;
2424 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2427 NS_IMETHODIMP
2428 nsDocShell::ClearCachedPlatform() {
2429 RefPtr<nsGlobalWindowInner> win =
2430 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2431 if (win) {
2432 Navigator* navigator = win->Navigator();
2433 if (navigator) {
2434 navigator->ClearPlatformCache();
2438 return NS_OK;
2441 NS_IMETHODIMP
2442 nsDocShell::ClearCachedUserAgent() {
2443 RefPtr<nsGlobalWindowInner> win =
2444 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2445 if (win) {
2446 Navigator* navigator = win->Navigator();
2447 if (navigator) {
2448 navigator->ClearUserAgentCache();
2452 return NS_OK;
2455 NS_IMETHODIMP
2456 nsDocShell::GetMetaViewportOverride(
2457 MetaViewportOverride* aMetaViewportOverride) {
2458 NS_ENSURE_ARG_POINTER(aMetaViewportOverride);
2460 *aMetaViewportOverride = mMetaViewportOverride;
2461 return NS_OK;
2464 NS_IMETHODIMP
2465 nsDocShell::SetMetaViewportOverride(
2466 MetaViewportOverride aMetaViewportOverride) {
2467 // We don't have a way to verify this coming from Javascript, so this check is
2468 // still needed.
2469 if (!(aMetaViewportOverride == META_VIEWPORT_OVERRIDE_NONE ||
2470 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_ENABLED ||
2471 aMetaViewportOverride == META_VIEWPORT_OVERRIDE_DISABLED)) {
2472 return NS_ERROR_INVALID_ARG;
2475 mMetaViewportOverride = aMetaViewportOverride;
2477 // Inform our presShell that it needs to re-check its need for a viewport
2478 // override.
2479 if (RefPtr<PresShell> presShell = GetPresShell()) {
2480 presShell->MaybeRecreateMobileViewportManager(true);
2483 return NS_OK;
2486 /* virtual */
2487 int32_t nsDocShell::ItemType() { return mItemType; }
2489 NS_IMETHODIMP
2490 nsDocShell::GetItemType(int32_t* aItemType) {
2491 NS_ENSURE_ARG_POINTER(aItemType);
2493 MOZ_DIAGNOSTIC_ASSERT(
2494 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2495 *aItemType = mItemType;
2496 return NS_OK;
2499 NS_IMETHODIMP
2500 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2501 if (!mParent) {
2502 *aParent = nullptr;
2503 } else {
2504 CallQueryInterface(mParent, aParent);
2506 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2507 // don't want to throw; we just want to return null.
2508 return NS_OK;
2511 // With Fission, related nsDocShell objects may exist in a different process. In
2512 // that case, this method will return `nullptr`, despite a parent nsDocShell
2513 // object existing.
2515 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2516 // parent entry is not in the current process, and handle the case where the
2517 // parent nsDocShell is inaccessible.
2518 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2519 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2520 return docshell.forget().downcast<nsDocShell>();
2523 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2524 MOZ_ASSERT(!mIsBeingDestroyed);
2526 // If there is an existing document then there is no need to create
2527 // a client for a future initial about:blank document.
2528 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal() &&
2529 mScriptGlobal->GetCurrentInnerWindowInternal()->GetExtantDoc()) {
2530 MOZ_DIAGNOSTIC_ASSERT(mScriptGlobal->GetCurrentInnerWindowInternal()
2531 ->GetClientInfo()
2532 .isSome());
2533 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2534 return;
2537 // Don't recreate the initial client source. We call this multiple times
2538 // when DoChannelLoad() is called before CreateAboutBlankContentViewer.
2539 if (mInitialClientSource) {
2540 return;
2543 // Don't pre-allocate the client when we are sandboxed. The inherited
2544 // principal does not take sandboxing into account.
2545 // TODO: Refactor sandboxing principal code out so we can use it here.
2546 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2547 return;
2550 // We cannot get inherited foreign partitioned principal here. Instead, we
2551 // directly check which principal we want to inherit for the service worker.
2552 nsIPrincipal* principal =
2553 aPrincipal
2554 ? aPrincipal
2555 : GetInheritedPrincipal(
2556 false, StoragePrincipalHelper::
2557 ShouldUsePartitionPrincipalForServiceWorker(this));
2559 // Sometimes there is no principal available when we are called from
2560 // CreateAboutBlankContentViewer. For example, sometimes the principal
2561 // is only extracted from the load context after the document is created
2562 // in Document::ResetToURI(). Ideally we would do something similar
2563 // here, but for now lets just avoid the issue by not preallocating the
2564 // client.
2565 if (!principal) {
2566 return;
2569 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2570 if (!win) {
2571 return;
2574 mInitialClientSource = ClientManager::CreateSource(
2575 ClientType::Window, win->EventTargetFor(TaskCategory::Other), principal);
2576 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2578 // Mark the initial client as execution ready, but owned by the docshell.
2579 // If the client is actually used this will cause ClientSource to force
2580 // the creation of the initial about:blank by calling
2581 // nsDocShell::GetDocument().
2582 mInitialClientSource->DocShellExecutionReady(this);
2584 // Next, check to see if the parent is controlled.
2585 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2586 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2587 nsPIDOMWindowInner* parentInner =
2588 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2589 if (!parentInner) {
2590 return;
2593 nsCOMPtr<nsIURI> uri;
2594 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
2596 // We're done if there is no parent controller or if this docshell
2597 // is not permitted to control for some reason.
2598 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2599 if (controller.isNothing() ||
2600 !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2601 return;
2604 mInitialClientSource->InheritController(controller.ref());
2607 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2608 if (mInitialClientSource) {
2609 Maybe<ClientInfo> result;
2610 result.emplace(mInitialClientSource->Info());
2611 return result;
2614 nsGlobalWindowInner* innerWindow =
2615 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
2616 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2618 if (!doc || !doc->IsInitialDocument()) {
2619 return Maybe<ClientInfo>();
2622 return innerWindow->GetClientInfo();
2625 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2626 bool wasFrame = IsSubframe();
2628 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2629 NS_ENSURE_SUCCESS(rv, rv);
2631 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2632 if (wasFrame != IsSubframe() && priorityGroup) {
2633 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2636 // Curse ambiguous nsISupports inheritance!
2637 nsISupports* parent = GetAsSupports(aParent);
2639 // If parent is another docshell, we inherit all their flags for
2640 // allowing plugins, scripting etc.
2641 bool value;
2642 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2644 if (parentAsDocShell) {
2645 if (mAllowMetaRedirects &&
2646 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2647 SetAllowMetaRedirects(value);
2649 if (mAllowSubframes &&
2650 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2651 SetAllowSubframes(value);
2653 if (mAllowImages &&
2654 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2655 SetAllowImages(value);
2657 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2658 if (mAllowWindowControl &&
2659 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2660 SetAllowWindowControl(value);
2662 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2663 value = false;
2665 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2667 // We don't need to inherit metaViewportOverride, because the viewport
2668 // is only relevant for the outermost nsDocShell, not for any iframes
2669 // like this that might be embedded within it.
2672 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2673 if (parentURIListener) {
2674 mContentListener->SetParentContentListener(parentURIListener);
2677 return NS_OK;
2680 void nsDocShell::MaybeRestoreWindowName() {
2681 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2682 return;
2685 // We only restore window.name for the top-level content.
2686 if (!mBrowsingContext->IsTopContent()) {
2687 return;
2690 nsAutoString name;
2692 // Following implements https://html.spec.whatwg.org/#history-traversal:
2693 // Step 4.4. Check if the loading entry has a name.
2695 if (mLSHE) {
2696 mLSHE->GetName(name);
2699 if (mLoadingEntry) {
2700 name = mLoadingEntry->mInfo.GetName();
2703 if (name.IsEmpty()) {
2704 return;
2707 // Step 4.4.1. Set the name to the browsing context.
2708 Unused << mBrowsingContext->SetName(name);
2710 // Step 4.4.2. Clear the name of all entries that are contiguous and
2711 // same-origin with the loading entry.
2712 if (mLSHE) {
2713 nsSHistory::WalkContiguousEntries(
2714 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
2717 if (mLoadingEntry) {
2718 // Clear the name of the session entry in the child side. For parent side,
2719 // the clearing will be done when we commit the history to the parent.
2720 mLoadingEntry->mInfo.SetName(EmptyString());
2724 void nsDocShell::StoreWindowNameToSHEntries() {
2725 MOZ_ASSERT(mBrowsingContext->IsTopContent());
2727 nsAutoString name;
2728 mBrowsingContext->GetName(name);
2730 if (mOSHE) {
2731 nsSHistory::WalkContiguousEntries(
2732 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2735 if (mozilla::SessionHistoryInParent()) {
2736 if (XRE_IsParentProcess()) {
2737 SessionHistoryEntry* entry =
2738 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2739 if (entry) {
2740 nsSHistory::WalkContiguousEntries(
2741 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2743 } else {
2744 // Ask parent process to store the name in entries.
2745 mozilla::Unused
2746 << ContentChild::GetSingleton()
2747 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2748 mBrowsingContext, name);
2753 NS_IMETHODIMP
2754 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2755 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2756 *aParent = do_AddRef(parentBC->GetDocShell()).take();
2758 return NS_OK;
2761 NS_IMETHODIMP
2762 nsDocShell::GetSameTypeInProcessParentIgnoreBrowserBoundaries(
2763 nsIDocShell** aParent) {
2764 NS_ENSURE_ARG_POINTER(aParent);
2765 *aParent = nullptr;
2767 nsCOMPtr<nsIDocShellTreeItem> parent =
2768 do_QueryInterface(GetAsSupports(mParent));
2769 if (!parent) {
2770 return NS_OK;
2773 if (parent->ItemType() == mItemType) {
2774 nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parent);
2775 parentDS.forget(aParent);
2777 return NS_OK;
2780 NS_IMETHODIMP
2781 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2782 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2784 RefPtr<nsDocShell> root = this;
2785 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2786 while (parent) {
2787 root = parent;
2788 parent = root->GetInProcessParentDocshell();
2791 root.forget(aRootTreeItem);
2792 return NS_OK;
2795 NS_IMETHODIMP
2796 nsDocShell::GetInProcessSameTypeRootTreeItem(
2797 nsIDocShellTreeItem** aRootTreeItem) {
2798 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2799 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2801 nsCOMPtr<nsIDocShellTreeItem> parent;
2802 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2803 NS_ERROR_FAILURE);
2804 while (parent) {
2805 *aRootTreeItem = parent;
2806 NS_ENSURE_SUCCESS(
2807 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2808 NS_ERROR_FAILURE);
2810 NS_ADDREF(*aRootTreeItem);
2811 return NS_OK;
2814 NS_IMETHODIMP
2815 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2816 NS_ENSURE_ARG_POINTER(aTreeOwner);
2818 *aTreeOwner = mTreeOwner;
2819 NS_IF_ADDREF(*aTreeOwner);
2820 return NS_OK;
2823 NS_IMETHODIMP
2824 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2825 if (mIsBeingDestroyed && aTreeOwner) {
2826 return NS_ERROR_FAILURE;
2829 // Don't automatically set the progress based on the tree owner for frames
2830 if (!IsSubframe()) {
2831 nsCOMPtr<nsIWebProgress> webProgress =
2832 do_QueryInterface(GetAsSupports(this));
2834 if (webProgress) {
2835 nsCOMPtr<nsIWebProgressListener> oldListener =
2836 do_QueryInterface(mTreeOwner);
2837 nsCOMPtr<nsIWebProgressListener> newListener =
2838 do_QueryInterface(aTreeOwner);
2840 if (oldListener) {
2841 webProgress->RemoveProgressListener(oldListener);
2844 if (newListener) {
2845 webProgress->AddProgressListener(newListener,
2846 nsIWebProgress::NOTIFY_ALL);
2851 mTreeOwner = aTreeOwner; // Weak reference per API
2853 for (auto* childDocLoader : mChildList.ForwardRange()) {
2854 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
2855 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2857 if (child->ItemType() == mItemType) {
2858 child->SetTreeOwner(aTreeOwner);
2862 // If we're in the content process and have had a TreeOwner set on us, extract
2863 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2864 // that it hasn't changed.
2865 if (mTreeOwner && XRE_IsContentProcess()) {
2866 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2867 MOZ_ASSERT(newBrowserChild,
2868 "No BrowserChild actor for tree owner in Content!");
2870 if (mBrowserChild) {
2871 nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2872 do_QueryReferent(mBrowserChild);
2873 MOZ_RELEASE_ASSERT(
2874 oldBrowserChild == newBrowserChild,
2875 "Cannot change BrowserChild during nsDocShell lifetime!");
2876 } else {
2877 mBrowserChild = do_GetWeakReference(newBrowserChild);
2881 return NS_OK;
2884 NS_IMETHODIMP
2885 nsDocShell::GetHistoryID(nsID& aID) {
2886 aID = mBrowsingContext->GetHistoryID();
2887 return NS_OK;
2890 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
2892 NS_IMETHODIMP
2893 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2894 *aIsInUnload = mFiredUnloadEvent;
2895 return NS_OK;
2898 NS_IMETHODIMP
2899 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2900 NS_ENSURE_ARG_POINTER(aChildCount);
2901 *aChildCount = mChildList.Length();
2902 return NS_OK;
2905 NS_IMETHODIMP
2906 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2907 NS_ENSURE_ARG_POINTER(aChild);
2909 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2910 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2912 // Make sure we're not creating a loop in the docshell tree
2913 nsDocLoader* ancestor = this;
2914 do {
2915 if (childAsDocLoader == ancestor) {
2916 return NS_ERROR_ILLEGAL_VALUE;
2918 ancestor = ancestor->GetParent();
2919 } while (ancestor);
2921 // Make sure to remove the child from its current parent.
2922 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2923 if (childsParent) {
2924 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2925 NS_ENSURE_SUCCESS(rv, rv);
2928 // Make sure to clear the treeowner in case this child is a different type
2929 // from us.
2930 aChild->SetTreeOwner(nullptr);
2932 nsresult res = AddChildLoader(childAsDocLoader);
2933 NS_ENSURE_SUCCESS(res, res);
2934 NS_ASSERTION(!mChildList.IsEmpty(),
2935 "child list must not be empty after a successful add");
2937 /* Set the child's global history if the parent has one */
2938 if (mBrowsingContext->GetUseGlobalHistory()) {
2939 // childDocShell->SetUseGlobalHistory(true);
2940 // this should be set through BC inherit
2941 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
2944 if (aChild->ItemType() != mItemType) {
2945 return NS_OK;
2948 aChild->SetTreeOwner(mTreeOwner);
2950 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2951 if (!childAsDocShell) {
2952 return NS_OK;
2955 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2957 // Now take this document's charset and set the child's parentCharset field
2958 // to it. We'll later use that field, in the loading process, for the
2959 // charset choosing algorithm.
2960 // If we fail, at any point, we just return NS_OK.
2961 // This code has some performance impact. But this will be reduced when
2962 // the current charset will finally be stored as an Atom, avoiding the
2963 // alias resolution extra look-up.
2965 // we are NOT going to propagate the charset is this Chrome's docshell
2966 if (mItemType == nsIDocShellTreeItem::typeChrome) {
2967 return NS_OK;
2970 // get the parent's current charset
2971 if (!mContentViewer) {
2972 return NS_OK;
2974 Document* doc = mContentViewer->GetDocument();
2975 if (!doc) {
2976 return NS_OK;
2979 const Encoding* parentCS = doc->GetDocumentCharacterSet();
2980 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2981 // set the child's parentCharset
2982 childAsDocShell->SetParentCharset(parentCS, charsetSource,
2983 doc->NodePrincipal());
2985 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2986 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2988 return NS_OK;
2991 NS_IMETHODIMP
2992 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
2993 NS_ENSURE_ARG_POINTER(aChild);
2995 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2996 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2998 nsresult rv = RemoveChildLoader(childAsDocLoader);
2999 NS_ENSURE_SUCCESS(rv, rv);
3001 aChild->SetTreeOwner(nullptr);
3003 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
3006 NS_IMETHODIMP
3007 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
3008 NS_ENSURE_ARG_POINTER(aChild);
3010 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
3011 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
3013 child.forget(aChild);
3015 return NS_OK;
3018 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
3019 #ifdef DEBUG
3020 if (aIndex < 0) {
3021 NS_WARNING("Negative index passed to GetChildAt");
3022 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
3023 NS_WARNING("Too large an index passed to GetChildAt");
3025 #endif
3027 nsIDocumentLoader* child = ChildAt(aIndex);
3029 // child may be nullptr here.
3030 return static_cast<nsDocShell*>(child);
3033 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
3034 nsISHEntry* aNewEntry,
3035 int32_t aChildOffset, uint32_t aLoadType,
3036 bool aCloneChildren) {
3037 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3038 nsresult rv = NS_OK;
3040 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
3041 /* You get here if you are currently building a
3042 * hierarchy ie.,you just visited a frameset page
3044 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
3045 rv = mLSHE->AddChild(aNewEntry, aChildOffset);
3047 } else if (!aCloneRef) {
3048 /* This is an initial load in some subframe. Just append it if we can */
3049 if (mOSHE) {
3050 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
3052 } else {
3053 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
3054 if (shistory) {
3055 rv = shistory->LegacySHistory()->AddChildSHEntryHelper(
3056 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
3059 return rv;
3062 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
3063 int32_t aChildOffset,
3064 bool aCloneChildren) {
3065 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3066 /* You will get here when you are in a subframe and
3067 * a new url has been loaded on you.
3068 * The mOSHE in this subframe will be the previous url's
3069 * mOSHE. This mOSHE will be used as the identification
3070 * for this subframe in the CloneAndReplace function.
3073 // In this case, we will end up calling AddEntry, which increases the
3074 // current index by 1
3075 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3076 if (rootSH) {
3077 mPreviousEntryIndex = rootSH->Index();
3080 nsresult rv;
3081 // XXX(farre): this is not Fission safe, expect errors. This never
3082 // get's executed once session history in the parent is enabled.
3083 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
3084 NS_WARNING_ASSERTION(
3085 parent || !UseRemoteSubframes(),
3086 "Failed to add child session history entry! This will be resolved once "
3087 "session history in the parent is enabled.");
3088 if (parent) {
3089 rv = nsDocShell::Cast(parent)->AddChildSHEntry(
3090 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
3093 if (rootSH) {
3094 mLoadedEntryIndex = rootSH->Index();
3096 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
3097 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
3098 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
3099 mLoadedEntryIndex));
3103 return rv;
3106 NS_IMETHODIMP
3107 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
3108 *aOSHE = false;
3109 *aEntry = nullptr;
3110 if (mLSHE) {
3111 NS_ADDREF(*aEntry = mLSHE);
3112 } else if (mOSHE) {
3113 NS_ADDREF(*aEntry = mOSHE);
3114 *aOSHE = true;
3116 return NS_OK;
3119 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
3120 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
3121 mBrowsingContext) {
3122 if (XRE_IsContentProcess()) {
3123 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
3124 if (contentChild) {
3125 contentChild->SendSynchronizeLayoutHistoryState(
3126 mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
3128 } else {
3129 SessionHistoryEntry* entry =
3130 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
3131 if (entry) {
3132 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
3135 if (mLoadingEntry &&
3136 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
3137 mLoadingEntry->mInfo.SetLayoutHistoryState(
3138 mActiveEntry->GetLayoutHistoryState());
3142 return NS_OK;
3145 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
3146 if (mLoadGroup) {
3147 mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
3148 } else {
3149 NS_WARNING(
3150 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3151 "propagate the mode to");
3155 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3156 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3157 return mScriptGlobal;
3160 Document* nsDocShell::GetDocument() {
3161 NS_ENSURE_SUCCESS(EnsureContentViewer(), nullptr);
3162 return mContentViewer->GetDocument();
3165 Document* nsDocShell::GetExtantDocument() {
3166 return mContentViewer ? mContentViewer->GetDocument() : nullptr;
3169 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3170 if (NS_FAILED(EnsureScriptEnvironment())) {
3171 return nullptr;
3173 return mScriptGlobal;
3176 NS_IMETHODIMP
3177 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3178 NS_ENSURE_ARG_POINTER(aWindow);
3180 nsresult rv = EnsureScriptEnvironment();
3181 NS_ENSURE_SUCCESS(rv, rv);
3183 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3184 window.forget(aWindow);
3185 return NS_OK;
3188 NS_IMETHODIMP
3189 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3190 RefPtr<ContentFrameMessageManager> mm;
3191 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3192 mm = browserChild->GetMessageManager();
3193 } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3194 mm = win->GetMessageManager();
3196 mm.forget(aMessageManager);
3197 return NS_OK;
3200 NS_IMETHODIMP
3201 nsDocShell::GetIsNavigating(bool* aOut) {
3202 *aOut = mIsNavigating;
3203 return NS_OK;
3206 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3207 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3208 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3209 if (!rootSH || !aEntry) {
3210 return;
3213 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3216 //-------------------------------------
3217 //-- Helper Method for Print discovery
3218 //-------------------------------------
3219 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
3220 if (!mBrowsingContext->Top()->GetIsPrinting()) {
3221 return false;
3223 if (aDisplayErrorDialog) {
3224 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3226 return true;
3229 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3230 bool aCheckIfUnloadFired) {
3231 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
3232 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3233 if (!isAllowed) {
3234 return false;
3236 if (!mContentViewer) {
3237 return true;
3239 bool firingBeforeUnload;
3240 mContentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3241 return !firingBeforeUnload;
3244 //*****************************************************************************
3245 // nsDocShell::nsIWebNavigation
3246 //*****************************************************************************
3248 NS_IMETHODIMP
3249 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3250 *aCanGoBack = false;
3251 if (!IsNavigationAllowed(false)) {
3252 return NS_OK; // JS may not handle returning of an error code
3254 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3255 if (rootSH) {
3256 *aCanGoBack = rootSH->CanGo(-1);
3257 MOZ_LOG(gSHLog, LogLevel::Verbose,
3258 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
3260 return NS_OK;
3262 return NS_ERROR_FAILURE;
3265 NS_IMETHODIMP
3266 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3267 *aCanGoForward = false;
3268 if (!IsNavigationAllowed(false)) {
3269 return NS_OK; // JS may not handle returning of an error code
3271 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3272 if (rootSH) {
3273 *aCanGoForward = rootSH->CanGo(1);
3274 MOZ_LOG(gSHLog, LogLevel::Verbose,
3275 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
3276 return NS_OK;
3278 return NS_ERROR_FAILURE;
3281 NS_IMETHODIMP
3282 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
3283 if (!IsNavigationAllowed()) {
3284 return NS_OK; // JS may not handle returning of an error code
3287 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3288 mIsNavigating = true;
3290 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3291 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3292 ErrorResult rv;
3293 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
3294 return rv.StealNSResult();
3297 NS_IMETHODIMP
3298 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
3299 if (!IsNavigationAllowed()) {
3300 return NS_OK; // JS may not handle returning of an error code
3303 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3304 mIsNavigating = true;
3306 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3307 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3308 ErrorResult rv;
3309 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
3310 return rv.StealNSResult();
3313 // XXX(nika): We may want to stop exposing this API in the child process? Going
3314 // to a specific index from multiple different processes could definitely race.
3315 NS_IMETHODIMP
3316 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
3317 if (!IsNavigationAllowed()) {
3318 return NS_OK; // JS may not handle returning of an error code
3321 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3322 mIsNavigating = true;
3324 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3325 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3327 ErrorResult rv;
3328 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
3329 rv);
3330 return rv.StealNSResult();
3333 nsresult nsDocShell::LoadURI(nsIURI* aURI,
3334 const LoadURIOptions& aLoadURIOptions) {
3335 if (!IsNavigationAllowed()) {
3336 return NS_OK; // JS may not handle returning of an error code
3338 RefPtr<nsDocShellLoadState> loadState;
3339 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3340 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3341 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
3342 if (NS_FAILED(rv) || !loadState) {
3343 return NS_ERROR_FAILURE;
3346 return LoadURI(loadState, true);
3349 NS_IMETHODIMP
3350 nsDocShell::LoadURIFromScript(nsIURI* aURI,
3351 JS::Handle<JS::Value> aLoadURIOptions,
3352 JSContext* aCx) {
3353 // generate dictionary for aLoadURIOptions and forward call
3354 LoadURIOptions loadURIOptions;
3355 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3356 return NS_ERROR_INVALID_ARG;
3358 return LoadURI(aURI, loadURIOptions);
3361 nsresult nsDocShell::FixupAndLoadURIString(
3362 const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) {
3363 if (!IsNavigationAllowed()) {
3364 return NS_OK; // JS may not handle returning of an error code
3367 RefPtr<nsDocShellLoadState> loadState;
3368 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3369 mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState));
3371 uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3372 if (NS_ERROR_MALFORMED_URI == rv) {
3373 MOZ_LOG(gSHLog, LogLevel::Debug,
3374 ("Creating an active entry on nsDocShell %p to %s (because "
3375 "we're showing an error page)",
3376 this, NS_ConvertUTF16toUTF8(aURIString).get()));
3378 // We need to store a session history entry. We don't have a valid URI, so
3379 // we use about:blank instead.
3380 nsCOMPtr<nsIURI> uri;
3381 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
3382 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
3383 if (aLoadURIOptions.mTriggeringPrincipal) {
3384 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
3385 } else {
3386 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
3388 if (mozilla::SessionHistoryInParent()) {
3389 mActiveEntry = MakeUnique<SessionHistoryInfo>(
3390 uri, triggeringPrincipal, nullptr, nullptr, nullptr,
3391 nsLiteralCString("text/html"));
3392 mBrowsingContext->SetActiveSessionHistoryEntry(
3393 Nothing(), mActiveEntry.get(), MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
3394 /* aUpdatedCacheKey = */ 0);
3396 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(),
3397 nullptr) &&
3398 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3399 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3403 if (NS_FAILED(rv) || !loadState) {
3404 return NS_ERROR_FAILURE;
3407 return LoadURI(loadState, true);
3410 NS_IMETHODIMP
3411 nsDocShell::FixupAndLoadURIStringFromScript(
3412 const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions,
3413 JSContext* aCx) {
3414 // generate dictionary for aLoadURIOptions and forward call
3415 LoadURIOptions loadURIOptions;
3416 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3417 return NS_ERROR_INVALID_ARG;
3419 return FixupAndLoadURIString(aURIString, loadURIOptions);
3422 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3423 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3424 // such as the content-chrome boundary, don't fire the error event.
3425 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3426 return;
3429 // If embedder is same-process, then unblocking the load event is already
3430 // handled by nsDocLoader. Fire the error event on our embedder element if
3431 // requested.
3433 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3434 // more like the remote case when in-process.
3435 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3436 if (element) {
3437 if (aFireFrameErrorEvent) {
3438 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3439 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3440 fl->FireErrorEvent();
3444 return;
3447 // If we have a cross-process parent document, we must notify it that we no
3448 // longer block its load event. This is necessary for OOP sub-documents
3449 // because error documents do not result in a call to
3450 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3451 // (Obviously, we must do this before any of the returns below.)
3452 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3453 if (browserChild) {
3454 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3455 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3456 : EmbedderElementEventType::NoEvent);
3460 NS_IMETHODIMP
3461 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3462 const char16_t* aURL, nsIChannel* aFailedChannel,
3463 bool* aDisplayedErrorPage) {
3464 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3465 ("DOCSHELL %p DisplayLoadError %s\n", this,
3466 aURI ? aURI->GetSpecOrDefault().get() : ""));
3468 *aDisplayedErrorPage = false;
3469 // Get prompt and string bundle services
3470 nsCOMPtr<nsIPrompt> prompter;
3471 nsCOMPtr<nsIStringBundle> stringBundle;
3472 GetPromptAndStringBundle(getter_AddRefs(prompter),
3473 getter_AddRefs(stringBundle));
3475 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3476 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3478 const char* error = nullptr;
3479 // The key used to select the appropriate error message from the properties
3480 // file.
3481 const char* errorDescriptionID = nullptr;
3482 AutoTArray<nsString, 3> formatStrs;
3483 bool addHostPort = false;
3484 bool isBadStsCertError = false;
3485 nsresult rv = NS_OK;
3486 nsAutoString messageStr;
3487 nsAutoCString cssClass;
3488 nsAutoCString errorPage;
3490 errorPage.AssignLiteral("neterror");
3492 // Turn the error code into a human readable error message.
3493 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3494 NS_ENSURE_ARG_POINTER(aURI);
3496 // Extract the schemes into a comma delimited list.
3497 nsAutoCString scheme;
3498 aURI->GetScheme(scheme);
3499 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3500 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3501 while (nestedURI) {
3502 nsCOMPtr<nsIURI> tempURI;
3503 nsresult rv2;
3504 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3505 if (NS_SUCCEEDED(rv2) && tempURI) {
3506 tempURI->GetScheme(scheme);
3507 formatStrs[0].AppendLiteral(", ");
3508 AppendASCIItoUTF16(scheme, formatStrs[0]);
3510 nestedURI = do_QueryInterface(tempURI);
3512 error = "unknownProtocolFound";
3513 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3514 NS_ENSURE_ARG_POINTER(aURI);
3515 error = "fileNotFound";
3516 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3517 NS_ENSURE_ARG_POINTER(aURI);
3518 error = "fileAccessDenied";
3519 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3520 NS_ENSURE_ARG_POINTER(aURI);
3521 // Get the host
3522 nsAutoCString host;
3523 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3524 innermostURI->GetHost(host);
3525 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3526 errorDescriptionID = "dnsNotFound2";
3527 error = "dnsNotFound";
3528 } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3529 NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3530 NS_ENSURE_ARG_POINTER(aURI);
3531 addHostPort = true;
3532 error = "connectionFailure";
3533 } else if (NS_ERROR_NET_INTERRUPT == aError) {
3534 NS_ENSURE_ARG_POINTER(aURI);
3535 addHostPort = true;
3536 error = "netInterrupt";
3537 } else if (NS_ERROR_NET_TIMEOUT == aError ||
3538 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
3539 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
3540 NS_ENSURE_ARG_POINTER(aURI);
3541 // Get the host
3542 nsAutoCString host;
3543 aURI->GetHost(host);
3544 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3545 error = "netTimeout";
3546 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3547 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError ||
3548 NS_ERROR_CSP_NAVIGATE_TO_VIOLATION == aError) {
3549 // CSP error
3550 cssClass.AssignLiteral("neterror");
3551 error = "cspBlocked";
3552 } else if (NS_ERROR_XFO_VIOLATION == aError) {
3553 // XFO error
3554 cssClass.AssignLiteral("neterror");
3555 error = "xfoBlocked";
3556 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3557 nsCOMPtr<nsINSSErrorsService> nsserr =
3558 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3560 uint32_t errorClass;
3561 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3562 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3565 nsCOMPtr<nsITransportSecurityInfo> tsi;
3566 if (aFailedChannel) {
3567 aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
3569 if (tsi) {
3570 uint32_t securityState;
3571 tsi->GetSecurityState(&securityState);
3572 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3573 error = "sslv3Used";
3574 addHostPort = true;
3575 } else if (securityState &
3576 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3577 error = "weakCryptoUsed";
3578 addHostPort = true;
3580 } else {
3581 // No channel, let's obtain the generic error message
3582 if (nsserr) {
3583 nsserr->GetErrorMessage(aError, messageStr);
3586 // We don't have a message string here anymore but DisplayLoadError
3587 // requires a non-empty messageStr.
3588 messageStr.Truncate();
3589 messageStr.AssignLiteral(u" ");
3590 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3591 error = "nssBadCert";
3593 // If this is an HTTP Strict Transport Security host or a pinned host
3594 // and the certificate is bad, don't allow overrides (RFC 6797 section
3595 // 12.1).
3596 bool isStsHost = false;
3597 bool isPinnedHost = false;
3598 OriginAttributes attrsForHSTS;
3599 if (aFailedChannel) {
3600 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
3601 attrsForHSTS);
3602 } else {
3603 attrsForHSTS = GetOriginAttributes();
3606 if (XRE_IsParentProcess()) {
3607 nsCOMPtr<nsISiteSecurityService> sss =
3608 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3609 NS_ENSURE_SUCCESS(rv, rv);
3610 rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost);
3611 NS_ENSURE_SUCCESS(rv, rv);
3612 } else {
3613 mozilla::dom::ContentChild* cc =
3614 mozilla::dom::ContentChild::GetSingleton();
3615 cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost);
3617 nsCOMPtr<nsIPublicKeyPinningService> pkps =
3618 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
3619 NS_ENSURE_SUCCESS(rv, rv);
3620 rv = pkps->HostHasPins(aURI, &isPinnedHost);
3622 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3623 false)) {
3624 cssClass.AssignLiteral("expertBadCert");
3627 // HSTS/pinning takes precedence over the expert bad cert pref. We
3628 // never want to show the "Add Exception" button for these sites.
3629 // In the future we should differentiate between an HSTS host and a
3630 // pinned host and display a more informative message to the user.
3631 if (isStsHost || isPinnedHost) {
3632 isBadStsCertError = true;
3633 cssClass.AssignLiteral("badStsCert");
3636 errorPage.Assign("certerror");
3637 } else {
3638 error = "nssFailure2";
3640 } else if (NS_ERROR_PHISHING_URI == aError ||
3641 NS_ERROR_MALWARE_URI == aError ||
3642 NS_ERROR_UNWANTED_URI == aError ||
3643 NS_ERROR_HARMFUL_URI == aError) {
3644 nsAutoCString host;
3645 aURI->GetHost(host);
3646 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3648 // Malware and phishing detectors may want to use an alternate error
3649 // page, but if the pref's not set, we'll fall back on the standard page
3650 nsAutoCString alternateErrorPage;
3651 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3652 alternateErrorPage);
3653 if (NS_SUCCEEDED(rv)) {
3654 errorPage.Assign(alternateErrorPage);
3657 if (NS_ERROR_PHISHING_URI == aError) {
3658 error = "deceptiveBlocked";
3659 } else if (NS_ERROR_MALWARE_URI == aError) {
3660 error = "malwareBlocked";
3661 } else if (NS_ERROR_UNWANTED_URI == aError) {
3662 error = "unwantedBlocked";
3663 } else if (NS_ERROR_HARMFUL_URI == aError) {
3664 error = "harmfulBlocked";
3667 cssClass.AssignLiteral("blacklist");
3668 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3669 errorPage.AssignLiteral("tabcrashed");
3670 error = "tabcrashed";
3672 RefPtr<EventTarget> handler = mChromeEventHandler;
3673 if (handler) {
3674 nsCOMPtr<Element> element = do_QueryInterface(handler);
3675 element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
3678 // DisplayLoadError requires a non-empty messageStr to proceed and call
3679 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3680 // space which will be trimmed and thus treated as empty by the front-end.
3681 if (messageStr.IsEmpty()) {
3682 messageStr.AssignLiteral(u" ");
3684 } else if (NS_ERROR_FRAME_CRASHED == aError) {
3685 errorPage.AssignLiteral("framecrashed");
3686 error = "framecrashed";
3687 messageStr.AssignLiteral(u" ");
3688 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3689 errorPage.AssignLiteral("restartrequired");
3690 error = "restartrequired";
3692 // DisplayLoadError requires a non-empty messageStr to proceed and call
3693 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3694 // space which will be trimmed and thus treated as empty by the front-end.
3695 if (messageStr.IsEmpty()) {
3696 messageStr.AssignLiteral(u" ");
3698 } else {
3699 // Errors requiring simple formatting
3700 switch (aError) {
3701 case NS_ERROR_MALFORMED_URI:
3702 // URI is malformed
3703 error = "malformedURI";
3704 errorDescriptionID = "malformedURI2";
3705 break;
3706 case NS_ERROR_REDIRECT_LOOP:
3707 // Doc failed to load because the server generated too many redirects
3708 error = "redirectLoop";
3709 break;
3710 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3711 // Doc failed to load because PSM is not installed
3712 error = "unknownSocketType";
3713 break;
3714 case NS_ERROR_NET_RESET:
3715 // Doc failed to load because the server kept reseting the connection
3716 // before we could read any data from it
3717 error = "netReset";
3718 break;
3719 case NS_ERROR_DOCUMENT_NOT_CACHED:
3720 // Doc failed to load because the cache does not contain a copy of
3721 // the document.
3722 error = "notCached";
3723 break;
3724 case NS_ERROR_OFFLINE:
3725 // Doc failed to load because we are offline.
3726 error = "netOffline";
3727 break;
3728 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3729 // Doc navigation attempted while Printing or Print Preview
3730 error = "isprinting";
3731 break;
3732 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3733 // Port blocked for security reasons
3734 addHostPort = true;
3735 error = "deniedPortAccess";
3736 break;
3737 case NS_ERROR_UNKNOWN_PROXY_HOST:
3738 // Proxy hostname could not be resolved.
3739 error = "proxyResolveFailure";
3740 break;
3741 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3742 case NS_ERROR_PROXY_FORBIDDEN:
3743 case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3744 case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3745 case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3746 // Proxy connection was refused.
3747 error = "proxyConnectFailure";
3748 break;
3749 case NS_ERROR_INVALID_CONTENT_ENCODING:
3750 // Bad Content Encoding.
3751 error = "contentEncodingError";
3752 break;
3753 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3754 // Channel refused to load from an unrecognized content type.
3755 error = "unsafeContentType";
3756 break;
3757 case NS_ERROR_CORRUPTED_CONTENT:
3758 // Broken Content Detected. e.g. Content-MD5 check failure.
3759 error = "corruptedContentErrorv2";
3760 break;
3761 case NS_ERROR_INTERCEPTION_FAILED:
3762 // ServiceWorker intercepted request, but something went wrong.
3763 error = "corruptedContentErrorv2";
3764 break;
3765 case NS_ERROR_NET_INADEQUATE_SECURITY:
3766 // Server negotiated bad TLS for HTTP/2.
3767 error = "inadequateSecurityError";
3768 addHostPort = true;
3769 break;
3770 case NS_ERROR_BLOCKED_BY_POLICY:
3771 case NS_ERROR_DOM_COOP_FAILED:
3772 case NS_ERROR_DOM_COEP_FAILED:
3773 // Page blocked by policy
3774 error = "blockedByPolicy";
3775 break;
3776 case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3777 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3778 // HTTP/2 or HTTP/3 stack detected a protocol error
3779 error = "networkProtocolError";
3780 break;
3782 default:
3783 break;
3787 nsresult delegateErrorCode = aError;
3788 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3789 // caused this error, we replace the error-page with about:httpsonlyerror
3790 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
3791 errorPage.AssignLiteral("httpsonlyerror");
3792 delegateErrorCode = NS_ERROR_HTTPS_ONLY;
3793 } else if (isBadStsCertError) {
3794 delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
3797 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3798 nsCOMPtr<nsIURI> errorPageURI;
3799 rv = loadURIDelegate->HandleLoadError(
3800 aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
3801 getter_AddRefs(errorPageURI));
3802 // If the docshell is going away there's no point in showing an error page.
3803 if (NS_FAILED(rv) || mIsBeingDestroyed) {
3804 *aDisplayedErrorPage = false;
3805 return NS_OK;
3808 if (errorPageURI) {
3809 *aDisplayedErrorPage =
3810 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3811 return NS_OK;
3815 // Test if the error should be displayed
3816 if (!error) {
3817 return NS_OK;
3820 if (!errorDescriptionID) {
3821 errorDescriptionID = error;
3824 Telemetry::AccumulateCategoricalKeyed(
3825 IsSubframe() ? "frame"_ns : "top"_ns,
3826 mozilla::dom::LoadErrorToTelemetryLabel(aError));
3828 // Test if the error needs to be formatted
3829 if (!messageStr.IsEmpty()) {
3830 // already obtained message
3831 } else {
3832 if (addHostPort) {
3833 // Build up the host:port string.
3834 nsAutoCString hostport;
3835 if (aURI) {
3836 aURI->GetHostPort(hostport);
3837 } else {
3838 hostport.Assign('?');
3840 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3843 nsAutoCString spec;
3844 rv = NS_ERROR_NOT_AVAILABLE;
3845 auto& nextFormatStr = *formatStrs.AppendElement();
3846 if (aURI) {
3847 // displaying "file://" is aesthetically unpleasing and could even be
3848 // confusing to the user
3849 if (SchemeIsFile(aURI)) {
3850 aURI->GetPathQueryRef(spec);
3851 } else {
3852 aURI->GetSpec(spec);
3855 nsCOMPtr<nsITextToSubURI> textToSubURI(
3856 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3857 if (NS_SUCCEEDED(rv)) {
3858 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3860 } else {
3861 spec.Assign('?');
3863 if (NS_FAILED(rv)) {
3864 CopyUTF8toUTF16(spec, nextFormatStr);
3866 rv = NS_OK;
3868 nsAutoString str;
3869 rv =
3870 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3871 NS_ENSURE_SUCCESS(rv, rv);
3872 messageStr.Assign(str);
3875 // Display the error as a page or an alert prompt
3876 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3878 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3879 SchemeIsHTTPS(aURI)) {
3880 // Maybe TLS intolerant. Treat this as an SSL error.
3881 error = "nssFailure2";
3884 if (mBrowsingContext->GetUseErrorPages()) {
3885 // Display an error page
3886 nsresult loadedPage =
3887 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3888 cssClass.get(), aFailedChannel);
3889 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3890 } else {
3891 // The prompter reqires that our private window has a document (or it
3892 // asserts). Satisfy that assertion now since GetDoc will force
3893 // creation of one if it hasn't already been created.
3894 if (mScriptGlobal) {
3895 Unused << mScriptGlobal->GetDoc();
3898 // Display a message box
3899 prompter->Alert(nullptr, messageStr.get());
3902 return NS_OK;
3905 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3907 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3908 const char* aErrorPage,
3909 const char* aErrorType,
3910 const char16_t* aDescription,
3911 const char* aCSSClass,
3912 nsIChannel* aFailedChannel) {
3913 if (mIsBeingDestroyed) {
3914 return NS_ERROR_NOT_AVAILABLE;
3917 #if defined(DEBUG)
3918 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3919 nsAutoCString chanName;
3920 if (aFailedChannel) {
3921 aFailedChannel->GetName(chanName);
3922 } else {
3923 chanName.AssignLiteral("<no channel>");
3926 MOZ_LOG(gDocShellLog, LogLevel::Debug,
3927 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3928 this, aURI ? aURI->GetSpecOrDefault().get() : "",
3929 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3931 #endif
3933 nsAutoCString url;
3934 if (aURI) {
3935 nsresult rv = aURI->GetSpec(url);
3936 NS_ENSURE_SUCCESS(rv, rv);
3937 } else if (aURL) {
3938 CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3939 } else {
3940 return NS_ERROR_INVALID_POINTER;
3943 // Create a URL to pass all the error information through to the page.
3945 #undef SAFE_ESCAPE
3946 #define SAFE_ESCAPE(output, input, params) \
3947 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3948 return NS_ERROR_OUT_OF_MEMORY; \
3951 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3952 SAFE_ESCAPE(escapedUrl, url, url_Path);
3953 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3954 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3955 url_Path);
3956 if (aCSSClass) {
3957 nsCString cssClass(aCSSClass);
3958 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3960 nsCString errorPageUrl("about:");
3961 errorPageUrl.AppendASCII(aErrorPage);
3962 errorPageUrl.AppendLiteral("?e=");
3964 errorPageUrl.AppendASCII(escapedError.get());
3965 errorPageUrl.AppendLiteral("&u=");
3966 errorPageUrl.AppendASCII(escapedUrl.get());
3967 if ((strcmp(aErrorPage, "blocked") == 0) &&
3968 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
3969 errorPageUrl.AppendLiteral("&o=1");
3971 if (!escapedCSSClass.IsEmpty()) {
3972 errorPageUrl.AppendLiteral("&s=");
3973 errorPageUrl.AppendASCII(escapedCSSClass.get());
3975 errorPageUrl.AppendLiteral("&c=UTF-8");
3977 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
3978 int32_t cpsState;
3979 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
3980 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
3981 errorPageUrl.AppendLiteral("&captive=true");
3984 errorPageUrl.AppendLiteral("&d=");
3985 errorPageUrl.AppendASCII(escapedDescription.get());
3987 nsCOMPtr<nsIURI> errorPageURI;
3988 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3989 NS_ENSURE_SUCCESS(rv, rv);
3991 return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
3994 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
3995 nsIChannel* aFailedChannel) {
3996 mFailedChannel = aFailedChannel;
3997 mFailedURI = aFailedURI;
3998 mFailedLoadType = mLoadType;
4000 if (mLSHE) {
4001 // Abandon mLSHE's BFCache entry and create a new one. This way, if
4002 // we go back or forward to another SHEntry with the same doc
4003 // identifier, the error page won't persist.
4004 mLSHE->AbandonBFCacheEntry();
4007 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
4008 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4009 if (mBrowsingContext) {
4010 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
4012 loadState->SetLoadType(LOAD_ERROR_PAGE);
4013 loadState->SetFirstParty(true);
4014 loadState->SetSourceBrowsingContext(mBrowsingContext);
4015 if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
4016 // We keep the loading entry for the load that failed here. If the user
4017 // reloads we want to try to reload the original load, not the error page.
4018 loadState->SetLoadingSessionHistoryInfo(
4019 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
4021 return InternalLoad(loadState);
4024 NS_IMETHODIMP
4025 nsDocShell::Reload(uint32_t aReloadFlags) {
4026 if (!IsNavigationAllowed()) {
4027 return NS_OK; // JS may not handle returning of an error code
4030 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
4031 "Reload command not updated to use load flags!");
4032 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
4033 "Don't pass these flags to Reload");
4035 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
4036 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
4038 // Send notifications to the HistoryListener if any, about the impending
4039 // reload
4040 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
4041 if (mozilla::SessionHistoryInParent()) {
4042 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
4043 bool forceReload = IsForceReloadType(loadType);
4044 if (!XRE_IsParentProcess()) {
4045 ++mPendingReloadCount;
4046 RefPtr<nsDocShell> docShell(this);
4047 nsCOMPtr<nsIContentViewer> cv(mContentViewer);
4048 NS_ENSURE_STATE(cv);
4050 bool okToUnload = true;
4051 MOZ_TRY(cv->PermitUnload(&okToUnload));
4052 if (!okToUnload) {
4053 return NS_OK;
4056 RefPtr<Document> doc(GetDocument());
4057 RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
4058 nsCOMPtr<nsIURI> currentURI(mCurrentURI);
4059 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
4060 RefPtr<StopDetector> stopDetector = new StopDetector();
4061 nsCOMPtr<nsILoadGroup> loadGroup;
4062 GetLoadGroup(getter_AddRefs(loadGroup));
4063 if (loadGroup) {
4064 // loadGroup may be null in theory. In that case stopDetector just
4065 // doesn't do anything.
4066 loadGroup->AddRequest(stopDetector, nullptr);
4069 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
4070 mBrowsingContext, forceReload,
4071 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo,
4072 loadGroup, stopDetector](
4073 std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>,
4074 Maybe<bool>>&& aResult) {
4075 auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() {
4076 if (loadGroup) {
4077 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
4081 // Decrease mPendingReloadCount before any other early returns!
4082 if (--(docShell->mPendingReloadCount) > 0) {
4083 return;
4086 if (stopDetector->Canceled()) {
4087 return;
4089 bool canReload;
4090 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4091 Maybe<bool> reloadingActiveEntry;
4093 std::tie(canReload, loadState, reloadingActiveEntry) = aResult;
4095 if (!canReload) {
4096 return;
4099 if (loadState.isSome()) {
4100 MOZ_LOG(
4101 gSHLog, LogLevel::Debug,
4102 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
4103 loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
4104 docShell->LoadHistoryEntry(loadState.ref(), loadType,
4105 reloadingActiveEntry.ref());
4106 } else {
4107 MOZ_LOG(gSHLog, LogLevel::Debug,
4108 ("nsDocShell %p ReloadDocument", docShell.get()));
4109 ReloadDocument(docShell, doc, loadType, browsingContext,
4110 currentURI, referrerInfo,
4111 /* aNotifiedBeforeUnloadListeners */ true);
4114 [](mozilla::ipc::ResponseRejectReason) {});
4115 } else {
4116 // Parent process
4117 bool canReload = false;
4118 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
4119 Maybe<bool> reloadingActiveEntry;
4120 if (!mBrowsingContext->IsDiscarded()) {
4121 mBrowsingContext->Canonical()->NotifyOnHistoryReload(
4122 forceReload, canReload, loadState, reloadingActiveEntry);
4124 if (canReload) {
4125 if (loadState.isSome()) {
4126 MOZ_LOG(gSHLog, LogLevel::Debug,
4127 ("nsDocShell %p Reload - LoadHistoryEntry", this));
4128 LoadHistoryEntry(loadState.ref(), loadType,
4129 reloadingActiveEntry.ref());
4130 } else {
4131 MOZ_LOG(gSHLog, LogLevel::Debug,
4132 ("nsDocShell %p ReloadDocument", this));
4133 ReloadDocument(this, GetDocument(), loadType, mBrowsingContext,
4134 mCurrentURI, mReferrerInfo);
4138 return NS_OK;
4141 bool canReload = true;
4142 if (rootSH) {
4143 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
4146 if (!canReload) {
4147 return NS_OK;
4150 /* If you change this part of code, make sure bug 45297 does not re-occur */
4151 if (mOSHE) {
4152 return LoadHistoryEntry(
4153 mOSHE, loadType,
4154 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4157 if (mLSHE) { // In case a reload happened before the current load is done
4158 return LoadHistoryEntry(
4159 mLSHE, loadType,
4160 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4163 return ReloadDocument(this, GetDocument(), loadType, mBrowsingContext,
4164 mCurrentURI, mReferrerInfo);
4167 /* static */
4168 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
4169 uint32_t aLoadType,
4170 BrowsingContext* aBrowsingContext,
4171 nsIURI* aCurrentURI,
4172 nsIReferrerInfo* aReferrerInfo,
4173 bool aNotifiedBeforeUnloadListeners) {
4174 if (!aDocument) {
4175 return NS_OK;
4178 // Do not inherit owner from document
4179 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4180 nsAutoString srcdoc;
4181 nsIURI* baseURI = nullptr;
4182 nsCOMPtr<nsIURI> originalURI;
4183 nsCOMPtr<nsIURI> resultPrincipalURI;
4184 bool loadReplace = false;
4186 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
4187 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
4188 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
4190 nsAutoString contentTypeHint;
4191 aDocument->GetContentType(contentTypeHint);
4193 if (aDocument->IsSrcdocDocument()) {
4194 aDocument->GetSrcdocData(srcdoc);
4195 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4196 baseURI = aDocument->GetBaseURI();
4197 } else {
4198 srcdoc = VoidString();
4200 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
4201 if (chan) {
4202 uint32_t loadFlags;
4203 chan->GetLoadFlags(&loadFlags);
4204 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4205 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4206 if (httpChan) {
4207 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4210 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
4211 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4214 if (!triggeringPrincipal) {
4215 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4216 return NS_ERROR_FAILURE;
4219 // Stack variables to ensure changes to the member variables don't affect to
4220 // the call.
4221 nsCOMPtr<nsIURI> currentURI = aCurrentURI;
4223 // Reload always rewrites result principal URI.
4224 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4225 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4227 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
4228 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
4229 loadState->SetReferrerInfo(aReferrerInfo);
4230 loadState->SetOriginalURI(originalURI);
4231 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
4232 loadState->SetLoadReplace(loadReplace);
4233 loadState->SetTriggeringPrincipal(triggeringPrincipal);
4234 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
4235 loadState->SetPrincipalToInherit(triggeringPrincipal);
4236 loadState->SetCsp(csp);
4237 loadState->SetInternalLoadFlags(flags);
4238 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
4239 loadState->SetLoadType(aLoadType);
4240 loadState->SetFirstParty(true);
4241 loadState->SetSrcdocData(srcdoc);
4242 loadState->SetSourceBrowsingContext(aBrowsingContext);
4243 loadState->SetBaseURI(baseURI);
4244 loadState->SetHasValidUserGestureActivation(
4245 context && context->HasValidTransientUserGestureActivation());
4246 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
4247 return aDocShell->InternalLoad(loadState);
4250 NS_IMETHODIMP
4251 nsDocShell::Stop(uint32_t aStopFlags) {
4252 // Revoke any pending event related to content viewer restoration
4253 mRestorePresentationEvent.Revoke();
4255 if (mLoadType == LOAD_ERROR_PAGE) {
4256 if (mLSHE) {
4257 // Since error page loads never unset mLSHE, do so now
4258 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
4260 mActiveEntryIsLoadingFromSessionHistory = false;
4262 mFailedChannel = nullptr;
4263 mFailedURI = nullptr;
4266 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4267 // Stop the document loading and animations
4268 if (mContentViewer) {
4269 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4270 cv->Stop();
4272 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4273 // Stop the document loading only
4274 if (mContentViewer) {
4275 RefPtr<Document> doc = mContentViewer->GetDocument();
4276 if (doc) {
4277 doc->StopDocumentLoad();
4282 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4283 // Suspend any timers that were set for this loader. We'll clear
4284 // them out for good in CreateContentViewer.
4285 if (mRefreshURIList) {
4286 SuspendRefreshURIs();
4287 mSavedRefreshURIList.swap(mRefreshURIList);
4288 mRefreshURIList = nullptr;
4291 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4292 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4293 // redundant apis!
4294 Stop();
4296 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4297 // BFCache now, and the Stop above will have removed the DocumentChannel
4298 // from the loadgroup.
4299 mChannelToDisconnectOnPageHide = 0;
4302 for (auto* child : mChildList.ForwardRange()) {
4303 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
4304 if (shellAsNav) {
4305 shellAsNav->Stop(aStopFlags);
4309 return NS_OK;
4312 NS_IMETHODIMP
4313 nsDocShell::GetDocument(Document** aDocument) {
4314 NS_ENSURE_ARG_POINTER(aDocument);
4315 NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
4317 RefPtr<Document> doc = mContentViewer->GetDocument();
4318 if (!doc) {
4319 return NS_ERROR_NOT_AVAILABLE;
4322 doc.forget(aDocument);
4323 return NS_OK;
4326 NS_IMETHODIMP
4327 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4328 NS_ENSURE_ARG_POINTER(aURI);
4330 nsCOMPtr<nsIURI> uri = mCurrentURI;
4331 uri.forget(aURI);
4332 return NS_OK;
4335 NS_IMETHODIMP
4336 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4337 NS_ENSURE_ARG_POINTER(aSessionHistory);
4338 RefPtr<ChildSHistory> shistory = GetSessionHistory();
4339 shistory.forget(aSessionHistory);
4340 return NS_OK;
4343 //*****************************************************************************
4344 // nsDocShell::nsIWebPageDescriptor
4345 //*****************************************************************************
4347 NS_IMETHODIMP
4348 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
4349 const nsAString& aURI) {
4350 if (!aOtherDocShell) {
4351 return NS_ERROR_INVALID_POINTER;
4353 nsCOMPtr<nsIURI> newURI;
4354 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
4355 if (NS_FAILED(rv)) {
4356 return rv;
4359 RefPtr<nsDocShellLoadState> loadState;
4360 uint32_t cacheKey;
4361 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
4362 if (mozilla::SessionHistoryInParent()) {
4363 loadState = new nsDocShellLoadState(newURI);
4364 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
4365 return NS_ERROR_INVALID_POINTER;
4367 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
4368 } else {
4369 nsCOMPtr<nsISHEntry> entry;
4370 bool isOriginalSHE;
4371 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
4372 if (!entry) {
4373 return NS_ERROR_INVALID_POINTER;
4375 rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
4376 NS_ENSURE_SUCCESS(rv, rv);
4377 entry->GetCacheKey(&cacheKey);
4378 loadState->SetURI(newURI);
4379 loadState->SetSHEntry(nullptr);
4382 // We're doing a load of the page, via an API that
4383 // is only exposed to system code. The triggering principal for this load
4384 // should be the system principal.
4385 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4386 loadState->SetOriginalURI(nullptr);
4387 loadState->SetResultPrincipalURI(nullptr);
4389 return InternalLoad(loadState, Some(cacheKey));
4392 NS_IMETHODIMP
4393 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4394 MOZ_ASSERT(aPageDescriptor, "Null out param?");
4396 *aPageDescriptor = nullptr;
4398 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4399 if (src) {
4400 nsCOMPtr<nsISHEntry> dest;
4402 nsresult rv = src->Clone(getter_AddRefs(dest));
4403 if (NS_FAILED(rv)) {
4404 return rv;
4407 // null out inappropriate cloned attributes...
4408 dest->SetParent(nullptr);
4409 dest->SetIsSubFrame(false);
4411 return CallQueryInterface(dest, aPageDescriptor);
4414 return NS_ERROR_NOT_AVAILABLE;
4417 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
4418 const {
4419 nsCOMPtr<nsIInputStream> postData;
4420 if (mozilla::SessionHistoryInParent()) {
4421 if (mActiveEntry) {
4422 postData = mActiveEntry->GetPostData();
4423 } else if (mLoadingEntry) {
4424 postData = mLoadingEntry->mInfo.GetPostData();
4426 } else {
4427 if (mOSHE) {
4428 postData = mOSHE->GetPostData();
4429 } else if (mLSHE) {
4430 postData = mLSHE->GetPostData();
4434 return postData.forget();
4437 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4438 if (mozilla::SessionHistoryInParent()) {
4439 if (mActiveEntry) {
4440 return Some(mActiveEntry->GetCacheKey());
4443 if (mLoadingEntry) {
4444 return Some(mLoadingEntry->mInfo.GetCacheKey());
4446 } else {
4447 if (mOSHE) {
4448 return Some(mOSHE->GetCacheKey());
4451 if (mLSHE) {
4452 return Some(mLSHE->GetCacheKey());
4456 return Nothing();
4459 bool nsDocShell::FillLoadStateFromCurrentEntry(
4460 nsDocShellLoadState& aLoadState) {
4461 if (mLoadingEntry) {
4462 mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
4463 return true;
4465 if (mActiveEntry) {
4466 mActiveEntry->FillLoadInfo(aLoadState);
4467 return true;
4469 return false;
4472 //*****************************************************************************
4473 // nsDocShell::nsIBaseWindow
4474 //*****************************************************************************
4476 NS_IMETHODIMP
4477 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4478 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4479 int32_t aWidth, int32_t aHeight) {
4480 SetParentWidget(aParentWidget);
4481 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4482 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE);
4484 return NS_OK;
4487 NS_IMETHODIMP
4488 nsDocShell::Destroy() {
4489 // XXX: We allow this function to be called just once. If you are going to
4490 // reset new variables in this function, please make sure the variables will
4491 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4492 // in the setter functions for the variables would be enough.
4493 if (mIsBeingDestroyed) {
4494 return NS_ERROR_DOCSHELL_DYING;
4497 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4498 "Unexpected item type in docshell");
4500 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4501 if (serv) {
4502 const char* msg = mItemType == typeContent
4503 ? NS_WEBNAVIGATION_DESTROY
4504 : NS_CHROME_WEBNAVIGATION_DESTROY;
4505 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4508 mIsBeingDestroyed = true;
4510 // Brak the cycle with the initial client, if present.
4511 mInitialClientSource.reset();
4513 // Make sure we don't record profile timeline markers anymore
4514 SetRecordProfileTimelineMarkers(false);
4516 // Make sure to blow away our mLoadingURI just in case. No loads
4517 // from inside this pagehide.
4518 mLoadingURI = nullptr;
4520 // Fire unload event before we blow anything away.
4521 (void)FirePageHideNotification(true);
4523 // Clear pointers to any detached nsEditorData that's lying
4524 // around in shistory entries. Breaks cycle. See bug 430921.
4525 if (mOSHE) {
4526 mOSHE->SetEditorData(nullptr);
4528 if (mLSHE) {
4529 mLSHE->SetEditorData(nullptr);
4532 // Note: mContentListener can be null if Init() failed and we're being
4533 // called from the destructor.
4534 if (mContentListener) {
4535 mContentListener->DropDocShellReference();
4536 mContentListener->SetParentContentListener(nullptr);
4537 // Note that we do NOT set mContentListener to null here; that
4538 // way if someone tries to do a load in us after this point
4539 // the nsDSURIContentListener will block it. All of which
4540 // means that we should do this before calling Stop(), of
4541 // course.
4544 // Stop any URLs that are currently being loaded...
4545 Stop(nsIWebNavigation::STOP_ALL);
4547 mEditorData = nullptr;
4549 // Save the state of the current document, before destroying the window.
4550 // This is needed to capture the state of a frameset when the new document
4551 // causes the frameset to be destroyed...
4552 PersistLayoutHistoryState();
4554 // Remove this docshell from its parent's child list
4555 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4556 do_QueryInterface(GetAsSupports(mParent));
4557 if (docShellParentAsItem) {
4558 docShellParentAsItem->RemoveChild(this);
4561 if (mContentViewer) {
4562 mContentViewer->Close(nullptr);
4563 mContentViewer->Destroy();
4564 mContentViewer = nullptr;
4567 nsDocLoader::Destroy();
4569 mParentWidget = nullptr;
4570 mCurrentURI = nullptr;
4572 if (mScriptGlobal) {
4573 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4574 mScriptGlobal = nullptr;
4577 if (GetSessionHistory()) {
4578 // We want to destroy these content viewers now rather than
4579 // letting their destruction wait for the session history
4580 // entries to get garbage collected. (Bug 488394)
4581 GetSessionHistory()->EvictLocalContentViewers();
4584 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
4585 mBrowsingContext->PrepareForProcessChange();
4588 SetTreeOwner(nullptr);
4590 mBrowserChild = nullptr;
4592 mChromeEventHandler = nullptr;
4594 // Cancel any timers that were set for this docshell; this is needed
4595 // to break the cycle between us and the timers.
4596 CancelRefreshURITimers();
4598 return NS_OK;
4601 double nsDocShell::GetWidgetCSSToDeviceScale() {
4602 if (mParentWidget) {
4603 return mParentWidget->GetDefaultScale().scale;
4605 if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) {
4606 return ownerWindow->GetWidgetCSSToDeviceScale();
4608 return 1.0;
4611 NS_IMETHODIMP
4612 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4613 if (mParentWidget) {
4614 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4615 return NS_OK;
4618 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4619 if (ownerWindow) {
4620 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4623 *aScale = 1.0;
4624 return NS_OK;
4627 NS_IMETHODIMP
4628 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4629 mBounds.MoveTo(aX, aY);
4631 if (mContentViewer) {
4632 NS_ENSURE_SUCCESS(mContentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4635 return NS_OK;
4638 NS_IMETHODIMP
4639 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4640 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4641 if (ownerWindow) {
4642 return ownerWindow->SetPositionDesktopPix(aX, aY);
4645 double scale = 1.0;
4646 GetDevicePixelsPerDesktopPixel(&scale);
4647 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4650 NS_IMETHODIMP
4651 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4652 return GetPositionAndSize(aX, aY, nullptr, nullptr);
4655 NS_IMETHODIMP
4656 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4657 int32_t x = 0, y = 0;
4658 GetPosition(&x, &y);
4659 return SetPositionAndSize(x, y, aWidth, aHeight,
4660 aRepaint ? nsIBaseWindow::eRepaint : 0);
4663 NS_IMETHODIMP
4664 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4665 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4668 NS_IMETHODIMP
4669 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4670 int32_t aHeight, uint32_t aFlags) {
4671 mBounds.SetRect(aX, aY, aWidth, aHeight);
4673 // Hold strong ref, since SetBounds can make us null out mContentViewer
4674 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
4675 if (viewer) {
4676 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4677 ? nsIContentViewer::eDelayResize
4678 : 0;
4679 // XXX Border figured in here or is that handled elsewhere?
4680 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4681 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4684 return NS_OK;
4687 NS_IMETHODIMP
4688 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4689 int32_t* aHeight) {
4690 if (mParentWidget) {
4691 // ensure size is up-to-date if window has changed resolution
4692 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4693 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4696 // We should really consider just getting this information from
4697 // our window instead of duplicating the storage and code...
4698 if (aWidth || aHeight) {
4699 // Caller wants to know our size; make sure to give them up to
4700 // date information.
4701 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4702 if (doc) {
4703 doc->FlushPendingNotifications(FlushType::Layout);
4707 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4708 return NS_OK;
4711 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4712 int32_t* aHeight) {
4713 if (aX) {
4714 *aX = mBounds.X();
4716 if (aY) {
4717 *aY = mBounds.Y();
4719 if (aWidth) {
4720 *aWidth = mBounds.Width();
4722 if (aHeight) {
4723 *aHeight = mBounds.Height();
4727 NS_IMETHODIMP
4728 nsDocShell::SetDimensions(DimensionRequest&& aRequest) {
4729 return NS_ERROR_NOT_IMPLEMENTED;
4732 NS_IMETHODIMP
4733 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
4734 int32_t* aY, int32_t* aCX, int32_t* aCY) {
4735 return NS_ERROR_NOT_IMPLEMENTED;
4738 NS_IMETHODIMP
4739 nsDocShell::Repaint(bool aForce) {
4740 PresShell* presShell = GetPresShell();
4741 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4743 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4744 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4746 viewManager->InvalidateAllViews();
4747 return NS_OK;
4750 NS_IMETHODIMP
4751 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4752 NS_ENSURE_ARG_POINTER(aParentWidget);
4754 *aParentWidget = mParentWidget;
4755 NS_IF_ADDREF(*aParentWidget);
4757 return NS_OK;
4760 NS_IMETHODIMP
4761 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4762 MOZ_ASSERT(!mIsBeingDestroyed);
4763 mParentWidget = aParentWidget;
4765 return NS_OK;
4768 NS_IMETHODIMP
4769 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4770 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4772 if (mParentWidget) {
4773 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4774 } else {
4775 *aParentNativeWindow = nullptr;
4778 return NS_OK;
4781 NS_IMETHODIMP
4782 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4783 return NS_ERROR_NOT_IMPLEMENTED;
4786 NS_IMETHODIMP
4787 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4788 // the nativeHandle should be accessed from nsIAppWindow
4789 return NS_ERROR_NOT_IMPLEMENTED;
4792 NS_IMETHODIMP
4793 nsDocShell::GetVisibility(bool* aVisibility) {
4794 NS_ENSURE_ARG_POINTER(aVisibility);
4796 *aVisibility = false;
4798 if (!mContentViewer) {
4799 return NS_OK;
4802 PresShell* presShell = GetPresShell();
4803 if (!presShell) {
4804 return NS_OK;
4807 // get the view manager
4808 nsViewManager* vm = presShell->GetViewManager();
4809 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4811 // get the root view
4812 nsView* view = vm->GetRootView(); // views are not ref counted
4813 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4815 // if our root view is hidden, we are not visible
4816 if (view->GetVisibility() == ViewVisibility::Hide) {
4817 return NS_OK;
4820 // otherwise, we must walk up the document and view trees checking
4821 // for a hidden view, unless we're an off screen browser, which
4822 // would make this test meaningless.
4824 RefPtr<nsDocShell> docShell = this;
4825 RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4826 while (parentItem) {
4827 // Null-check for crash in bug 267804
4828 if (!parentItem->GetPresShell()) {
4829 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4830 return NS_OK;
4833 vm = docShell->GetPresShell()->GetViewManager();
4834 if (vm) {
4835 view = vm->GetRootView();
4838 if (view) {
4839 view = view->GetParent(); // anonymous inner view
4840 if (view) {
4841 view = view->GetParent(); // subdocumentframe's view
4845 nsIFrame* frame = view ? view->GetFrame() : nullptr;
4846 if (frame && !frame->IsVisibleConsideringAncestors(
4847 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
4848 return NS_OK;
4851 docShell = parentItem;
4852 parentItem = docShell->GetInProcessParentDocshell();
4855 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4856 if (!treeOwnerAsWin) {
4857 *aVisibility = true;
4858 return NS_OK;
4861 // Check with the tree owner as well to give embedders a chance to
4862 // expose visibility as well.
4863 nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility);
4864 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
4865 // The tree owner had no opinion on our visibility.
4866 *aVisibility = true;
4867 return NS_OK;
4869 return rv;
4872 void nsDocShell::ActivenessMaybeChanged() {
4873 const bool isActive = mBrowsingContext->IsActive();
4874 if (RefPtr<PresShell> presShell = GetPresShell()) {
4875 presShell->ActivenessMaybeChanged();
4878 // Tell the window about it
4879 if (mScriptGlobal) {
4880 mScriptGlobal->SetIsBackground(!isActive);
4881 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4882 // Update orientation when the top-level browsing context becomes active.
4883 if (isActive && mBrowsingContext->IsTop()) {
4884 // We only care about the top-level browsing context.
4885 auto orientation = mBrowsingContext->GetOrientationLock();
4886 ScreenOrientation::UpdateActiveOrientationLock(orientation);
4889 doc->PostVisibilityUpdateEvent();
4893 // Tell the nsDOMNavigationTiming about it
4894 RefPtr<nsDOMNavigationTiming> timing = mTiming;
4895 if (!timing && mContentViewer) {
4896 if (Document* doc = mContentViewer->GetDocument()) {
4897 timing = doc->GetNavigationTiming();
4900 if (timing) {
4901 timing->NotifyDocShellStateChanged(
4902 isActive ? nsDOMNavigationTiming::DocShellState::eActive
4903 : nsDOMNavigationTiming::DocShellState::eInactive);
4906 // Restart or stop meta refresh timers if necessary
4907 if (mDisableMetaRefreshWhenInactive) {
4908 if (isActive) {
4909 ResumeRefreshURIs();
4910 } else {
4911 SuspendRefreshURIs();
4915 if (InputTaskManager::CanSuspendInputEvent()) {
4916 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
4920 NS_IMETHODIMP
4921 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4922 if (!mWillChangeProcess) {
4923 // Intentionally ignoring handling discarded browsing contexts.
4924 Unused << mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4925 } else {
4926 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4927 // shutdown. Sorry DevTools, your DocShell is in another process.
4928 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4930 return NS_OK;
4933 NS_IMETHODIMP
4934 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4935 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4936 return NS_OK;
4939 NS_IMETHODIMP
4940 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4941 NS_ENSURE_ARG_POINTER(aFailedChannel);
4942 Document* doc = GetDocument();
4943 if (!doc) {
4944 *aFailedChannel = nullptr;
4945 return NS_OK;
4947 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4948 return NS_OK;
4951 NS_IMETHODIMP
4952 nsDocShell::SetVisibility(bool aVisibility) {
4953 // Show()/Hide() may change mContentViewer.
4954 nsCOMPtr<nsIContentViewer> cv = mContentViewer;
4955 if (!cv) {
4956 return NS_OK;
4958 if (aVisibility) {
4959 cv->Show();
4960 } else {
4961 cv->Hide();
4964 return NS_OK;
4967 NS_IMETHODIMP
4968 nsDocShell::GetEnabled(bool* aEnabled) {
4969 NS_ENSURE_ARG_POINTER(aEnabled);
4970 *aEnabled = true;
4971 return NS_ERROR_NOT_IMPLEMENTED;
4974 NS_IMETHODIMP
4975 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4977 NS_IMETHODIMP
4978 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4979 // We don't create our own widget, so simply return the parent one.
4980 return GetParentWidget(aMainWidget);
4983 NS_IMETHODIMP
4984 nsDocShell::GetTitle(nsAString& aTitle) {
4985 aTitle = mTitle;
4986 return NS_OK;
4989 NS_IMETHODIMP
4990 nsDocShell::SetTitle(const nsAString& aTitle) {
4991 // Avoid unnecessary updates of the title if the URI and the title haven't
4992 // changed.
4993 if (mTitleValidForCurrentURI && mTitle == aTitle) {
4994 return NS_OK;
4997 // Store local title
4998 mTitle = aTitle;
4999 mTitleValidForCurrentURI = true;
5001 // When title is set on the top object it should then be passed to the
5002 // tree owner.
5003 if (mBrowsingContext->IsTop()) {
5004 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
5005 if (treeOwnerAsWin) {
5006 treeOwnerAsWin->SetTitle(aTitle);
5010 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
5011 UpdateGlobalHistoryTitle(mCurrentURI);
5014 // Update SessionHistory with the document's title.
5015 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
5016 SetTitleOnHistoryEntry(true);
5019 return NS_OK;
5022 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
5023 if (mOSHE) {
5024 mOSHE->SetTitle(mTitle);
5027 if (mActiveEntry && mBrowsingContext) {
5028 mActiveEntry->SetTitle(mTitle);
5029 if (aUpdateEntryInSessionHistory) {
5030 if (XRE_IsParentProcess()) {
5031 SessionHistoryEntry* entry =
5032 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
5033 if (entry) {
5034 entry->SetTitle(mTitle);
5036 } else {
5037 mozilla::Unused
5038 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
5039 mBrowsingContext, mTitle);
5045 nsPoint nsDocShell::GetCurScrollPos() {
5046 nsPoint scrollPos;
5047 if (nsIScrollableFrame* sf = GetRootScrollFrame()) {
5048 scrollPos = sf->GetVisualViewportOffset();
5050 return scrollPos;
5053 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
5054 int32_t aCurVerticalPos) {
5055 nsIScrollableFrame* sf = GetRootScrollFrame();
5056 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
5058 ScrollMode scrollMode =
5059 sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
5061 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
5062 sf->ScrollTo(targetPos, scrollMode);
5064 // Set the visual viewport offset as well.
5066 RefPtr<PresShell> presShell = GetPresShell();
5067 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
5069 nsPresContext* presContext = presShell->GetPresContext();
5070 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
5072 // Only the root content document can have a distinct visual viewport offset.
5073 if (!presContext->IsRootContentDocumentCrossProcess()) {
5074 return NS_OK;
5077 // Not on a platform with a distinct visual viewport - don't bother setting
5078 // the visual viewport offset.
5079 if (!presShell->IsVisualViewportSizeSet()) {
5080 return NS_OK;
5083 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
5084 scrollMode);
5086 return NS_OK;
5089 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
5090 if (mScrollbarPref == aPref) {
5091 return;
5093 mScrollbarPref = aPref;
5094 auto* ps = GetPresShell();
5095 if (!ps) {
5096 return;
5098 nsIFrame* scrollFrame = ps->GetRootScrollFrame();
5099 if (!scrollFrame) {
5100 return;
5102 ps->FrameNeedsReflow(scrollFrame,
5103 IntrinsicDirty::FrameAncestorsAndDescendants,
5104 NS_FRAME_IS_DIRTY);
5107 //*****************************************************************************
5108 // nsDocShell::nsIRefreshURI
5109 //*****************************************************************************
5111 NS_IMETHODIMP
5112 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5113 uint32_t aDelay) {
5114 MOZ_ASSERT(!mIsBeingDestroyed);
5116 NS_ENSURE_ARG(aURI);
5118 /* Check if Meta refresh/redirects are permitted. Some
5119 * embedded applications may not want to do this.
5120 * Must do this before sending out NOTIFY_REFRESH events
5121 * because listeners may have side effects (e.g. displaying a
5122 * button to manually trigger the refresh later).
5124 bool allowRedirects = true;
5125 GetAllowMetaRedirects(&allowRedirects);
5126 if (!allowRedirects) {
5127 return NS_OK;
5130 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5131 // give them a chance to block this refresh.
5132 bool sameURI;
5133 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5134 if (NS_FAILED(rv)) {
5135 sameURI = false;
5137 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
5138 return NS_OK;
5141 nsCOMPtr<nsITimerCallback> refreshTimer =
5142 new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
5144 BusyFlags busyFlags = GetBusyFlags();
5146 if (!mRefreshURIList) {
5147 mRefreshURIList = nsArray::Create();
5150 if (busyFlags & BUSY_FLAGS_BUSY ||
5151 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
5152 // We don't want to create the timer right now. Instead queue up the
5153 // request and trigger the timer in EndPageLoad() or whenever we become
5154 // active.
5155 mRefreshURIList->AppendElement(refreshTimer);
5156 } else {
5157 // There is no page loading going on right now. Create the
5158 // timer and fire it right away.
5159 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5160 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
5162 nsCOMPtr<nsITimer> timer;
5163 MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
5164 nsITimer::TYPE_ONE_SHOT));
5166 mRefreshURIList->AppendElement(timer); // owning timer ref
5168 return NS_OK;
5171 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
5172 nsIPrincipal* aPrincipal,
5173 uint32_t aDelay,
5174 nsITimer* aTimer) {
5175 MOZ_ASSERT(aTimer, "Must have a timer here");
5177 // Remove aTimer from mRefreshURIList if needed
5178 if (mRefreshURIList) {
5179 uint32_t n = 0;
5180 mRefreshURIList->GetLength(&n);
5182 for (uint32_t i = 0; i < n; ++i) {
5183 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5184 if (timer == aTimer) {
5185 mRefreshURIList->RemoveElementAt(i);
5186 break;
5191 return ForceRefreshURI(aURI, aPrincipal, aDelay);
5194 NS_IMETHODIMP
5195 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5196 uint32_t aDelay) {
5197 NS_ENSURE_ARG(aURI);
5199 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
5200 loadState->SetOriginalURI(mCurrentURI);
5201 loadState->SetResultPrincipalURI(aURI);
5202 loadState->SetResultPrincipalURIIsSome(true);
5203 loadState->SetKeepResultPrincipalURIIfSet(true);
5204 loadState->SetIsMetaRefresh(true);
5206 // Set the triggering pricipal to aPrincipal if available, or current
5207 // document's principal otherwise.
5208 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
5209 RefPtr<Document> doc = GetDocument();
5210 if (!principal) {
5211 if (!doc) {
5212 return NS_ERROR_FAILURE;
5214 principal = doc->NodePrincipal();
5216 loadState->SetTriggeringPrincipal(principal);
5217 if (doc) {
5218 loadState->SetCsp(doc->GetCsp());
5219 loadState->SetHasValidUserGestureActivation(
5220 doc->HasValidTransientUserGestureActivation());
5221 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
5224 loadState->SetPrincipalIsExplicit(true);
5226 /* Check if this META refresh causes a redirection
5227 * to another site.
5229 bool equalUri = false;
5230 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5232 nsCOMPtr<nsIReferrerInfo> referrerInfo;
5233 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
5234 /* It is a META refresh based redirection within the threshold time
5235 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5236 * Pass a REPLACE flag to LoadURI().
5238 loadState->SetLoadType(LOAD_REFRESH_REPLACE);
5240 /* For redirects we mimic HTTP, which passes the
5241 * original referrer.
5242 * We will pass in referrer but will not send to server
5244 if (mReferrerInfo) {
5245 referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5246 ->CloneWithNewSendReferrer(false);
5248 } else {
5249 loadState->SetLoadType(LOAD_REFRESH);
5250 /* We do need to pass in a referrer, but we don't want it to
5251 * be sent to the server.
5252 * For most refreshes the current URI is an appropriate
5253 * internal referrer.
5255 referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5258 loadState->SetReferrerInfo(referrerInfo);
5259 loadState->SetLoadFlags(
5260 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5261 loadState->SetFirstParty(true);
5264 * LoadURI(...) will cancel all refresh timers... This causes the
5265 * Timer and its refreshData instance to be released...
5267 LoadURI(loadState, false);
5269 return NS_OK;
5272 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
5273 const char16_t* aEnd) {
5274 const char16_t* iter = aStart;
5275 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
5276 ++iter;
5278 return iter;
5281 static std::tuple<const char16_t*, const char16_t*> ExtractURLString(
5282 const char16_t* aPosition, const char16_t* aEnd) {
5283 MOZ_ASSERT(aPosition != aEnd);
5285 // 1. Let urlString be the substring of input from the code point at
5286 // position to the end of the string.
5287 const char16_t* urlStart = aPosition;
5288 const char16_t* urlEnd = aEnd;
5290 // 2. If the code point in input pointed to by position is U+0055 (U) or
5291 // U+0075 (u), then advance position to the next code point.
5292 // Otherwise, jump to the step labeled skip quotes.
5293 if (*aPosition == 'U' || *aPosition == 'u') {
5294 ++aPosition;
5296 // 3. If the code point in input pointed to by position is U+0052 (R) or
5297 // U+0072 (r), then advance position to the next code point.
5298 // Otherwise, jump to the step labeled parse.
5299 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
5300 return std::make_tuple(urlStart, urlEnd);
5303 ++aPosition;
5305 // 4. If the code point in input pointed to by position is U+004C (L) or
5306 // U+006C (l), then advance position to the next code point.
5307 // Otherwise, jump to the step labeled parse.
5308 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
5309 return std::make_tuple(urlStart, urlEnd);
5312 ++aPosition;
5314 // 5. Skip ASCII whitespace within input given position.
5315 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5317 // 6. If the code point in input pointed to by position is U+003D (=),
5318 // then advance position to the next code point. Otherwise, jump to
5319 // the step labeled parse.
5320 if (aPosition == aEnd || *aPosition != '=') {
5321 return std::make_tuple(urlStart, urlEnd);
5324 ++aPosition;
5326 // 7. Skip ASCII whitespace within input given position.
5327 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5330 // 8. Skip quotes: If the code point in input pointed to by position is
5331 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5332 // advance position to the next code point. Otherwise, let quote be
5333 // the empty string.
5334 Maybe<char> quote;
5335 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
5336 quote.emplace(*aPosition);
5337 ++aPosition;
5340 // 9. Set urlString to the substring of input from the code point at
5341 // position to the end of the string.
5342 urlStart = aPosition;
5343 urlEnd = aEnd;
5345 // 10. If quote is not the empty string, and there is a code point in
5346 // urlString equal to quote, then truncate urlString at that code
5347 // point, so that it and all subsequent code points are removed.
5348 const char16_t* quotePos;
5349 if (quote.isSome() &&
5350 (quotePos = nsCharTraits<char16_t>::find(
5351 urlStart, std::distance(urlStart, aEnd), quote.value()))) {
5352 urlEnd = quotePos;
5355 return std::make_tuple(urlStart, urlEnd);
5358 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
5359 const nsAString& aHeader) {
5360 if (mIsBeingDestroyed) {
5361 return;
5364 const char16_t* position = aHeader.BeginReading();
5365 const char16_t* end = aHeader.EndReading();
5367 // See
5368 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5370 // 3. Skip ASCII whitespace
5371 position = SkipASCIIWhitespace(position, end);
5373 // 4. Let time be 0.
5374 CheckedInt<uint32_t> milliSeconds;
5376 // 5. Collect a sequence of code points that are ASCII digits
5377 const char16_t* digitsStart = position;
5378 while (position != end && mozilla::IsAsciiDigit(*position)) {
5379 ++position;
5382 if (position == digitsStart) {
5383 // 6. If timeString is the empty string, then:
5384 // 1. If the code point in input pointed to by position is not U+002E
5385 // (.), then return.
5386 if (position == end || *position != '.') {
5387 return;
5389 } else {
5390 // 7. Otherwise, set time to the result of parsing timeString using the
5391 // rules for parsing non-negative integers.
5392 nsContentUtils::ParseHTMLIntegerResultFlags result;
5393 uint32_t seconds =
5394 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
5395 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
5396 if (result & nsContentUtils::eParseHTMLInteger_Error) {
5397 // The spec assumes no errors here (since we only pass ASCII digits in),
5398 // but we can still overflow, so this block should deal with that (and
5399 // only that).
5400 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
5401 return;
5403 MOZ_ASSERT(
5404 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
5406 milliSeconds = seconds;
5407 milliSeconds *= 1000;
5408 if (!milliSeconds.isValid()) {
5409 return;
5413 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5414 // STOP characters (.) from input given position. Ignore any collected
5415 // characters.
5416 while (position != end &&
5417 (mozilla::IsAsciiDigit(*position) || *position == '.')) {
5418 ++position;
5421 // 9. Let urlRecord be document's URL.
5422 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
5424 // 10. If position is not past the end of input
5425 if (position != end) {
5426 // 1. If the code point in input pointed to by position is not U+003B (;),
5427 // U+002C (,), or ASCII whitespace, then return.
5428 if (*position != ';' && *position != ',' &&
5429 !mozilla::IsAsciiWhitespace(*position)) {
5430 return;
5433 // 2. Skip ASCII whitespace within input given position.
5434 position = SkipASCIIWhitespace(position, end);
5436 // 3. If the code point in input pointed to by position is U+003B (;) or
5437 // U+002C (,), then advance position to the next code point.
5438 if (position != end && (*position == ';' || *position == ',')) {
5439 ++position;
5441 // 4. Skip ASCII whitespace within input given position.
5442 position = SkipASCIIWhitespace(position, end);
5445 // 11. If position is not past the end of input, then:
5446 if (position != end) {
5447 const char16_t* urlStart;
5448 const char16_t* urlEnd;
5450 // 1-10. See ExtractURLString.
5451 std::tie(urlStart, urlEnd) = ExtractURLString(position, end);
5453 // 11. Parse: Parse urlString relative to document. If that fails, return.
5454 // Otherwise, set urlRecord to the resulting URL record.
5455 nsresult rv =
5456 NS_NewURI(getter_AddRefs(urlRecord),
5457 Substring(urlStart, std::distance(urlStart, urlEnd)),
5458 /* charset = */ nullptr, aDocument->GetDocBaseURI());
5459 NS_ENSURE_SUCCESS_VOID(rv);
5463 nsIPrincipal* principal = aDocument->NodePrincipal();
5464 nsCOMPtr<nsIScriptSecurityManager> securityManager =
5465 nsContentUtils::GetSecurityManager();
5466 nsresult rv = securityManager->CheckLoadURIWithPrincipal(
5467 principal, urlRecord,
5468 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5469 aDocument->InnerWindowID());
5470 NS_ENSURE_SUCCESS_VOID(rv);
5472 bool isjs = true;
5473 rv = NS_URIChainHasFlags(
5474 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5475 NS_ENSURE_SUCCESS_VOID(rv);
5477 if (isjs) {
5478 return;
5481 RefreshURI(urlRecord, principal, milliSeconds.value());
5484 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5485 if (!aTimerList) {
5486 return;
5489 uint32_t n = 0;
5490 aTimerList->GetLength(&n);
5492 while (n) {
5493 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5495 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5497 if (timer) {
5498 timer->Cancel();
5503 NS_IMETHODIMP
5504 nsDocShell::CancelRefreshURITimers() {
5505 DoCancelRefreshURITimers(mRefreshURIList);
5506 DoCancelRefreshURITimers(mSavedRefreshURIList);
5507 DoCancelRefreshURITimers(mBFCachedRefreshURIList);
5508 mRefreshURIList = nullptr;
5509 mSavedRefreshURIList = nullptr;
5510 mBFCachedRefreshURIList = nullptr;
5512 return NS_OK;
5515 NS_IMETHODIMP
5516 nsDocShell::GetRefreshPending(bool* aResult) {
5517 if (!mRefreshURIList) {
5518 *aResult = false;
5519 return NS_OK;
5522 uint32_t count;
5523 nsresult rv = mRefreshURIList->GetLength(&count);
5524 if (NS_SUCCEEDED(rv)) {
5525 *aResult = (count != 0);
5527 return rv;
5530 void nsDocShell::RefreshURIToQueue() {
5531 if (mRefreshURIList) {
5532 uint32_t n = 0;
5533 mRefreshURIList->GetLength(&n);
5535 for (uint32_t i = 0; i < n; ++i) {
5536 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5537 if (!timer) {
5538 continue; // this must be a nsRefreshURI already
5541 // Replace this timer object with a nsRefreshTimer object.
5542 nsCOMPtr<nsITimerCallback> callback;
5543 timer->GetCallback(getter_AddRefs(callback));
5545 timer->Cancel();
5547 mRefreshURIList->ReplaceElementAt(callback, i);
5552 NS_IMETHODIMP
5553 nsDocShell::SuspendRefreshURIs() {
5554 RefreshURIToQueue();
5556 // Suspend refresh URIs for our child shells as well.
5557 for (auto* child : mChildList.ForwardRange()) {
5558 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5559 if (shell) {
5560 shell->SuspendRefreshURIs();
5564 return NS_OK;
5567 NS_IMETHODIMP
5568 nsDocShell::ResumeRefreshURIs() {
5569 RefreshURIFromQueue();
5571 // Resume refresh URIs for our child shells as well.
5572 for (auto* child : mChildList.ForwardRange()) {
5573 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5574 if (shell) {
5575 shell->ResumeRefreshURIs();
5579 return NS_OK;
5582 nsresult nsDocShell::RefreshURIFromQueue() {
5583 if (!mRefreshURIList) {
5584 return NS_OK;
5586 uint32_t n = 0;
5587 mRefreshURIList->GetLength(&n);
5589 while (n) {
5590 nsCOMPtr<nsITimerCallback> refreshInfo =
5591 do_QueryElementAt(mRefreshURIList, --n);
5593 if (refreshInfo) {
5594 // This is the nsRefreshTimer object, waiting to be
5595 // setup in a timer object and fired.
5596 // Create the timer and trigger it.
5597 uint32_t delay = static_cast<nsRefreshTimer*>(
5598 static_cast<nsITimerCallback*>(refreshInfo))
5599 ->GetDelay();
5600 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5601 if (win) {
5602 nsCOMPtr<nsITimer> timer;
5603 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5604 nsITimer::TYPE_ONE_SHOT);
5606 if (timer) {
5607 // Replace the nsRefreshTimer element in the queue with
5608 // its corresponding timer object, so that in case another
5609 // load comes through before the timer can go off, the timer will
5610 // get cancelled in CancelRefreshURITimer()
5611 mRefreshURIList->ReplaceElementAt(timer, n);
5617 return NS_OK;
5620 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) {
5621 nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
5622 bool firstPart = false;
5623 return multiPartChannel &&
5624 NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) &&
5625 !firstPart;
5628 nsresult nsDocShell::Embed(nsIContentViewer* aContentViewer,
5629 WindowGlobalChild* aWindowActor,
5630 bool aIsTransientAboutBlank, bool aPersist,
5631 nsIRequest* aRequest, nsIURI* aPreviousURI) {
5632 // Save the LayoutHistoryState of the previous document, before
5633 // setting up new document
5634 PersistLayoutHistoryState();
5636 nsresult rv = SetupNewViewer(aContentViewer, aWindowActor);
5637 NS_ENSURE_SUCCESS(rv, rv);
5639 // XXX What if SetupNewViewer fails?
5640 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
5641 // Set history.state
5642 SetDocCurrentStateObj(mLSHE,
5643 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
5646 if (mLSHE) {
5647 // Restore the editing state, if it's stored in session history.
5648 if (mLSHE->HasDetachedEditor()) {
5649 ReattachEditorToWindow(mLSHE);
5652 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5655 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() &&
5656 !IsFollowupPartOfMultipart(aRequest)) {
5657 bool expired = false;
5658 uint32_t cacheKey = 0;
5659 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
5660 if (cacheChannel) {
5661 // Check if the page has expired from cache
5662 uint32_t expTime = 0;
5663 cacheChannel->GetCacheTokenExpirationTime(&expTime);
5664 uint32_t now = PRTimeToSeconds(PR_Now());
5665 if (expTime <= now) {
5666 expired = true;
5669 // The checks for updating cache key are similar to the old session
5670 // history in OnNewURI. Try to update the cache key if
5671 // - we should update session history and aren't doing a session
5672 // history load.
5673 // - we're doing a forced reload.
5674 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
5675 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
5676 IsForceReloadType(mLoadType)) {
5677 cacheChannel->GetCacheKey(&cacheKey);
5681 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
5682 MoveLoadingToActiveEntry(aPersist, expired, cacheKey, aPreviousURI);
5685 bool updateHistory = true;
5687 // Determine if this type of load should update history
5688 switch (mLoadType) {
5689 case LOAD_NORMAL_REPLACE:
5690 case LOAD_REFRESH_REPLACE:
5691 case LOAD_STOP_CONTENT_AND_REPLACE:
5692 case LOAD_RELOAD_BYPASS_CACHE:
5693 case LOAD_RELOAD_BYPASS_PROXY:
5694 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5695 case LOAD_REPLACE_BYPASS_CACHE:
5696 updateHistory = false;
5697 break;
5698 default:
5699 break;
5702 if (!updateHistory) {
5703 SetLayoutHistoryState(nullptr);
5706 return NS_OK;
5709 //*****************************************************************************
5710 // nsDocShell::nsIWebProgressListener
5711 //*****************************************************************************
5713 NS_IMETHODIMP
5714 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5715 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5716 int32_t aCurTotalProgress,
5717 int32_t aMaxTotalProgress) {
5718 return NS_OK;
5721 NS_IMETHODIMP
5722 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5723 uint32_t aStateFlags, nsresult aStatus) {
5724 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5725 // Save timing statistics.
5726 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5727 nsCOMPtr<nsIURI> uri;
5728 channel->GetURI(getter_AddRefs(uri));
5729 nsAutoCString aURI;
5730 uri->GetAsciiSpec(aURI);
5732 if (this == aProgress) {
5733 mozilla::Unused << MaybeInitTiming();
5734 mTiming->NotifyFetchStart(uri,
5735 ConvertLoadTypeToNavigationType(mLoadType));
5736 // If we are starting a DocumentChannel, we need to pass the timing
5737 // statistics so that should a process switch occur, the starting type can
5738 // be passed to the new DocShell running in the other content process.
5739 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5740 docChannel->SetNavigationTiming(mTiming);
5744 // Page has begun to load
5745 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5747 if ((aStateFlags & STATE_RESTORING) == 0) {
5748 // Show the progress cursor if the pref is set
5749 if (StaticPrefs::ui_use_activity_cursor()) {
5750 nsCOMPtr<nsIWidget> mainWidget;
5751 GetMainWidget(getter_AddRefs(mainWidget));
5752 if (mainWidget) {
5753 mainWidget->SetCursor(nsIWidget::Cursor{eCursor_spinning});
5757 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
5758 if (IsForceReloadType(mLoadType)) {
5759 if (WindowContext* windowContext =
5760 mBrowsingContext->GetCurrentWindowContext()) {
5761 SessionStoreChild::From(windowContext->GetWindowGlobalChild())
5762 ->ResetSessionStore(mBrowsingContext,
5763 mBrowsingContext->GetSessionStoreEpoch());
5768 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5769 // Page is loading
5770 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5771 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5772 // Page has finished loading
5773 mBusyFlags = BUSY_FLAGS_NONE;
5775 // Hide the progress cursor if the pref is set
5776 if (StaticPrefs::ui_use_activity_cursor()) {
5777 nsCOMPtr<nsIWidget> mainWidget;
5778 GetMainWidget(getter_AddRefs(mainWidget));
5779 if (mainWidget) {
5780 mainWidget->SetCursor(nsIWidget::Cursor{eCursor_standard});
5785 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5786 nsCOMPtr<nsIWebProgress> webProgress =
5787 do_QueryInterface(GetAsSupports(this));
5788 // Is the document stop notification for this document?
5789 if (aProgress == webProgress.get()) {
5790 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5791 EndPageLoad(aProgress, channel, aStatus);
5794 // note that redirect state changes will go through here as well, but it
5795 // is better to handle those in OnRedirectStateChange where more
5796 // information is available.
5797 return NS_OK;
5800 NS_IMETHODIMP
5801 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5802 nsIURI* aURI, uint32_t aFlags) {
5803 // Since we've now changed Documents, notify the BrowsingContext that we've
5804 // changed. Ideally we'd just let the BrowsingContext do this when it
5805 // changes the current window global, but that happens before this and we
5806 // have a lot of tests that depend on the specific ordering of messages.
5807 bool isTopLevel = false;
5808 if (XRE_IsParentProcess() &&
5809 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
5810 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
5811 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5813 return NS_OK;
5816 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5817 nsIChannel* aNewChannel,
5818 uint32_t aRedirectFlags,
5819 uint32_t aStateFlags) {
5820 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5821 "Calling OnRedirectStateChange when there is no redirect");
5823 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5824 return; // not a toplevel document
5827 nsCOMPtr<nsIURI> oldURI, newURI;
5828 aOldChannel->GetURI(getter_AddRefs(oldURI));
5829 aNewChannel->GetURI(getter_AddRefs(newURI));
5830 if (!oldURI || !newURI) {
5831 return;
5834 // DocumentChannel adds redirect chain to global history in the parent
5835 // process. The redirect chain can't be queried from the content process, so
5836 // there's no need to update global history here.
5837 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5838 if (!docChannel) {
5839 // Below a URI visit is saved (see AddURIVisit method doc).
5840 // The visit chain looks something like:
5841 // ...
5842 // Site N - 1
5843 // => Site N
5844 // (redirect to =>) Site N + 1 (we are here!)
5846 // Get N - 1 and transition type
5847 nsCOMPtr<nsIURI> previousURI;
5848 uint32_t previousFlags = 0;
5849 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5851 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5852 net::ChannelIsPost(aOldChannel)) {
5853 // 1. Internal redirects are ignored because they are specific to the
5854 // channel implementation.
5855 // 2. POSTs are not saved by global history.
5857 // Regardless, we need to propagate the previous visit to the new
5858 // channel.
5859 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5860 } else {
5861 // Get the HTTP response code, if available.
5862 uint32_t responseStatus = 0;
5863 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5864 if (httpChannel) {
5865 Unused << httpChannel->GetResponseStatus(&responseStatus);
5868 // Add visit N -1 => N
5869 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5871 // Since N + 1 could be the final destination, we will not save N => N + 1
5872 // here. OnNewURI will do that, so we will cache it.
5873 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5877 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5878 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5879 mLoadType = LOAD_NORMAL_REPLACE;
5880 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5884 NS_IMETHODIMP
5885 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5886 nsresult aStatus, const char16_t* aMessage) {
5887 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5888 return NS_OK;
5891 NS_IMETHODIMP
5892 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5893 uint32_t aState) {
5894 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5895 return NS_OK;
5898 NS_IMETHODIMP
5899 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5900 nsIRequest* aRequest, uint32_t aEvent) {
5901 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5902 return NS_OK;
5905 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5906 const nsACString& aKeyword, bool aIsPrivateContext) {
5907 nsCOMPtr<nsIURIFixupInfo> info;
5908 if (!XRE_IsContentProcess()) {
5909 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5910 if (uriFixup) {
5911 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
5914 return info.forget();
5917 /* static */
5918 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
5919 nsIChannel* aChannel, nsIURI* aUrl) {
5920 if (!aChannel) {
5921 return nullptr;
5924 nsresult rv = NS_OK;
5925 nsAutoCString host;
5926 rv = aUrl->GetAsciiHost(host);
5927 if (NS_WARN_IF(NS_FAILED(rv))) {
5928 return nullptr;
5931 // No point in going further if "www." is included in the hostname
5932 // already. That is the only hueristic we're applying in this function.
5933 if (StringBeginsWith(host, "www."_ns)) {
5934 return nullptr;
5937 // Return if fixup enable pref is turned off.
5938 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5939 return nullptr;
5942 // Return if scheme is not HTTPS.
5943 if (!SchemeIsHTTPS(aUrl)) {
5944 return nullptr;
5947 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
5948 if (!info) {
5949 return nullptr;
5952 // Skip doing the fixup if our channel was redirected, because we
5953 // shouldn't be guessing things about the post-redirect URI.
5954 if (!info->RedirectChain().IsEmpty()) {
5955 return nullptr;
5958 int32_t port = 0;
5959 rv = aUrl->GetPort(&port);
5960 if (NS_WARN_IF(NS_FAILED(rv))) {
5961 return nullptr;
5964 // Don't fix up hosts with ports.
5965 if (port != -1) {
5966 return nullptr;
5969 // Don't fix up localhost url.
5970 if (host == "localhost") {
5971 return nullptr;
5974 // Don't fix up hostnames with IP address.
5975 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
5976 return nullptr;
5979 nsAutoCString userPass;
5980 rv = aUrl->GetUserPass(userPass);
5981 if (NS_WARN_IF(NS_FAILED(rv))) {
5982 return nullptr;
5985 // Security - URLs with user / password info should NOT be modified.
5986 if (!userPass.IsEmpty()) {
5987 return nullptr;
5990 nsCOMPtr<nsITransportSecurityInfo> tsi;
5991 rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi));
5992 if (NS_WARN_IF(NS_FAILED(rv))) {
5993 return nullptr;
5996 if (NS_WARN_IF(!tsi)) {
5997 return nullptr;
6000 nsCOMPtr<nsIX509Cert> cert;
6001 rv = tsi->GetServerCert(getter_AddRefs(cert));
6002 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
6003 return nullptr;
6006 nsTArray<uint8_t> certBytes;
6007 rv = cert->GetRawDER(certBytes);
6008 if (NS_FAILED(rv)) {
6009 return nullptr;
6012 mozilla::pkix::Input serverCertInput;
6013 mozilla::pkix::Result rv1 =
6014 serverCertInput.Init(certBytes.Elements(), certBytes.Length());
6015 if (rv1 != mozilla::pkix::Success) {
6016 return nullptr;
6019 nsAutoCString newHost("www."_ns);
6020 newHost.Append(host);
6022 mozilla::pkix::Input newHostInput;
6023 rv1 = newHostInput.Init(
6024 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
6025 newHost.Length());
6026 if (rv1 != mozilla::pkix::Success) {
6027 return nullptr;
6030 // Check if adding a "www." prefix to the request's hostname will
6031 // cause the response's certificate to match.
6032 rv1 = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput);
6033 if (rv1 != mozilla::pkix::Success) {
6034 return nullptr;
6037 nsCOMPtr<nsIURI> newURI;
6038 Unused << NS_MutateURI(aUrl).SetHost(newHost).Finalize(
6039 getter_AddRefs(newURI));
6041 return newURI.forget();
6044 /* static */
6045 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
6046 nsIChannel* aChannel, nsresult aStatus,
6047 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
6048 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
6049 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData) {
6050 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
6051 aStatus != NS_ERROR_CONNECTION_REFUSED &&
6052 aStatus !=
6053 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6054 return nullptr;
6057 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
6058 return nullptr;
6061 nsCOMPtr<nsIURI> url;
6062 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6063 if (NS_FAILED(rv)) {
6064 return nullptr;
6068 // Try and make an alternative URI from the old one
6070 nsCOMPtr<nsIURI> newURI;
6071 nsCOMPtr<nsIInputStream> newPostData;
6073 nsAutoCString oldSpec;
6074 url->GetSpec(oldSpec);
6077 // First try keyword fixup
6079 nsAutoString keywordProviderName, keywordAsSent;
6080 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
6081 // we should only perform a keyword search under the following
6082 // conditions:
6083 // (0) Pref keyword.enabled is true
6084 // (1) the url scheme is http (or https)
6085 // (2) the url does not have a protocol scheme
6086 // If we don't enforce such a policy, then we end up doing
6087 // keyword searchs on urls we don't intend like imap, file,
6088 // mailbox, etc. This could lead to a security problem where we
6089 // send data to the keyword server that we shouldn't be.
6090 // Someone needs to clean up keywords in general so we can
6091 // determine on a per url basis if we want keywords
6092 // enabled...this is just a bandaid...
6093 nsAutoCString scheme;
6094 Unused << url->GetScheme(scheme);
6095 if (Preferences::GetBool("keyword.enabled", false) &&
6096 StringBeginsWith(scheme, "http"_ns)) {
6097 bool attemptFixup = false;
6098 nsAutoCString host;
6099 Unused << url->GetHost(host);
6100 if (host.FindChar('.') == kNotFound) {
6101 attemptFixup = true;
6102 } else {
6103 // For domains with dots, we check the public suffix validity.
6104 nsCOMPtr<nsIEffectiveTLDService> tldService =
6105 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
6106 if (tldService) {
6107 nsAutoCString suffix;
6108 attemptFixup =
6109 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
6110 suffix.IsEmpty();
6113 if (attemptFixup) {
6114 nsCOMPtr<nsIURIFixupInfo> info;
6115 // only send non-qualified hosts to the keyword server
6116 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
6117 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
6118 } else {
6120 // If this string was passed through nsStandardURL by
6121 // chance, then it may have been converted from UTF-8 to
6122 // ACE, which would result in a completely bogus keyword
6123 // query. Here we try to recover the original Unicode
6124 // value, but this is not 100% correct since the value may
6125 // have been normalized per the IDN normalization rules.
6127 // Since we don't have access to the exact original string
6128 // that was entered by the user, this will just have to do.
6129 bool isACE;
6130 nsAutoCString utf8Host;
6131 nsCOMPtr<nsIIDNService> idnSrv =
6132 do_GetService(NS_IDNSERVICE_CONTRACTID);
6133 if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6134 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6135 info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
6137 } else {
6138 info = KeywordToURI(host, aUsePrivateBrowsing);
6141 if (info) {
6142 info->GetPreferredURI(getter_AddRefs(newURI));
6143 if (newURI) {
6144 info->GetKeywordAsSent(keywordAsSent);
6145 info->GetKeywordProviderName(keywordProviderName);
6146 info->GetPostData(getter_AddRefs(newPostData));
6154 // Now try change the address, e.g. turn http://foo into
6155 // http://www.foo.com, and if that doesn't work try https with
6156 // https://foo and https://www.foo.com.
6158 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6159 // Skip fixup for anything except a normal document load
6160 // operation on the topframe.
6161 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
6163 if (doCreateAlternate) {
6164 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6165 nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
6166 // Only do this if our channel was loaded directly by the user from the
6167 // URL bar or similar (system principal) and not redirected, because we
6168 // shouldn't be guessing things about links from other sites, or a
6169 // post-redirect URI.
6170 doCreateAlternate = principal && principal->IsSystemPrincipal() &&
6171 loadInfo->RedirectChain().IsEmpty();
6173 // Test if keyword lookup produced a new URI or not
6174 if (doCreateAlternate && newURI) {
6175 bool sameURI = false;
6176 url->Equals(newURI, &sameURI);
6177 if (!sameURI) {
6178 // Keyword lookup made a new URI so no need to try
6179 // an alternate one.
6180 doCreateAlternate = false;
6183 if (doCreateAlternate) {
6184 newURI = nullptr;
6185 newPostData = nullptr;
6186 keywordProviderName.Truncate();
6187 keywordAsSent.Truncate();
6188 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6189 if (uriFixup) {
6190 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
6191 uriFixup->GetFixupURIInfo(oldSpec,
6192 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
6193 getter_AddRefs(fixupInfo));
6194 if (fixupInfo) {
6195 fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
6199 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6200 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6201 // Try HTTPS, since http didn't work
6202 if (SchemeIsHTTP(url)) {
6203 int32_t port = 0;
6204 url->GetPort(&port);
6206 // Fall back to HTTPS only if port is default
6207 if (port == -1) {
6208 newURI = nullptr;
6209 newPostData = nullptr;
6210 Unused << NS_MutateURI(url)
6211 .SetScheme("https"_ns)
6212 .Finalize(getter_AddRefs(newURI));
6217 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try prefixing the domain name
6218 // with www. to see if we can avoid showing the cert error page. For example,
6219 // https://example.com -> https://www.example.com.
6220 if (aStatus ==
6221 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6222 newPostData = nullptr;
6223 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
6226 // Did we make a new URI that is different to the old one? If so
6227 // load it.
6229 if (newURI) {
6230 // Make sure the new URI is different from the old one,
6231 // otherwise there's little point trying to load it again.
6232 bool sameURI = false;
6233 url->Equals(newURI, &sameURI);
6234 if (!sameURI) {
6235 if (aNewPostData) {
6236 newPostData.forget(aNewPostData);
6238 if (aNotifyKeywordSearchLoading) {
6239 // This notification is meant for Firefox Health Report so it
6240 // can increment counts from the search engine
6241 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6243 return newURI.forget();
6247 return nullptr;
6250 nsresult nsDocShell::FilterStatusForErrorPage(
6251 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
6252 bool aIsTopFrame, bool aUseErrorPages, bool aIsInitialDocument,
6253 bool* aSkippedUnknownProtocolNavigation) {
6254 // Errors to be shown only on top-level frames
6255 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6256 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6257 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6258 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6259 aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6260 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6261 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6262 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6263 aStatus == NS_ERROR_MALFORMED_URI ||
6264 aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
6265 aStatus == NS_ERROR_DOM_COOP_FAILED ||
6266 aStatus == NS_ERROR_DOM_COEP_FAILED) &&
6267 (aIsTopFrame || aUseErrorPages)) {
6268 return aStatus;
6271 if (aStatus == NS_ERROR_NET_TIMEOUT ||
6272 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
6273 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6274 aStatus == NS_ERROR_REDIRECT_LOOP ||
6275 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6276 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
6277 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
6278 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
6279 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
6280 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6281 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6282 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6283 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6284 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6285 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
6286 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
6287 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
6288 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
6289 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6290 // Errors to be shown for any frame
6291 return aStatus;
6294 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6295 // For unknown protocols we only display an error if the load is triggered
6296 // by the browser itself, or we're replacing the initial document (and
6297 // nothing else). Showing the error for page-triggered navigations causes
6298 // annoying behavior for users, see bug 1528305.
6300 // We could, maybe, try to detect if this is in response to some user
6301 // interaction (like clicking a link, or something else) and maybe show
6302 // the error page in that case. But this allows for ctrl+clicking and such
6303 // to see the error page.
6304 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6305 if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6306 StaticPrefs::dom_no_unknown_protocol_error_enabled() &&
6307 !aIsInitialDocument) {
6308 if (aSkippedUnknownProtocolNavigation) {
6309 *aSkippedUnknownProtocolNavigation = true;
6311 return NS_OK;
6313 return aStatus;
6316 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6317 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6318 // Caching channels would have to look at their flags to work
6319 // out which error to return. Or we can fix up the error here.
6320 if (!(aLoadType & LOAD_CMD_HISTORY)) {
6321 return NS_ERROR_OFFLINE;
6323 return aStatus;
6326 return NS_OK;
6329 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6330 nsIChannel* aChannel, nsresult aStatus) {
6331 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
6332 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
6333 static_cast<uint32_t>(aStatus)));
6334 if (!aChannel) {
6335 return NS_ERROR_NULL_POINTER;
6338 // Make sure to discard the initial client if we never created the initial
6339 // about:blank document. Do this before possibly returning from the method
6340 // due to an error.
6341 mInitialClientSource.reset();
6343 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6344 if (reporter) {
6345 nsCOMPtr<nsILoadGroup> loadGroup;
6346 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6347 if (loadGroup) {
6348 reporter->FlushConsoleReports(loadGroup);
6349 } else {
6350 reporter->FlushConsoleReports(GetDocument());
6354 nsCOMPtr<nsIURI> url;
6355 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6356 if (NS_FAILED(rv)) {
6357 return rv;
6360 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6361 if (timingChannel) {
6362 TimeStamp channelCreationTime;
6363 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6364 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6365 Telemetry::AccumulateTimeDelta(Telemetry::TOTAL_CONTENT_PAGE_LOAD_TIME,
6366 channelCreationTime);
6370 // Timing is picked up by the window, we don't need it anymore
6371 mTiming = nullptr;
6373 // clean up reload state for meta charset
6374 if (eCharsetReloadRequested == mCharsetReloadState) {
6375 mCharsetReloadState = eCharsetReloadStopOrigional;
6376 } else {
6377 mCharsetReloadState = eCharsetReloadInit;
6380 // Save a pointer to the currently-loading history entry.
6381 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6382 // entry further down in this method.
6383 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6384 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
6387 // one of many safeguards that prevent death and destruction if
6388 // someone is so very very rude as to bring this window down
6389 // during this load handler.
6391 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6393 // Notify the ContentViewer that the Document has finished loading. This
6394 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6395 if (!mEODForCurrentDocument && mContentViewer) {
6396 mIsExecutingOnLoadHandler = true;
6397 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
6398 contentViewer->LoadComplete(aStatus);
6399 mIsExecutingOnLoadHandler = false;
6401 mEODForCurrentDocument = true;
6403 /* Check if the httpChannel has any cache-control related response headers,
6404 * like no-store, no-cache. If so, update SHEntry so that
6405 * when a user goes back/forward to this page, we appropriately do
6406 * form value restoration or load from server.
6408 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6409 if (!httpChannel) {
6410 // HttpChannel could be hiding underneath a Multipart channel.
6411 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6414 if (httpChannel) {
6415 // figure out if SH should be saving layout state.
6416 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6417 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6418 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
6419 mLSHE->SetSaveLayoutStateFlag(false);
6423 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6424 // onLoadHandler tries to load something different in
6425 // itself or one of its children, we can deal with it appropriately.
6426 if (mLSHE) {
6427 mLSHE->SetLoadType(LOAD_HISTORY);
6429 // Clear the mLSHE reference to indicate document loading is done one
6430 // way or another.
6431 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6433 mActiveEntryIsLoadingFromSessionHistory = false;
6435 // if there's a refresh header in the channel, this method
6436 // will set it up for us.
6437 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
6438 RefreshURIFromQueue();
6440 // Test whether this is the top frame or a subframe
6441 bool isTopFrame = mBrowsingContext->IsTop();
6443 bool hadErrorStatus = false;
6444 // If status code indicates an error it means that DocumentChannel already
6445 // tried to fixup the uri and failed. Throw an error dialog box here.
6446 if (NS_FAILED(aStatus)) {
6447 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6448 // the error event to our embedder, since tests are relying on this.
6449 // The error event is usually fired by the caller of InternalLoad, but
6450 // this particular error can happen asynchronously.
6451 // Bug 1629201 is filed for having much clearer decision making around
6452 // which cases need error events.
6453 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6454 aStatus == NS_ERROR_CONTENT_BLOCKED);
6455 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6457 bool isInitialDocument =
6458 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6459 bool skippedUnknownProtocolNavigation = false;
6460 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
6461 mBrowsingContext->GetUseErrorPages(),
6462 isInitialDocument,
6463 &skippedUnknownProtocolNavigation);
6464 hadErrorStatus = true;
6465 if (NS_FAILED(aStatus)) {
6466 if (!mIsBeingDestroyed) {
6467 DisplayLoadError(aStatus, url, nullptr, aChannel);
6469 } else if (skippedUnknownProtocolNavigation) {
6470 nsTArray<nsString> params;
6471 if (NS_FAILED(
6472 NS_GetSanitizedURIStringFromURI(url, *params.AppendElement()))) {
6473 params.LastElement().AssignLiteral(u"(unknown uri)");
6475 nsContentUtils::ReportToConsole(
6476 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
6477 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
6478 params);
6480 } else {
6481 // If we have a host
6482 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6483 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6486 if (hadErrorStatus) {
6487 // Don't send session store updates if the reason EndPageLoad was called is
6488 // because we are process switching. Sometimes the update takes too long and
6489 // incorrectly overrides session store data from the following load.
6490 return NS_OK;
6492 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
6493 if (WindowContext* windowContext =
6494 mBrowsingContext->GetCurrentWindowContext()) {
6495 using Change = SessionStoreChangeListener::Change;
6497 // We've finished loading the page and now we want to collect all the
6498 // session store state that the page is initialized with.
6499 SessionStoreChangeListener::CollectSessionStoreData(
6500 windowContext,
6501 EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory,
6502 Change::WireFrame));
6506 return NS_OK;
6509 //*****************************************************************************
6510 // nsDocShell: Content Viewer Management
6511 //*****************************************************************************
6513 nsresult nsDocShell::EnsureContentViewer() {
6514 if (mContentViewer) {
6515 return NS_OK;
6517 if (mIsBeingDestroyed) {
6518 return NS_ERROR_FAILURE;
6521 nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6522 nsCOMPtr<nsIURI> baseURI;
6523 nsIPrincipal* principal = GetInheritedPrincipal(false);
6524 nsIPrincipal* partitionedPrincipal = GetInheritedPrincipal(false, true);
6526 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6527 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6528 if (parentItem) {
6529 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6530 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6531 if (parentElement) {
6532 baseURI = parentElement->GetBaseURI();
6533 cspToInheritForAboutBlank = parentElement->GetCsp();
6538 nsresult rv = CreateAboutBlankContentViewer(
6539 principal, partitionedPrincipal, cspToInheritForAboutBlank, baseURI,
6540 /* aIsInitialDocument */ true);
6542 NS_ENSURE_STATE(mContentViewer);
6544 if (NS_SUCCEEDED(rv)) {
6545 RefPtr<Document> doc(GetDocument());
6546 MOZ_ASSERT(doc,
6547 "Should have doc if CreateAboutBlankContentViewer "
6548 "succeeded!");
6549 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
6551 // Documents created using EnsureContentViewer may be transient
6552 // placeholders created by framescripts before content has a
6553 // chance to load. In some cases, window.open(..., "noopener")
6554 // will create such a document and then synchronously tear it
6555 // down, firing a "pagehide" event. Doing so violates our
6556 // assertions about DocGroups. It's easier to silence the
6557 // assertion here than to avoid creating the extra document.
6558 doc->IgnoreDocGroupMismatches();
6561 return rv;
6564 nsresult nsDocShell::CreateAboutBlankContentViewer(
6565 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
6566 nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI, bool aIsInitialDocument,
6567 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
6568 bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6569 WindowGlobalChild* aActor) {
6570 RefPtr<Document> blankDoc;
6571 nsCOMPtr<nsIContentViewer> viewer;
6572 nsresult rv = NS_ERROR_FAILURE;
6574 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6576 /* mCreatingDocument should never be true at this point. However, it's
6577 a theoretical possibility. We want to know about it and make it stop,
6578 and this sounds like a job for an assertion. */
6579 NS_ASSERTION(!mCreatingDocument,
6580 "infinite(?) loop creating document averted");
6581 if (mCreatingDocument) {
6582 return NS_ERROR_FAILURE;
6585 if (!mBrowsingContext->AncestorsAreCurrent() ||
6586 mBrowsingContext->IsInBFCache()) {
6587 mBrowsingContext->RemoveRootFromBFCacheSync();
6588 return NS_ERROR_NOT_AVAILABLE;
6591 // mContentViewer->PermitUnload may release |this| docshell.
6592 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6594 AutoRestore<bool> creatingDocument(mCreatingDocument);
6595 mCreatingDocument = true;
6597 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6598 mItemType != typeChrome) {
6599 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6600 mBrowsingContext->OriginAttributesRef());
6603 // Make sure timing is created. But first record whether we had it
6604 // already, so we don't clobber the timing for an in-progress load.
6605 bool hadTiming = mTiming;
6606 bool toBeReset = MaybeInitTiming();
6607 if (mContentViewer) {
6608 if (aCheckPermitUnload) {
6609 // We've got a content viewer already. Make sure the user
6610 // permits us to discard the current document and replace it
6611 // with about:blank. And also ensure we fire the unload events
6612 // in the current document.
6614 // Unload gets fired first for
6615 // document loaded from the session history.
6616 mTiming->NotifyBeforeUnload();
6618 bool okToUnload;
6619 rv = mContentViewer->PermitUnload(&okToUnload);
6621 if (NS_SUCCEEDED(rv) && !okToUnload) {
6622 // The user chose not to unload the page, interrupt the load.
6623 MaybeResetInitTiming(toBeReset);
6624 return NS_ERROR_FAILURE;
6626 if (mTiming) {
6627 mTiming->NotifyUnloadAccepted(mCurrentURI);
6631 mSavingOldViewer =
6632 aTryToSaveOldPresentation &&
6633 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
6634 /* aReportBFCacheComboTelemetry */ true);
6636 // Make sure to blow away our mLoadingURI just in case. No loads
6637 // from inside this pagehide.
6638 mLoadingURI = nullptr;
6640 // Stop any in-progress loading, so that we don't accidentally trigger any
6641 // PageShow notifications from Embed() interrupting our loading below.
6642 Stop();
6644 // Notify the current document that it is about to be unloaded!!
6646 // It is important to fire the unload() notification *before* any state
6647 // is changed within the DocShell - otherwise, javascript will get the
6648 // wrong information :-(
6650 (void)FirePageHideNotification(!mSavingOldViewer);
6651 // pagehide notification might destroy this docshell.
6652 if (mIsBeingDestroyed) {
6653 return NS_ERROR_DOCSHELL_DYING;
6657 // Now make sure we don't think we're in the middle of firing unload after
6658 // this point. This will make us fire unload when the about:blank document
6659 // unloads... but that's ok, more or less. Would be nice if it fired load
6660 // too, of course.
6661 mFiredUnloadEvent = false;
6663 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6664 nsContentUtils::FindInternalContentViewer("text/html"_ns);
6666 if (docFactory) {
6667 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6668 const uint32_t sandboxFlags =
6669 mBrowsingContext->GetHasLoadedNonInitialDocument()
6670 ? mBrowsingContext->GetSandboxFlags()
6671 : mBrowsingContext->GetInitialSandboxFlags();
6672 // If we're sandboxed, then create a new null principal. We skip
6673 // this if we're being created from WindowGlobalChild, since in
6674 // that case we already have a null principal if required.
6675 // We can't compare againt the BrowsingContext sandbox flag, since
6676 // the value was taken when the load initiated and may have since
6677 // changed.
6678 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6679 if (aPrincipal) {
6680 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6681 } else {
6682 principal = NullPrincipal::Create(GetOriginAttributes());
6684 partitionedPrincipal = principal;
6685 } else {
6686 principal = aPrincipal;
6687 partitionedPrincipal = aPartitionedPrincipal;
6690 // We cannot get the foreign partitioned prinicpal for the initial
6691 // about:blank page. So, we change to check if we need to use the
6692 // partitioned principal for the service worker here.
6693 MaybeCreateInitialClientSource(
6694 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6695 this)
6696 ? partitionedPrincipal
6697 : principal);
6699 // generate (about:blank) document to load
6700 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6701 partitionedPrincipal, this);
6702 if (blankDoc) {
6703 // Hack: manually set the CSP for the new document
6704 // Please create an actual copy of the CSP (do not share the same
6705 // reference) otherwise appending a new policy within the new
6706 // document will be incorrectly propagated to the opening doc.
6707 if (aCSP) {
6708 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6709 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6710 blankDoc->SetCsp(cspToInherit);
6713 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6715 blankDoc->SetEmbedderPolicy(aCOEP);
6717 // Hack: set the base URI manually, since this document never
6718 // got Reset() with a channel.
6719 blankDoc->SetBaseURI(aBaseURI);
6721 // Copy our sandbox flags to the document. These are immutable
6722 // after being set here.
6723 blankDoc->SetSandboxFlags(sandboxFlags);
6725 blankDoc->InitFeaturePolicy();
6727 // create a content viewer for us and the new document
6728 docFactory->CreateInstanceForDocument(
6729 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6730 getter_AddRefs(viewer));
6732 // hook 'em up
6733 if (viewer) {
6734 viewer->SetContainer(this);
6735 rv = Embed(viewer, aActor, true, false, nullptr, mCurrentURI);
6736 NS_ENSURE_SUCCESS(rv, rv);
6738 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6739 /* aFireLocationChange */ true,
6740 /* aIsInitialAboutBlank */ true,
6741 /* aLocationFlags */ 0);
6742 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6747 // The transient about:blank viewer doesn't have a session history entry.
6748 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6750 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6751 // have one before entering this function.
6752 if (!hadTiming) {
6753 mTiming = nullptr;
6754 mBlankTiming = true;
6757 return rv;
6760 NS_IMETHODIMP
6761 nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal,
6762 nsIPrincipal* aPartitionedPrincipal,
6763 nsIContentSecurityPolicy* aCSP) {
6764 return CreateAboutBlankContentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6765 nullptr, /* aIsInitialDocument */ false);
6768 nsresult nsDocShell::CreateContentViewerForActor(
6769 WindowGlobalChild* aWindowActor) {
6770 MOZ_ASSERT(aWindowActor);
6772 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6773 // FIXME: We may want to support non-initial documents here.
6774 nsresult rv = CreateAboutBlankContentViewer(
6775 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6776 /* aCsp */ nullptr,
6777 /* aBaseURI */ nullptr,
6778 /* aIsInitialDocument */ true,
6779 /* aCOEP */ Nothing(),
6780 /* aTryToSaveOldPresentation */ true,
6781 /* aCheckPermitUnload */ true, aWindowActor);
6782 #ifdef DEBUG
6783 if (NS_SUCCEEDED(rv)) {
6784 RefPtr<Document> doc(GetDocument());
6785 MOZ_ASSERT(
6786 doc,
6787 "Should have a document if CreateAboutBlankContentViewer succeeded");
6788 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6789 "New document should be in the same global as our actor");
6790 MOZ_ASSERT(doc->IsInitialDocument(),
6791 "New document should be an initial document");
6793 #endif
6795 return rv;
6798 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6799 nsIRequest* aNewRequest,
6800 Document* aNewDocument,
6801 bool aReportBFCacheComboTelemetry) {
6802 if (!mOSHE) {
6803 return false; // no entry to save into
6806 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6807 "mOSHE cannot be non-null with SHIP");
6808 nsCOMPtr<nsIContentViewer> viewer = mOSHE->GetContentViewer();
6809 if (viewer) {
6810 NS_WARNING("mOSHE already has a content viewer!");
6811 return false;
6814 // Only save presentation for "normal" loads and link loads. Anything else
6815 // probably wants to refetch the page, so caching the old presentation
6816 // would be incorrect.
6817 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6818 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6819 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6820 aLoadType != LOAD_ERROR_PAGE) {
6821 return false;
6824 // If the session history entry has the saveLayoutState flag set to false,
6825 // then we should not cache the presentation.
6826 if (!mOSHE->GetSaveLayoutStateFlag()) {
6827 return false;
6830 // If the document is not done loading, don't cache it.
6831 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6832 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6833 ("Blocked due to document still loading"));
6834 return false;
6837 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6838 return false;
6841 // Avoid doing the work of saving the presentation state in the case where
6842 // the content viewer cache is disabled.
6843 if (nsSHistory::GetMaxTotalViewers() == 0) {
6844 return false;
6847 // Don't cache the content viewer if we're in a subframe.
6848 if (mBrowsingContext->GetParent()) {
6849 return false; // this is a subframe load
6852 // If the document does not want its presentation cached, then don't.
6853 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6855 uint32_t bfCacheCombo = 0;
6856 bool canSavePresentation =
6857 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6858 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6859 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6860 auto* browsingContextGroup = mBrowsingContext->Group();
6861 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6862 browsingContextGroup->Toplevels();
6864 for (const auto& browsingContext : topLevelContext) {
6865 if (browsingContext != mBrowsingContext) {
6866 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6867 canSavePresentation = false;
6869 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6870 break;
6875 if (aReportBFCacheComboTelemetry) {
6876 ReportBFCacheComboTelemetry(bfCacheCombo);
6878 return doc && canSavePresentation;
6881 /* static */
6882 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6883 // There are 11 possible reasons to make a request fails to use BFCache
6884 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6885 // the common combinations for reasons which make requests fail to use
6886 // BFCache. These combinations are generated based on some local browsings,
6887 // we need to adjust them when necessary.
6888 enum BFCacheStatusCombo : uint32_t {
6889 BFCACHE_SUCCESS,
6890 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6891 // If both unload and beforeunload listeners are presented, it'll be
6892 // recorded as unload
6893 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6894 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6895 mozilla::dom::BFCacheStatus::REQUEST,
6896 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6897 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6898 mozilla::dom::BFCacheStatus::REQUEST |
6899 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6900 UNLOAD_REQUEST_PEER_MSE =
6901 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6902 mozilla::dom::BFCacheStatus::REQUEST |
6903 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6904 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6905 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6906 mozilla::dom::BFCacheStatus::REQUEST |
6907 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6908 SUSPENDED_UNLOAD_REQUEST_PEER =
6909 mozilla::dom::BFCacheStatus::SUSPENDED |
6910 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6911 mozilla::dom::BFCacheStatus::REQUEST |
6912 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6913 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6914 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6917 // Beforeunload is recorded as a blocker only if it is the only one to block
6918 // bfcache.
6919 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6920 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6922 switch (aCombo) {
6923 case BFCACHE_SUCCESS:
6924 Telemetry::AccumulateCategorical(
6925 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6926 break;
6927 case NOT_ONLY_TOPLEVEL:
6928 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6929 Telemetry::AccumulateCategorical(
6930 Telemetry::LABELS_BFCACHE_COMBO::Other);
6931 break;
6933 Telemetry::AccumulateCategorical(
6934 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6935 Telemetry::AccumulateCategorical(
6936 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6937 break;
6938 case UNLOAD:
6939 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6940 break;
6941 case BEFOREUNLOAD:
6942 Telemetry::AccumulateCategorical(
6943 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6944 break;
6945 case UNLOAD_REQUEST:
6946 Telemetry::AccumulateCategorical(
6947 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6948 break;
6949 case REQUEST:
6950 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6951 break;
6952 case UNLOAD_REQUEST_PEER:
6953 Telemetry::AccumulateCategorical(
6954 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6955 break;
6956 case UNLOAD_REQUEST_PEER_MSE:
6957 Telemetry::AccumulateCategorical(
6958 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6959 break;
6960 case UNLOAD_REQUEST_MSE:
6961 Telemetry::AccumulateCategorical(
6962 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6963 break;
6964 case SUSPENDED_UNLOAD_REQUEST_PEER:
6965 Telemetry::AccumulateCategorical(
6966 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6967 break;
6968 case REMOTE_SUBFRAMES:
6969 Telemetry::AccumulateCategorical(
6970 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6971 break;
6972 default:
6973 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6974 break;
6978 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6979 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6980 MOZ_ASSERT(!mIsBeingDestroyed);
6982 NS_ASSERTION(!mEditorData,
6983 "Why reattach an editor when we already have one?");
6984 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6985 "Reattaching when there's not a detached editor.");
6987 if (mEditorData || !aSHEntry) {
6988 return;
6991 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6992 if (mEditorData) {
6993 #ifdef DEBUG
6994 nsresult rv =
6995 #endif
6996 mEditorData->ReattachToWindow(this);
6997 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
7001 void nsDocShell::DetachEditorFromWindow() {
7002 if (!mEditorData || mEditorData->WaitingForLoad()) {
7003 // If there's nothing to detach, or if the editor data is actually set
7004 // up for the _new_ page that's coming in, don't detach.
7005 return;
7008 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
7009 "Detaching editor when it's already detached.");
7011 nsresult res = mEditorData->DetachFromWindow();
7012 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
7014 if (NS_SUCCEEDED(res)) {
7015 // Make mOSHE hold the owning ref to the editor data.
7016 if (mOSHE) {
7017 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
7018 "We should not set the editor data again once after we "
7019 "detached the editor data during destroying this docshell");
7020 mOSHE->SetEditorData(mEditorData.release());
7021 } else {
7022 mEditorData = nullptr;
7026 #ifdef DEBUG
7028 bool isEditable;
7029 GetEditable(&isEditable);
7030 NS_ASSERTION(!isEditable,
7031 "Window is still editable after detaching editor.");
7033 #endif // DEBUG
7036 nsresult nsDocShell::CaptureState() {
7037 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7039 if (!mOSHE || mOSHE == mLSHE) {
7040 // No entry to save into, or we're replacing the existing entry.
7041 return NS_ERROR_FAILURE;
7044 if (!mScriptGlobal) {
7045 return NS_ERROR_FAILURE;
7048 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
7049 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
7051 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7052 nsAutoCString spec;
7053 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
7054 if (uri) {
7055 uri->GetSpec(spec);
7057 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7058 ("Saving presentation into session history, URI: %s", spec.get()));
7061 mOSHE->SetWindowState(windowState);
7063 // Suspend refresh URIs and save off the timer queue
7064 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
7066 // Capture the current content viewer bounds.
7067 if (mContentViewer) {
7068 nsIntRect bounds;
7069 mContentViewer->GetBounds(bounds);
7070 mOSHE->SetViewerBounds(bounds);
7073 // Capture the docshell hierarchy.
7074 mOSHE->ClearChildShells();
7076 uint32_t childCount = mChildList.Length();
7077 for (uint32_t i = 0; i < childCount; ++i) {
7078 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
7079 NS_ASSERTION(childShell, "null child shell");
7081 mOSHE->AddChildShell(childShell);
7084 return NS_OK;
7087 NS_IMETHODIMP
7088 nsDocShell::RestorePresentationEvent::Run() {
7089 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7091 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
7092 NS_WARNING("RestoreFromHistory failed");
7094 return NS_OK;
7097 NS_IMETHODIMP
7098 nsDocShell::BeginRestore(nsIContentViewer* aContentViewer, bool aTop) {
7099 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7101 nsresult rv;
7102 if (!aContentViewer) {
7103 rv = EnsureContentViewer();
7104 NS_ENSURE_SUCCESS(rv, rv);
7106 aContentViewer = mContentViewer;
7109 // Dispatch events for restoring the presentation. We try to simulate
7110 // the progress notifications loading the document would cause, so we add
7111 // the document's channel to the loadgroup to initiate stateChange
7112 // notifications.
7114 RefPtr<Document> doc = aContentViewer->GetDocument();
7115 if (doc) {
7116 nsIChannel* channel = doc->GetChannel();
7117 if (channel) {
7118 mEODForCurrentDocument = false;
7119 mIsRestoringDocument = true;
7120 mLoadGroup->AddRequest(channel, nullptr);
7121 mIsRestoringDocument = false;
7125 if (!aTop) {
7126 // This point corresponds to us having gotten OnStartRequest or
7127 // STATE_START, so do the same thing that CreateContentViewer does at
7128 // this point to ensure that unload/pagehide events for this document
7129 // will fire when it's unloaded again.
7130 mFiredUnloadEvent = false;
7132 // For non-top frames, there is no notion of making sure that the
7133 // previous document is in the domwindow when STATE_START notifications
7134 // happen. We can just call BeginRestore for all of the child shells
7135 // now.
7136 rv = BeginRestoreChildren();
7137 NS_ENSURE_SUCCESS(rv, rv);
7140 return NS_OK;
7143 nsresult nsDocShell::BeginRestoreChildren() {
7144 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7146 for (auto* childDocLoader : mChildList.ForwardRange()) {
7147 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7148 if (child) {
7149 nsresult rv = child->BeginRestore(nullptr, false);
7150 NS_ENSURE_SUCCESS(rv, rv);
7153 return NS_OK;
7156 NS_IMETHODIMP
7157 nsDocShell::FinishRestore() {
7158 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7160 // First we call finishRestore() on our children. In the simulated load,
7161 // all of the child frames finish loading before the main document.
7163 for (auto* childDocLoader : mChildList.ForwardRange()) {
7164 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7165 if (child) {
7166 child->FinishRestore();
7170 if (mOSHE && mOSHE->HasDetachedEditor()) {
7171 ReattachEditorToWindow(mOSHE);
7174 RefPtr<Document> doc = GetDocument();
7175 if (doc) {
7176 // Finally, we remove the request from the loadgroup. This will
7177 // cause onStateChange(STATE_STOP) to fire, which will fire the
7178 // pageshow event to the chrome.
7180 nsIChannel* channel = doc->GetChannel();
7181 if (channel) {
7182 mIsRestoringDocument = true;
7183 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7184 mIsRestoringDocument = false;
7188 return NS_OK;
7191 NS_IMETHODIMP
7192 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7193 *aRestoring = mIsRestoringDocument;
7194 return NS_OK;
7197 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7198 bool* aRestoring) {
7199 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7200 MOZ_ASSERT(!mIsBeingDestroyed);
7202 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7203 "RestorePresentation should only be called for history loads");
7205 nsCOMPtr<nsIContentViewer> viewer = aSHEntry->GetContentViewer();
7207 nsAutoCString spec;
7208 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7209 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7210 if (uri) {
7211 uri->GetSpec(spec);
7215 *aRestoring = false;
7217 if (!viewer) {
7218 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7219 ("no saved presentation for uri: %s", spec.get()));
7220 return NS_OK;
7223 // We need to make sure the content viewer's container is this docshell.
7224 // In subframe navigation, it's possible for the docshell that the
7225 // content viewer was originally loaded into to be replaced with a
7226 // different one. We don't currently support restoring the presentation
7227 // in that case.
7229 nsCOMPtr<nsIDocShell> container;
7230 viewer->GetContainer(getter_AddRefs(container));
7231 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7232 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7233 ("No valid container, clearing presentation"));
7234 aSHEntry->SetContentViewer(nullptr);
7235 return NS_ERROR_FAILURE;
7238 NS_ASSERTION(mContentViewer != viewer, "Restoring existing presentation");
7240 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7241 ("restoring presentation from session history: %s", spec.get()));
7243 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7245 // Post an event that will remove the request after we've returned
7246 // to the event loop. This mimics the way it is called by nsIChannel
7247 // implementations.
7249 // Revoke any pending restore (just in case).
7250 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7251 "should only have one RestorePresentationEvent");
7252 mRestorePresentationEvent.Revoke();
7254 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7255 nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(evt));
7256 if (NS_SUCCEEDED(rv)) {
7257 mRestorePresentationEvent = evt.get();
7258 // The rest of the restore processing will happen on our event
7259 // callback.
7260 *aRestoring = true;
7263 return rv;
7266 namespace {
7267 class MOZ_STACK_CLASS PresentationEventForgetter {
7268 public:
7269 explicit PresentationEventForgetter(
7270 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7271 aRestorePresentationEvent)
7272 : mRestorePresentationEvent(aRestorePresentationEvent),
7273 mEvent(aRestorePresentationEvent.get()) {}
7275 ~PresentationEventForgetter() { Forget(); }
7277 void Forget() {
7278 if (mRestorePresentationEvent.get() == mEvent) {
7279 mRestorePresentationEvent.Forget();
7280 mEvent = nullptr;
7284 private:
7285 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7286 mRestorePresentationEvent;
7287 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7290 } // namespace
7292 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7293 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7296 nsresult nsDocShell::RestoreFromHistory() {
7297 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7298 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7299 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7301 // This section of code follows the same ordering as CreateContentViewer.
7302 if (!mLSHE) {
7303 return NS_ERROR_FAILURE;
7306 nsCOMPtr<nsIContentViewer> viewer = mLSHE->GetContentViewer();
7307 if (!viewer) {
7308 return NS_ERROR_FAILURE;
7311 if (mSavingOldViewer) {
7312 // We determined that it was safe to cache the document presentation
7313 // at the time we initiated the new load. We need to check whether
7314 // it's still safe to do so, since there may have been DOM mutations
7315 // or new requests initiated.
7316 RefPtr<Document> doc = viewer->GetDocument();
7317 nsIRequest* request = nullptr;
7318 if (doc) {
7319 request = doc->GetChannel();
7321 mSavingOldViewer = CanSavePresentation(
7322 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
7325 // Protect against mLSHE going away via a load triggered from
7326 // pagehide or unload.
7327 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7329 // Make sure to blow away our mLoadingURI just in case. No loads
7330 // from inside this pagehide.
7331 mLoadingURI = nullptr;
7333 // Notify the old content viewer that it's being hidden.
7334 FirePageHideNotification(!mSavingOldViewer);
7335 // pagehide notification might destroy this docshell.
7336 if (mIsBeingDestroyed) {
7337 return NS_ERROR_DOCSHELL_DYING;
7340 // If mLSHE was changed as a result of the pagehide event, then
7341 // something else was loaded. Don't finish restoring.
7342 if (mLSHE != origLSHE) {
7343 return NS_OK;
7346 // Add the request to our load group. We do this before swapping out
7347 // the content viewers so that consumers of STATE_START can access
7348 // the old document. We only deal with the toplevel load at this time --
7349 // to be consistent with normal document loading, subframes cannot start
7350 // loading until after data arrives, which is after STATE_START completes.
7352 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7353 mRestorePresentationEvent.get();
7354 Stop();
7355 // Make sure we're still restoring the same presentation.
7356 // If we aren't, docshell is in process doing another load already.
7357 NS_ENSURE_STATE(currentPresentationRestoration ==
7358 mRestorePresentationEvent.get());
7359 BeginRestore(viewer, true);
7360 NS_ENSURE_STATE(currentPresentationRestoration ==
7361 mRestorePresentationEvent.get());
7362 forgetter.Forget();
7364 // Set mFiredUnloadEvent = false so that the unload handler for the
7365 // *new* document will fire.
7366 mFiredUnloadEvent = false;
7368 mURIResultedInDocument = true;
7369 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7370 if (rootSH) {
7371 mPreviousEntryIndex = rootSH->Index();
7372 rootSH->LegacySHistory()->UpdateIndex();
7373 mLoadedEntryIndex = rootSH->Index();
7374 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7375 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7376 mLoadedEntryIndex));
7379 // Rather than call Embed(), we will retrieve the viewer from the session
7380 // history entry and swap it in.
7381 // XXX can we refactor this so that we can just call Embed()?
7382 PersistLayoutHistoryState();
7383 nsresult rv;
7384 if (mContentViewer) {
7385 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7386 if (mOSHE) {
7387 mOSHE->SyncPresentationState();
7389 mSavingOldViewer = false;
7393 mSavedRefreshURIList = nullptr;
7395 // In cases where we use a transient about:blank viewer between loads,
7396 // we never show the transient viewer, so _its_ previous viewer is never
7397 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7398 // before we grab the root view sibling, so that we don't grab a view
7399 // that's about to go away.
7401 if (mContentViewer) {
7402 // Make sure to hold a strong ref to previousViewer here while we
7403 // drop the reference to it from mContentViewer.
7404 nsCOMPtr<nsIContentViewer> previousViewer =
7405 mContentViewer->GetPreviousViewer();
7406 if (previousViewer) {
7407 mContentViewer->SetPreviousViewer(nullptr);
7408 previousViewer->Destroy();
7412 // Save off the root view's parent and sibling so that we can insert the
7413 // new content viewer's root view at the same position. Also save the
7414 // bounds of the root view's widget.
7416 nsView* rootViewSibling = nullptr;
7417 nsView* rootViewParent = nullptr;
7418 nsIntRect newBounds(0, 0, 0, 0);
7420 PresShell* oldPresShell = GetPresShell();
7421 if (oldPresShell) {
7422 nsViewManager* vm = oldPresShell->GetViewManager();
7423 if (vm) {
7424 nsView* oldRootView = vm->GetRootView();
7426 if (oldRootView) {
7427 rootViewSibling = oldRootView->GetNextSibling();
7428 rootViewParent = oldRootView->GetParent();
7430 mContentViewer->GetBounds(newBounds);
7435 nsCOMPtr<nsIContent> container;
7436 RefPtr<Document> sibling;
7437 if (rootViewParent && rootViewParent->GetParent()) {
7438 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7439 container = frame ? frame->GetContent() : nullptr;
7441 if (rootViewSibling) {
7442 nsIFrame* frame = rootViewSibling->GetFrame();
7443 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7446 // Transfer ownership to mContentViewer. By ensuring that either the
7447 // docshell or the session history, but not both, have references to the
7448 // content viewer, we prevent the viewer from being torn down after
7449 // Destroy() is called.
7451 if (mContentViewer) {
7452 mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7453 viewer->SetPreviousViewer(mContentViewer);
7455 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
7456 // We don't plan to save a viewer in mOSHE; tell it to drop
7457 // any other state it's holding.
7458 mOSHE->SyncPresentationState();
7461 // Order the mContentViewer setup just like Embed does.
7462 mContentViewer = nullptr;
7464 // Now that we're about to switch documents, forget all of our children.
7465 // Note that we cached them as needed up in CaptureState above.
7466 DestroyChildren();
7468 mContentViewer.swap(viewer);
7470 // Grab all of the related presentation from the SHEntry now.
7471 // Clearing the viewer from the SHEntry will clear all of this state.
7472 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7473 mLSHE->SetWindowState(nullptr);
7475 bool sticky = mLSHE->GetSticky();
7477 RefPtr<Document> document = mContentViewer->GetDocument();
7479 nsCOMArray<nsIDocShellTreeItem> childShells;
7480 int32_t i = 0;
7481 nsCOMPtr<nsIDocShellTreeItem> child;
7482 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7483 child) {
7484 childShells.AppendObject(child);
7487 // get the previous content viewer size
7488 nsIntRect oldBounds(0, 0, 0, 0);
7489 mLSHE->GetViewerBounds(oldBounds);
7491 // Restore the refresh URI list. The refresh timers will be restarted
7492 // when EndPageLoad() is called.
7493 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7495 // Reattach to the window object.
7496 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7497 rv = mContentViewer->Open(windowState, mLSHE);
7498 mIsRestoringDocument = false;
7500 // Hack to keep nsDocShellEditorData alive across the
7501 // SetContentViewer(nullptr) call below.
7502 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7504 // Now remove it from the cached presentation.
7505 mLSHE->SetContentViewer(nullptr);
7506 mEODForCurrentDocument = false;
7508 mLSHE->SetEditorData(data.release());
7510 #ifdef DEBUG
7512 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7513 nsCOMPtr<nsIDocShellTreeItem> childShell;
7514 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7515 NS_ASSERTION(!refreshURIs && !childShell,
7516 "SHEntry should have cleared presentation state");
7518 #endif
7520 // Restore the sticky state of the viewer. The viewer has set this state
7521 // on the history entry in Destroy() just before marking itself non-sticky,
7522 // to avoid teardown of the presentation.
7523 mContentViewer->SetSticky(sticky);
7525 NS_ENSURE_SUCCESS(rv, rv);
7527 // mLSHE is now our currently-loaded document.
7528 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7530 // We aren't going to restore any items from the LayoutHistoryState,
7531 // but we don't want them to stay around in case the page is reloaded.
7532 SetLayoutHistoryState(nullptr);
7534 // This is the end of our Embed() replacement
7536 mSavingOldViewer = false;
7537 mEODForCurrentDocument = false;
7539 if (document) {
7540 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7541 if (parent) {
7542 RefPtr<Document> d = parent->GetDocument();
7543 if (d) {
7544 if (d->EventHandlingSuppressed()) {
7545 document->SuppressEventHandling(d->EventHandlingSuppressed());
7550 // Use the uri from the mLSHE we had when we entered this function
7551 // (which need not match the document's URI if anchors are involved),
7552 // since that's the history entry we're loading. Note that if we use
7553 // origLSHE we don't have to worry about whether the entry in question
7554 // is still mLSHE or whether it's now mOSHE.
7555 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7556 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7557 /* aIsInitialAboutBlank */ false,
7558 /* aLocationFlags */ 0);
7561 // This is the end of our CreateContentViewer() replacement.
7562 // Now we simulate a load. First, we restore the state of the javascript
7563 // window object.
7564 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7565 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7567 // Now, dispatch a title change event which would happen as the
7568 // <head> is parsed.
7569 document->NotifyPossibleTitleChange(false);
7571 // Now we simulate appending child docshells for subframes.
7572 for (i = 0; i < childShells.Count(); ++i) {
7573 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7574 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7576 // Make sure to not clobber the state of the child. Since AddChild
7577 // always clobbers it, save it off first.
7578 bool allowRedirects;
7579 childShell->GetAllowMetaRedirects(&allowRedirects);
7581 bool allowSubframes;
7582 childShell->GetAllowSubframes(&allowSubframes);
7584 bool allowImages;
7585 childShell->GetAllowImages(&allowImages);
7587 bool allowMedia = childShell->GetAllowMedia();
7589 bool allowDNSPrefetch;
7590 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7592 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7593 bool allowContentRetargetingOnChildren =
7594 childShell->GetAllowContentRetargetingOnChildren();
7596 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7597 // the child inherits our state. Among other things, this means that the
7598 // child inherits our mPrivateBrowsingId, which is what we want.
7599 AddChild(childItem);
7601 childShell->SetAllowMetaRedirects(allowRedirects);
7602 childShell->SetAllowSubframes(allowSubframes);
7603 childShell->SetAllowImages(allowImages);
7604 childShell->SetAllowMedia(allowMedia);
7605 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7606 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7607 childShell->SetAllowContentRetargetingOnChildren(
7608 allowContentRetargetingOnChildren);
7610 rv = childShell->BeginRestore(nullptr, false);
7611 NS_ENSURE_SUCCESS(rv, rv);
7614 // Make sure to restore the window state after adding the child shells back
7615 // to the tree. This is necessary for Thaw() and Resume() to propagate
7616 // properly.
7617 rv = privWin->RestoreWindowState(windowState);
7618 NS_ENSURE_SUCCESS(rv, rv);
7620 RefPtr<PresShell> presShell = GetPresShell();
7622 // We may be displayed on a different monitor (or in a different
7623 // HiDPI mode) than when we got into the history list. So we need
7624 // to check if this has happened. See bug 838239.
7626 // Because the prescontext normally handles resolution changes via
7627 // a runnable (see nsPresContext::UIResolutionChanged), its device
7628 // context won't be -immediately- updated as a result of calling
7629 // presShell->BackingScaleFactorChanged().
7631 // But we depend on that device context when adjusting the view size
7632 // via mContentViewer->SetBounds(newBounds) below. So we need to
7633 // explicitly tell it to check for changed resolution here.
7634 if (presShell) {
7635 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7636 if (pc->DeviceContext()->CheckDPIChange()) {
7637 presShell->BackingScaleFactorChanged();
7639 // Recompute zoom and text-zoom and such.
7640 pc->RecomputeBrowsingContextDependentData();
7643 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7644 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7646 // Insert the new root view at the correct location in the view tree.
7647 if (container) {
7648 nsSubDocumentFrame* subDocFrame =
7649 do_QueryFrame(container->GetPrimaryFrame());
7650 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7651 } else {
7652 rootViewParent = nullptr;
7654 if (sibling && sibling->GetPresShell() &&
7655 sibling->GetPresShell()->GetViewManager()) {
7656 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7657 } else {
7658 rootViewSibling = nullptr;
7660 if (rootViewParent && newRootView &&
7661 newRootView->GetParent() != rootViewParent) {
7662 nsViewManager* parentVM = rootViewParent->GetViewManager();
7663 if (parentVM) {
7664 // InsertChild(parent, child, sib, true) inserts the child after
7665 // sib in content order, which is before sib in view order. BUT
7666 // when sib is null it inserts at the end of the the document
7667 // order, i.e., first in view order. But when oldRootSibling is
7668 // null, the old root as at the end of the view list --- last in
7669 // content order --- and we want to call InsertChild(parent, child,
7670 // nullptr, false) in that case.
7671 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7672 rootViewSibling ? true : false);
7674 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7675 "error in InsertChild");
7679 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7681 // If parent is suspended, increase suspension count.
7682 // This can't be done as early as event suppression since this
7683 // depends on docshell tree.
7684 privWinInner->SyncStateFromParentWindow();
7686 // Now that all of the child docshells have been put into place, we can
7687 // restart the timers for the window and all of the child frames.
7688 privWinInner->Resume();
7690 // Now that we have found the inner window of the page restored
7691 // from the history, we have to make sure that
7692 // performance.navigation.type is 2.
7693 Performance* performance = privWinInner->GetPerformance();
7694 if (performance) {
7695 performance->GetDOMTiming()->NotifyRestoreStart();
7698 // Restore the refresh URI list. The refresh timers will be restarted
7699 // when EndPageLoad() is called.
7700 mRefreshURIList = refreshURIList;
7702 // Meta-refresh timers have been restarted for this shell, but not
7703 // for our children. Walk the child shells and restart their timers.
7704 for (auto* childDocLoader : mChildList.ForwardRange()) {
7705 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7706 if (child) {
7707 child->ResumeRefreshURIs();
7711 // Make sure this presentation is the same size as the previous
7712 // presentation. If this is not the same size we showed it at last time,
7713 // then we need to resize the widget.
7715 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7716 // presentation had the infobar visible, then we will resize the new
7717 // presentation to that smaller size. However, firing the locationchanged
7718 // event will hide the infobar, which will immediately resize the window
7719 // back to the larger size. A future optimization might be to restore
7720 // the presentation at the "wrong" size, then fire the locationchanged
7721 // event and check whether the docshell's new size is the same as the
7722 // cached viewer size (skipping the resize if they are equal).
7724 if (newRootView) {
7725 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7726 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7727 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7728 newBounds.width, newBounds.height));
7729 mContentViewer->SetBounds(newBounds);
7730 } else {
7731 nsIScrollableFrame* rootScrollFrame =
7732 presShell->GetRootScrollFrameAsScrollable();
7733 if (rootScrollFrame) {
7734 rootScrollFrame->PostScrolledAreaEventForCurrentArea();
7739 // The FinishRestore call below can kill these, null them out so we don't
7740 // have invalid pointer lying around.
7741 newRootView = rootViewSibling = rootViewParent = nullptr;
7742 newVM = nullptr;
7744 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7745 // update it.
7746 if (oldPresShell && presShell &&
7747 presShell->IsUnderHiddenEmbedderElement() !=
7748 oldPresShell->IsUnderHiddenEmbedderElement()) {
7749 presShell->SetIsUnderHiddenEmbedderElement(
7750 oldPresShell->IsUnderHiddenEmbedderElement());
7753 // Simulate the completion of the load.
7754 nsDocShell::FinishRestore();
7756 // Restart plugins, and paint the content.
7757 if (presShell) {
7758 presShell->Thaw();
7761 return privWin->FireDelayedDOMEvents(true);
7764 nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
7765 nsIRequest* aRequest,
7766 nsIStreamListener** aContentHandler) {
7767 if (DocGroup::TryToLoadIframesInBackground()) {
7768 ResetToFirstLoad();
7771 *aContentHandler = nullptr;
7773 if (!mTreeOwner || mIsBeingDestroyed) {
7774 // If we don't have a tree owner, then we're in the process of being
7775 // destroyed. Rather than continue trying to load something, just give up.
7776 return NS_ERROR_DOCSHELL_DYING;
7779 if (!mBrowsingContext->AncestorsAreCurrent() ||
7780 mBrowsingContext->IsInBFCache()) {
7781 mBrowsingContext->RemoveRootFromBFCacheSync();
7782 return NS_ERROR_NOT_AVAILABLE;
7785 // Can we check the content type of the current content viewer
7786 // and reuse it without destroying it and re-creating it?
7788 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7790 // Instantiate the content viewer object
7791 nsCOMPtr<nsIContentViewer> viewer;
7792 nsresult rv = NewContentViewerObj(aContentType, aRequest, mLoadGroup,
7793 aContentHandler, getter_AddRefs(viewer));
7795 if (NS_FAILED(rv)) {
7796 return rv;
7799 // Notify the current document that it is about to be unloaded!!
7801 // It is important to fire the unload() notification *before* any state
7802 // is changed within the DocShell - otherwise, javascript will get the
7803 // wrong information :-(
7806 if (mSavingOldViewer) {
7807 // We determined that it was safe to cache the document presentation
7808 // at the time we initiated the new load. We need to check whether
7809 // it's still safe to do so, since there may have been DOM mutations
7810 // or new requests initiated.
7811 RefPtr<Document> doc = viewer->GetDocument();
7812 mSavingOldViewer = CanSavePresentation(
7813 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
7816 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7818 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7819 if (aOpenedChannel) {
7820 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7823 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7824 // it before we do call Embed.
7825 nsCOMPtr<nsIURI> previousURI = mCurrentURI;
7827 FirePageHideNotification(!mSavingOldViewer);
7828 if (mIsBeingDestroyed) {
7829 // Force to stop the newly created orphaned viewer.
7830 viewer->Stop();
7831 return NS_ERROR_DOCSHELL_DYING;
7833 mLoadingURI = nullptr;
7835 // Set mFiredUnloadEvent = false so that the unload handler for the
7836 // *new* document will fire.
7837 mFiredUnloadEvent = false;
7839 // we've created a new document so go ahead and call
7840 // OnNewURI(), but don't fire OnLocationChange()
7841 // notifications before we've called Embed(). See bug 284993.
7842 mURIResultedInDocument = true;
7843 bool errorOnLocationChangeNeeded = false;
7844 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7845 nsCOMPtr<nsIURI> failedURI;
7847 if (mLoadType == LOAD_ERROR_PAGE) {
7848 // We need to set the SH entry and our current URI here and not
7849 // at the moment we load the page. We want the same behavior
7850 // of Stop() as for a normal page load. See bug 514232 for details.
7852 // Revert mLoadType to load type to state the page load failed,
7853 // following function calls need it.
7854 mLoadType = mFailedLoadType;
7856 Document* doc = viewer->GetDocument();
7857 if (doc) {
7858 doc->SetFailedChannel(failedChannel);
7861 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7862 if (failedChannel) {
7863 // Make sure we have a URI to set currentURI.
7864 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7865 } else {
7866 // if there is no failed channel we have to explicitly provide
7867 // a triggeringPrincipal for the history entry.
7868 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7871 if (!failedURI) {
7872 failedURI = mFailedURI;
7874 if (!failedURI) {
7875 // We need a URI object to store a session history entry, so make up a URI
7876 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7879 // When we don't have failedURI, something wrong will happen. See
7880 // bug 291876.
7881 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7883 mFailedChannel = nullptr;
7884 mFailedURI = nullptr;
7886 // Create an shistory entry for the old load.
7887 if (failedURI) {
7888 errorOnLocationChangeNeeded =
7889 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7890 nullptr, nullptr, false, false, false);
7893 // Be sure to have a correct mLSHE, it may have been cleared by
7894 // EndPageLoad. See bug 302115.
7895 ChildSHistory* shistory = GetSessionHistory();
7896 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7897 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7898 if (idx == -1) {
7899 idx = shistory->Index();
7901 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7904 mLoadType = LOAD_ERROR_PAGE;
7907 nsCOMPtr<nsIURI> finalURI;
7908 // If this a redirect, use the final url (uri)
7909 // else use the original url
7911 // Note that this should match what documents do (see Document::Reset).
7912 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7914 bool onLocationChangeNeeded = false;
7915 if (finalURI) {
7916 // Pass false for aCloneSHChildren, since we're loading a new page here.
7917 onLocationChangeNeeded =
7918 OnNewURI(finalURI, aOpenedChannel, nullptr, nullptr, nullptr, nullptr,
7919 false, true, false);
7922 // let's try resetting the load group if we need to...
7923 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7924 NS_ENSURE_SUCCESS(
7925 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7926 NS_ERROR_FAILURE);
7928 if (currentLoadGroup != mLoadGroup) {
7929 nsLoadFlags loadFlags = 0;
7931 // Cancel any URIs that are currently loading...
7932 // XXX: Need to do this eventually Stop();
7934 // Retarget the document to this loadgroup...
7936 /* First attach the channel to the right loadgroup
7937 * and then remove from the old loadgroup. This
7938 * puts the notifications in the right order and
7939 * we don't null-out mLSHE in OnStateChange() for
7940 * all redirected urls
7942 aOpenedChannel->SetLoadGroup(mLoadGroup);
7944 // Mark the channel as being a document URI...
7945 aOpenedChannel->GetLoadFlags(&loadFlags);
7946 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7947 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7948 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7949 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7952 aOpenedChannel->SetLoadFlags(loadFlags);
7954 mLoadGroup->AddRequest(aRequest, nullptr);
7955 if (currentLoadGroup) {
7956 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7959 // Update the notification callbacks, so that progress and
7960 // status information are sent to the right docshell...
7961 aOpenedChannel->SetNotificationCallbacks(this);
7964 if (DocGroup::TryToLoadIframesInBackground()) {
7965 if ((!mContentViewer || GetDocument()->IsInitialDocument()) &&
7966 IsSubframe()) {
7967 // At this point, we know we just created a new iframe document based on
7968 // the response from the server, and we check if it's a cross-domain
7969 // iframe
7971 RefPtr<Document> newDoc = viewer->GetDocument();
7973 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7974 nsCOMPtr<nsIPrincipal> parentPrincipal =
7975 parent->GetDocument()->NodePrincipal();
7976 nsCOMPtr<nsIPrincipal> thisPrincipal = newDoc->NodePrincipal();
7978 SiteIdentifier parentSite;
7979 SiteIdentifier thisSite;
7981 nsresult rv =
7982 BasePrincipal::Cast(parentPrincipal)->GetSiteIdentifier(parentSite);
7983 NS_ENSURE_SUCCESS(rv, rv);
7985 rv = BasePrincipal::Cast(thisPrincipal)->GetSiteIdentifier(thisSite);
7986 NS_ENSURE_SUCCESS(rv, rv);
7988 if (!parentSite.Equals(thisSite)) {
7989 if (profiler_thread_is_being_profiled_for_markers()) {
7990 nsCOMPtr<nsIURI> prinURI;
7991 BasePrincipal::Cast(thisPrincipal)->GetURI(getter_AddRefs(prinURI));
7992 nsPrintfCString marker(
7993 "Iframe loaded in background: %s",
7994 nsContentUtils::TruncatedURLForDisplay(prinURI).get());
7995 PROFILER_MARKER_TEXT("Background Iframe", DOM, {}, marker);
7997 SetBackgroundLoadIframe();
8002 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
8003 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
8004 aOpenedChannel, previousURI),
8005 NS_ERROR_FAILURE);
8007 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
8008 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
8011 if (TreatAsBackgroundLoad()) {
8012 nsCOMPtr<nsIRunnable> triggerParentCheckDocShell =
8013 NewRunnableMethod("nsDocShell::TriggerParentCheckDocShellIsEmpty", this,
8014 &nsDocShell::TriggerParentCheckDocShellIsEmpty);
8015 nsresult rv = NS_DispatchToCurrentThread(triggerParentCheckDocShell);
8016 NS_ENSURE_SUCCESS(rv, rv);
8019 mSavedRefreshURIList = nullptr;
8020 mSavingOldViewer = false;
8021 mEODForCurrentDocument = false;
8023 // if this document is part of a multipart document,
8024 // the ID can be used to distinguish it from the other parts.
8025 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
8026 if (multiPartChannel) {
8027 if (PresShell* presShell = GetPresShell()) {
8028 if (Document* doc = presShell->GetDocument()) {
8029 uint32_t partID;
8030 multiPartChannel->GetPartID(&partID);
8031 doc->SetPartID(partID);
8036 if (errorOnLocationChangeNeeded) {
8037 FireOnLocationChange(this, failedChannel, failedURI,
8038 LOCATION_CHANGE_ERROR_PAGE);
8039 } else if (onLocationChangeNeeded) {
8040 uint32_t locationFlags =
8041 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
8042 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
8045 return NS_OK;
8048 nsresult nsDocShell::NewContentViewerObj(const nsACString& aContentType,
8049 nsIRequest* aRequest,
8050 nsILoadGroup* aLoadGroup,
8051 nsIStreamListener** aContentHandler,
8052 nsIContentViewer** aViewer) {
8053 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
8055 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
8056 nsContentUtils::FindInternalContentViewer(aContentType);
8057 if (!docLoaderFactory) {
8058 return NS_ERROR_FAILURE;
8061 // Now create an instance of the content viewer nsLayoutDLF makes the
8062 // determination if it should be a "view-source" instead of "view"
8063 nsresult rv = docLoaderFactory->CreateInstance(
8064 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
8065 aContentHandler, aViewer);
8066 NS_ENSURE_SUCCESS(rv, rv);
8068 (*aViewer)->SetContainer(this);
8069 return NS_OK;
8072 nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
8073 WindowGlobalChild* aWindowActor) {
8074 MOZ_ASSERT(!mIsBeingDestroyed);
8077 // Copy content viewer state from previous or parent content viewer.
8079 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
8081 // Do NOT to maintain a reference to the old content viewer outside
8082 // of this "copying" block, or it will not be destroyed until the end of
8083 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
8085 // In this block of code, if we get an error result, we return it
8086 // but if we get a null pointer, that's perfectly legal for parent
8087 // and parentContentViewer.
8090 int32_t x = 0;
8091 int32_t y = 0;
8092 int32_t cx = 0;
8093 int32_t cy = 0;
8095 // This will get the size from the current content viewer or from the
8096 // Init settings
8097 DoGetPositionAndSize(&x, &y, &cx, &cy);
8099 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
8100 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
8101 NS_ERROR_FAILURE);
8102 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
8104 const Encoding* reloadEncoding = nullptr;
8105 int32_t reloadEncodingSource = kCharsetUninitialized;
8106 // |newMUDV| also serves as a flag to set the data from the above vars
8107 nsCOMPtr<nsIContentViewer> newCv;
8109 if (mContentViewer || parent) {
8110 nsCOMPtr<nsIContentViewer> oldCv;
8111 if (mContentViewer) {
8112 // Get any interesting state from old content viewer
8113 // XXX: it would be far better to just reuse the document viewer ,
8114 // since we know we're just displaying the same document as before
8115 oldCv = mContentViewer;
8117 // Tell the old content viewer to hibernate in session history when
8118 // it is destroyed.
8120 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
8121 if (mOSHE) {
8122 mOSHE->SyncPresentationState();
8124 mSavingOldViewer = false;
8126 } else {
8127 // No old content viewer, so get state from parent's content viewer
8128 parent->GetContentViewer(getter_AddRefs(oldCv));
8131 if (oldCv) {
8132 newCv = aNewViewer;
8133 if (newCv) {
8134 reloadEncoding =
8135 oldCv->GetReloadEncodingAndSource(&reloadEncodingSource);
8140 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
8141 bool isUnderHiddenEmbedderElement = false;
8142 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
8143 nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
8144 if (contentViewer) {
8145 // Stop any activity that may be happening in the old document before
8146 // releasing it...
8147 contentViewer->Stop();
8149 // Try to extract the canvas background color from the old
8150 // presentation shell, so we can use it for the next document.
8151 if (PresShell* presShell = contentViewer->GetPresShell()) {
8152 bgcolor = presShell->GetCanvasBackground();
8153 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
8156 contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
8157 aNewViewer->SetPreviousViewer(contentViewer);
8159 if (mOSHE && (!mContentViewer || !mSavingOldViewer)) {
8160 // We don't plan to save a viewer in mOSHE; tell it to drop
8161 // any other state it's holding.
8162 mOSHE->SyncPresentationState();
8165 mContentViewer = nullptr;
8167 // Now that we're about to switch documents, forget all of our children.
8168 // Note that we cached them as needed up in CaptureState above.
8169 DestroyChildren();
8171 mContentViewer = aNewViewer;
8173 nsCOMPtr<nsIWidget> widget;
8174 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8176 nsIntRect bounds(x, y, cx, cy);
8178 mContentViewer->SetNavigationTiming(mTiming);
8180 if (NS_FAILED(mContentViewer->Init(widget, bounds, aWindowActor))) {
8181 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
8182 viewer->Close(nullptr);
8183 viewer->Destroy();
8184 mContentViewer = nullptr;
8185 mCurrentURI = nullptr;
8186 NS_WARNING("ContentViewer Initialization failed");
8187 return NS_ERROR_FAILURE;
8190 // If we have old state to copy, set the old state onto the new content
8191 // viewer
8192 if (newCv) {
8193 newCv->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8196 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8198 // Stuff the bgcolor from the old pres shell into the new
8199 // pres shell. This improves page load continuity.
8200 if (RefPtr<PresShell> presShell = mContentViewer->GetPresShell()) {
8201 presShell->SetCanvasBackground(bgcolor);
8202 presShell->ActivenessMaybeChanged();
8203 if (isUnderHiddenEmbedderElement) {
8204 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8208 // XXX: It looks like the LayoutState gets restored again in Embed()
8209 // right after the call to SetupNewViewer(...)
8211 // We don't show the mContentViewer yet, since we want to draw the old page
8212 // until we have enough of the new page to show. Just return with the new
8213 // viewer still set to hidden.
8215 return NS_OK;
8218 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8219 SessionHistoryInfo* aInfo) {
8220 NS_ENSURE_TRUE_VOID(mContentViewer);
8222 RefPtr<Document> document = GetDocument();
8223 NS_ENSURE_TRUE_VOID(document);
8225 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8226 if (mozilla::SessionHistoryInParent()) {
8227 // If aInfo is null, just set the document's state object to null.
8228 if (aInfo) {
8229 scContainer = aInfo->GetStateData();
8231 MOZ_LOG(gSHLog, LogLevel::Debug,
8232 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8233 } else {
8234 if (aShEntry) {
8235 scContainer = aShEntry->GetStateData();
8237 // If aShEntry is null, just set the document's state object to null.
8241 // It's OK for scContainer too be null here; that just means there's no
8242 // state data associated with this history entry.
8243 document->SetStateObject(scContainer);
8246 nsresult nsDocShell::CheckLoadingPermissions() {
8247 // This method checks whether the caller may load content into
8248 // this docshell. Even though we've done our best to hide windows
8249 // from code that doesn't have the right to access them, it's
8250 // still possible for an evil site to open a window and access
8251 // frames in the new window through window.frames[] (which is
8252 // allAccess for historic reasons), so we still need to do this
8253 // check on load.
8254 nsresult rv = NS_OK;
8256 if (!IsSubframe()) {
8257 // We're not a frame. Permit all loads.
8258 return rv;
8261 // Note - The check for a current JSContext here isn't necessarily sensical.
8262 // It's just designed to preserve the old semantics during a mass-conversion
8263 // patch.
8264 if (!nsContentUtils::GetCurrentJSContext()) {
8265 return NS_OK;
8268 // Check if the caller is from the same origin as this docshell,
8269 // or any of its ancestors.
8270 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8271 bc = bc->GetParent()) {
8272 // If the BrowsingContext is not in process, then it
8273 // is true by construction that its principal will not
8274 // subsume the current docshell principal.
8275 if (!bc->IsInProcess()) {
8276 continue;
8279 nsCOMPtr<nsIScriptGlobalObject> sgo =
8280 bc->GetDocShell()->GetScriptGlobalObject();
8281 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8283 nsIPrincipal* p;
8284 if (!sop || !(p = sop->GetPrincipal())) {
8285 return NS_ERROR_UNEXPECTED;
8288 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8289 // Same origin, permit load
8290 return NS_OK;
8294 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8297 //*****************************************************************************
8298 // nsDocShell: Site Loading
8299 //*****************************************************************************
8301 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8302 bool aInPrivateBrowsing) {
8303 if (XRE_IsContentProcess()) {
8304 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8305 if (contentChild) {
8306 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8308 return;
8311 #ifdef MOZ_PLACES
8312 nsCOMPtr<nsIFaviconService> favSvc =
8313 do_GetService("@mozilla.org/browser/favicon-service;1");
8314 if (favSvc) {
8315 favSvc->CopyFavicons(aOldURI, aNewURI,
8316 aInPrivateBrowsing
8317 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8318 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8319 nullptr);
8321 #endif
8324 class InternalLoadEvent : public Runnable {
8325 public:
8326 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8327 : mozilla::Runnable("InternalLoadEvent"),
8328 mDocShell(aDocShell),
8329 mLoadState(aLoadState) {
8330 // For events, both target and filename should be the version of "null" they
8331 // expect. By the time the event is fired, both window targeting and file
8332 // downloading have been handled, so we should never have an internal load
8333 // event that retargets or had a download.
8334 mLoadState->SetTarget(u""_ns);
8335 mLoadState->SetFileName(VoidString());
8338 NS_IMETHOD
8339 Run() override {
8340 #ifndef ANDROID
8341 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8342 "InternalLoadEvent: Should always have a principal here");
8343 #endif
8344 return mDocShell->InternalLoad(mLoadState);
8347 private:
8348 RefPtr<nsDocShell> mDocShell;
8349 RefPtr<nsDocShellLoadState> mLoadState;
8353 * Returns true if we started an asynchronous load (i.e., from the network), but
8354 * the document we're loading there hasn't yet become this docshell's active
8355 * document.
8357 * When JustStartedNetworkLoad is true, you should be careful about modifying
8358 * mLoadType and mLSHE. These are both set when the asynchronous load first
8359 * starts, and the load expects that, when it eventually runs InternalLoad,
8360 * mLoadType and mLSHE will have their original values.
8362 bool nsDocShell::JustStartedNetworkLoad() {
8363 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8366 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8367 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8368 // <embed>, etc)
8370 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8371 // later when we call DoURILoad.
8372 nsContentPolicyType nsDocShell::DetermineContentType() {
8373 if (!IsSubframe()) {
8374 return nsIContentPolicy::TYPE_DOCUMENT;
8377 const auto& maybeEmbedderElementType =
8378 GetBrowsingContext()->GetEmbedderElementType();
8379 if (!maybeEmbedderElementType) {
8380 // If the EmbedderElementType hasn't been set yet, just assume we're
8381 // an iframe since that's more common.
8382 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8385 return maybeEmbedderElementType->EqualsLiteral("iframe")
8386 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8387 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8390 bool nsDocShell::NoopenerForceEnabled() {
8391 // If current's top-level browsing context's active document's
8392 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8393 // if currentDoc's origin is not same origin with currentDoc's top-level
8394 // origin, noopener is force enabled, and name is cleared to "_blank".
8395 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8396 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8397 topPolicy ==
8398 nsILoadInfo::
8399 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8400 !mBrowsingContext->SameOriginWithTop();
8403 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8404 MOZ_ASSERT(aLoadState, "need a load state!");
8405 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8406 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8407 "should not have picked target yet");
8409 nsresult rv = NS_OK;
8410 RefPtr<BrowsingContext> targetContext;
8412 // Only _self, _parent, and _top are supported in noopener case. But we
8413 // have to be careful to not apply that to the noreferrer case. See bug
8414 // 1358469.
8415 bool allowNamedTarget =
8416 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8417 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8418 if (allowNamedTarget ||
8419 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8420 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8421 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8422 Document* document = GetDocument();
8423 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8424 WindowGlobalChild* wgc = document->GetWindowGlobalChild();
8425 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
8426 targetContext = wgc->FindBrowsingContextWithName(
8427 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8430 if (!targetContext) {
8431 // If the targetContext doesn't exist, then this is a new docShell and we
8432 // should consider this a TYPE_DOCUMENT load
8434 // For example, when target="_blank"
8436 // If there's no targetContext, that means we are about to create a new
8437 // window. Perform a content policy check before creating the window. Please
8438 // note for all other docshell loads content policy checks are performed
8439 // within the contentSecurityManager when the channel is about to be
8440 // openend.
8441 nsISupports* requestingContext = nullptr;
8442 if (XRE_IsContentProcess()) {
8443 // In e10s the child process doesn't have access to the element that
8444 // contains the browsing context (because that element is in the chrome
8445 // process). So we just pass mScriptGlobal.
8446 requestingContext = ToSupports(mScriptGlobal);
8447 } else {
8448 // This is for loading non-e10s tabs and toplevel windows of various
8449 // sorts.
8450 // For the toplevel window cases, requestingElement will be null.
8451 nsCOMPtr<Element> requestingElement =
8452 mScriptGlobal->GetFrameElementInternal();
8453 requestingContext = requestingElement;
8456 // Ideally we should use the same loadinfo as within DoURILoad which
8457 // should match this one when both are applicable.
8458 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
8459 new LoadInfo(mScriptGlobal, aLoadState->URI(),
8460 aLoadState->TriggeringPrincipal(), requestingContext,
8461 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8463 // Since Content Policy checks are performed within docShell as well as
8464 // the ContentSecurityManager we need a reliable way to let certain
8465 // nsIContentPolicy consumers ignore duplicate calls.
8466 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8468 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8469 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8470 ""_ns, // mime guess
8471 &shouldLoad);
8473 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8474 if (NS_SUCCEEDED(rv)) {
8475 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8476 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8478 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8479 return NS_ERROR_BLOCKED_BY_POLICY;
8483 return NS_ERROR_CONTENT_BLOCKED;
8488 // Resolve the window target before going any further...
8489 // If the load has been targeted to another DocShell, then transfer the
8490 // load to it...
8493 // We've already done our owner-inheriting. Mask out that bit, so we
8494 // don't try inheriting an owner from the target window if we came up
8495 // with a null owner above.
8496 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8498 if (!targetContext) {
8499 // If the docshell's document is sandboxed, only open a new window
8500 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8501 // (i.e. if allow-popups is specified)
8502 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
8503 Document* doc = mContentViewer->GetDocument();
8505 const bool isDocumentAuxSandboxed =
8506 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8508 if (isDocumentAuxSandboxed) {
8509 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8512 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8513 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8515 RefPtr<BrowsingContext> newBC;
8516 nsAutoCString spec;
8517 aLoadState->URI()->GetSpec(spec);
8519 // If we are a noopener load, we just hand the whole thing over to our
8520 // window.
8521 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8522 NoopenerForceEnabled()) {
8523 // Various asserts that we know to hold because NO_OPENER loads can only
8524 // happen for links.
8525 MOZ_ASSERT(!aLoadState->LoadReplace());
8526 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8527 aLoadState->TriggeringPrincipal());
8528 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8529 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8530 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8531 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8532 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8533 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8534 aLoadState->IsFormSubmission());
8535 MOZ_ASSERT(!aLoadState->HeadersStream());
8536 // If OnLinkClickSync was invoked inside the onload handler, the load
8537 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8538 // LOAD_LINK.
8539 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8540 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8541 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8542 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8544 RefPtr<nsDocShellLoadState> loadState =
8545 new nsDocShellLoadState(aLoadState->URI());
8547 // Set up our loadinfo so it will do the load as much like we would have
8548 // as possible.
8549 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8550 loadState->SetOriginalURI(aLoadState->OriginalURI());
8552 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8553 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8555 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8556 loadState->SetKeepResultPrincipalURIIfSet(
8557 aLoadState->KeepResultPrincipalURIIfSet());
8558 // LoadReplace will always be false due to asserts above, skip setting
8559 // it.
8560 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8561 loadState->SetTriggeringSandboxFlags(
8562 aLoadState->TriggeringSandboxFlags());
8563 loadState->SetCsp(aLoadState->Csp());
8564 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8565 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8566 // Explicit principal because we do not want any guesses as to what the
8567 // principal to inherit is: it should be aTriggeringPrincipal.
8568 loadState->SetPrincipalIsExplicit(true);
8569 loadState->SetLoadType(aLoadState->LoadType());
8570 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8571 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8573 loadState->SetHasValidUserGestureActivation(
8574 aLoadState->HasValidUserGestureActivation());
8576 // Propagate POST data to the new load.
8577 loadState->SetPostDataStream(aLoadState->PostDataStream());
8578 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8580 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8581 aLoadState->Target(), // window name
8582 u""_ns, // Features
8583 loadState,
8584 true, // aForceNoOpener
8585 getter_AddRefs(newBC));
8586 MOZ_ASSERT(!newBC);
8587 return rv;
8590 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8591 aLoadState->Target(), // window name
8592 u""_ns, // Features
8593 getter_AddRefs(newBC));
8595 // In some cases the Open call doesn't actually result in a new
8596 // window being opened. We can detect these cases by examining the
8597 // document in |newBC|, if any.
8598 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8599 newBC ? newBC->GetDOMWindow() : nullptr;
8600 if (piNewWin) {
8601 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8602 if (!newDoc || newDoc->IsInitialDocument()) {
8603 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8607 if (newBC) {
8608 targetContext = newBC;
8611 NS_ENSURE_SUCCESS(rv, rv);
8612 NS_ENSURE_TRUE(targetContext, rv);
8614 // If our target BrowsingContext is still pending initialization, ignore the
8615 // navigation request targeting it.
8616 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8617 return NS_OK;
8620 aLoadState->SetTargetBrowsingContext(targetContext);
8621 if (aLoadState->IsFormSubmission()) {
8622 aLoadState->SetLoadType(
8623 GetLoadTypeForFormSubmission(targetContext, aLoadState));
8627 // Transfer the load to the target BrowsingContext... Clear the window target
8628 // name to the empty string to prevent recursive retargeting!
8630 // No window target
8631 aLoadState->SetTarget(u""_ns);
8632 // No forced download
8633 aLoadState->SetFileName(VoidString());
8634 return targetContext->InternalLoad(aLoadState);
8637 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8638 nsAutoCString result;
8639 if (NS_FAILED(aURI->GetRef(result))) {
8640 result.SetIsVoid(true);
8642 return result;
8645 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8646 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8648 bool equal = false;
8649 if (mCurrentURI &&
8650 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8651 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8652 flags |= LOCATION_CHANGE_HASHCHANGE;
8655 return flags;
8658 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8659 SameDocumentNavigationState& aState) {
8660 MOZ_ASSERT(aLoadState);
8661 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8662 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8663 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8664 LOAD_FLAGS_REPLACE_HISTORY) ||
8665 aLoadState->LoadType() == LOAD_HISTORY ||
8666 aLoadState->LoadType() == LOAD_LINK)) {
8667 return false;
8670 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8672 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8673 if (NS_SUCCEEDED(rvURINew)) {
8674 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8677 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8678 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8679 if (NS_SUCCEEDED(rvURIOld)) {
8680 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8682 if (NS_SUCCEEDED(rvURIOld)) {
8683 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8684 &aState.mSameExceptHashes))) {
8685 aState.mSameExceptHashes = false;
8690 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8691 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8692 nsCOMPtr<nsIURI> currentExposableURI =
8693 nsIOService::CreateExposableURI(currentURI);
8694 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8695 if (NS_SUCCEEDED(rvURIOld)) {
8696 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8698 if (NS_SUCCEEDED(rvURIOld)) {
8699 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8700 aLoadState->URI(), &aState.mSameExceptHashes))) {
8701 aState.mSameExceptHashes = false;
8703 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8704 // have to perform a special check here to avoid an actual navigation. If
8705 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8706 // fact that the new URI is currently http), then set mSameExceptHashes to
8707 // true and only perform a fragment navigation.
8708 if (!aState.mSameExceptHashes) {
8709 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
8710 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8711 if (!docLoadInfo->GetLoadErrorPage() &&
8712 nsHTTPSOnlyUtils::IsEqualURIExceptSchemeAndRef(
8713 currentExposableURI, aLoadState->URI(), docLoadInfo)) {
8714 uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
8715 if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
8716 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
8717 // At this point the requested URI is for sure a fragment
8718 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8719 // enabled. Also it is not interfering the upgrade order of
8720 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8721 // Since we are on an HTTPS site the fragment
8722 // navigation should also be an HTTPS.
8723 // For that reason we should upgrade the URI to HTTPS.
8724 aState.mSecureUpgradeURI = true;
8725 aState.mSameExceptHashes = true;
8733 if (mozilla::SessionHistoryInParent()) {
8734 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8735 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8736 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8738 MOZ_LOG(gSHLog, LogLevel::Debug,
8739 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8740 this, aState.mHistoryNavBetweenSameDoc));
8741 } else {
8742 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8743 // We're doing a history load.
8745 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8746 &aState.mHistoryNavBetweenSameDoc);
8750 // A same document navigation happens when we navigate between two SHEntries
8751 // for the same document. We do a same document navigation under two
8752 // circumstances. Either
8754 // a) we're navigating between two different SHEntries which share a
8755 // document, or
8757 // b) we're navigating to a new shentry whose URI differs from the
8758 // current URI only in its hash, the new hash is non-empty, and
8759 // we're not doing a POST.
8761 // The restriction that the SHEntries in (a) must be different ensures
8762 // that history.go(0) and the like trigger full refreshes, rather than
8763 // same document navigations.
8764 if (!mozilla::SessionHistoryInParent()) {
8765 bool doSameDocumentNavigation =
8766 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8767 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8768 aState.mSameExceptHashes && aState.mNewURIHasRef);
8769 MOZ_LOG(gSHLog, LogLevel::Debug,
8770 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8771 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8772 return doSameDocumentNavigation;
8775 if (aState.mHistoryNavBetweenSameDoc &&
8776 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8777 return true;
8780 MOZ_LOG(
8781 gSHLog, LogLevel::Debug,
8782 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8783 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8784 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8785 !aLoadState->PostDataStream() ? "true" : "false",
8786 aState.mSameExceptHashes ? "true" : "false",
8787 aState.mNewURIHasRef ? "true" : "false"));
8788 return !aLoadState->LoadIsFromSessionHistory() &&
8789 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8790 aState.mNewURIHasRef;
8793 nsresult nsDocShell::HandleSameDocumentNavigation(
8794 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState) {
8795 #ifdef DEBUG
8796 SameDocumentNavigationState state;
8797 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8798 #endif
8800 MOZ_LOG(gSHLog, LogLevel::Debug,
8801 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8802 mCurrentURI->GetSpecOrDefault().get(),
8803 aLoadState->URI()->GetSpecOrDefault().get()));
8805 RefPtr<Document> doc = GetDocument();
8806 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8807 doc->DoNotifyPossibleTitleChange();
8809 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8811 // We need to upgrade the new URI from http: to https:
8812 nsCOMPtr<nsIURI> newURI = aLoadState->URI();
8813 if (aState.mSecureUpgradeURI) {
8814 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
8815 MOZ_LOG(gSHLog, LogLevel::Debug,
8816 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
8819 #ifdef DEBUG
8820 if (aState.mSameExceptHashes) {
8821 bool sameExceptHashes = false;
8822 currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
8823 MOZ_ASSERT(sameExceptHashes);
8825 #endif
8827 // Save the position of the scrollers.
8828 nsPoint scrollPos = GetCurScrollPos();
8830 // Reset mLoadType to its original value once we exit this block, because this
8831 // same document navigation might have started after a normal, network load,
8832 // and we don't want to clobber its load type. See bug 737307.
8833 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8835 // If a non-same-document-navigation (i.e., a network load) is pending, make
8836 // this a replacement load, so that we don't add a SHEntry here and the
8837 // network load goes into the SHEntry it expects to.
8838 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8839 mLoadType = LOAD_NORMAL_REPLACE;
8840 } else {
8841 mLoadType = aLoadState->LoadType();
8844 mURIResultedInDocument = true;
8846 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8848 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8849 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8850 // onLocationChange() notifications to the browser to update back/forward
8851 // buttons.
8852 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8853 Nothing());
8854 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8855 mLoadingEntry.swap(oldLoadingEntry);
8856 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8857 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8858 *aLoadState->GetLoadingSessionHistoryInfo());
8859 mNeedToReportActiveAfterLoadingBecomesActive = false;
8862 // Set the doc's URI according to the new history entry's URI.
8863 doc->SetDocumentURI(newURI);
8865 /* This is a anchor traversal within the same page.
8866 * call OnNewURI() so that, this traversal will be
8867 * recorded in session and global history.
8869 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8870 newURIPartitionedPrincipalToInherit;
8871 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8872 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8873 if (mozilla::SessionHistoryInParent()) {
8874 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8875 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8876 newURIPartitionedPrincipalToInherit =
8877 mActiveEntry->GetPartitionedPrincipalToInherit();
8878 newCsp = mActiveEntry->GetCsp();
8879 } else {
8880 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8881 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8882 newURIPartitionedPrincipalToInherit =
8883 mOSHE->GetPartitionedPrincipalToInherit();
8884 newCsp = mOSHE->GetCsp();
8886 } else {
8887 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8888 newURIPrincipalToInherit = doc->NodePrincipal();
8889 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8890 newCsp = doc->GetCsp();
8893 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
8895 // Pass true for aCloneSHChildren, since we're not
8896 // changing documents here, so all of our subframes are
8897 // still relevant to the new session history entry.
8899 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8900 // flag on firing onLocationChange(...).
8901 // Anyway, aCloneSHChildren param is simply reflecting
8902 // doSameDocumentNavigation in this scope.
8904 // Note: we'll actually fire onLocationChange later, in order to preserve
8905 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8906 // 1668126)
8907 bool locationChangeNeeded = OnNewURI(
8908 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
8909 newURIPartitionedPrincipalToInherit, newCsp, false, true, true);
8911 nsCOMPtr<nsIInputStream> postData;
8912 uint32_t cacheKey = 0;
8914 bool scrollRestorationIsManual = false;
8915 if (!mozilla::SessionHistoryInParent()) {
8916 if (mOSHE) {
8917 /* save current position of scroller(s) (bug 59774) */
8918 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8919 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8920 // Get the postdata and page ident from the current page, if
8921 // the new load is being done via normal means. Note that
8922 // "normal means" can be checked for just by checking for
8923 // LOAD_CMD_NORMAL, given the loadType and allowScroll check
8924 // above -- it filters out some LOAD_CMD_NORMAL cases that we
8925 // wouldn't want here.
8926 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8927 postData = mOSHE->GetPostData();
8928 cacheKey = mOSHE->GetCacheKey();
8931 // Link our new SHEntry to the old SHEntry's back/forward
8932 // cache data, since the two SHEntries correspond to the
8933 // same document.
8934 if (mLSHE) {
8935 if (!aLoadState->LoadIsFromSessionHistory()) {
8936 // If we're not doing a history load, scroll restoration
8937 // should be inherited from the previous session history entry.
8938 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8939 scrollRestorationIsManual);
8941 mLSHE->AdoptBFCacheEntry(mOSHE);
8944 } else {
8945 if (mActiveEntry) {
8946 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8947 if (mBrowsingContext) {
8948 CollectWireframe();
8949 if (XRE_IsParentProcess()) {
8950 SessionHistoryEntry* entry =
8951 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8952 if (entry) {
8953 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8955 } else {
8956 mozilla::Unused << ContentChild::GetSingleton()
8957 ->SendSessionHistoryEntryScrollPosition(
8958 mBrowsingContext, scrollPos.x,
8959 scrollPos.y);
8963 if (mLoadingEntry) {
8964 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8965 // If we're not doing a history load, scroll restoration
8966 // should be inherited from the previous session history entry.
8967 // XXX This needs most probably tweaks once fragment navigation is
8968 // fixed to work with session-history-in-parent.
8969 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8970 scrollRestorationIsManual);
8975 // If we're doing a history load, use its scroll restoration state.
8976 if (aLoadState->LoadIsFromSessionHistory()) {
8977 if (mozilla::SessionHistoryInParent()) {
8978 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8979 ->mInfo.GetScrollRestorationIsManual();
8980 } else {
8981 scrollRestorationIsManual =
8982 aLoadState->SHEntry()->GetScrollRestorationIsManual();
8986 /* Assign mLSHE to mOSHE. This will either be a new entry created
8987 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8988 * loads.
8990 if (!mozilla::SessionHistoryInParent()) {
8991 if (mLSHE) {
8992 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8993 // Save the postData obtained from the previous page
8994 // in to the session history entry created for the
8995 // anchor page, so that any history load of the anchor
8996 // page will restore the appropriate postData.
8997 if (postData) {
8998 mOSHE->SetPostData(postData);
9001 // Make sure we won't just repost without hitting the
9002 // cache first
9003 if (cacheKey != 0) {
9004 mOSHE->SetCacheKey(cacheKey);
9008 /* Set the title for the SH entry for this target url so that
9009 * SH menus in go/back/forward buttons won't be empty for this.
9010 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
9011 * the code above.
9012 * Note, when session history lives in the parent process, this does not
9013 * update the title there.
9015 SetTitleOnHistoryEntry(false);
9016 } else {
9017 if (aLoadState->LoadIsFromSessionHistory()) {
9018 MOZ_LOG(
9019 gSHLog, LogLevel::Debug,
9020 ("Moving the loading entry to the active entry on nsDocShell %p to "
9021 "%s",
9022 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
9024 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
9025 if (mActiveEntry) {
9026 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
9029 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
9030 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
9031 if (currentLayoutHistoryState) {
9032 // Restore the existing nsILayoutHistoryState object, since it is
9033 // possibly being used by the layout. When doing a new load, the
9034 // shared state is copied from the existing active entry, so this
9035 // special case is needed only with the history loads.
9036 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
9039 if (cacheKey != 0) {
9040 mActiveEntry->SetCacheKey(cacheKey);
9042 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
9043 // does require a non-null uri if this is for a refresh load of the same
9044 // URI, but in that case mCurrentURI won't be null here.
9045 mBrowsingContext->SessionHistoryCommit(
9046 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
9047 true, true,
9048 /* No expiration update on the same document loads*/
9049 false, cacheKey);
9050 // FIXME Need to set postdata.
9052 // Set the title for the SH entry for this target url so that
9053 // SH menus in go/back/forward buttons won't be empty for this.
9054 // Note, when session history lives in the parent process, this does not
9055 // update the title there.
9056 SetTitleOnHistoryEntry(false);
9057 } else {
9058 Maybe<bool> scrollRestorationIsManual;
9059 if (mActiveEntry) {
9060 scrollRestorationIsManual.emplace(
9061 mActiveEntry->GetScrollRestorationIsManual());
9063 // Get the postdata and page ident from the current page, if the new
9064 // load is being done via normal means. Note that "normal means" can be
9065 // checked for just by checking for LOAD_CMD_NORMAL, given the loadType
9066 // and allowScroll check above -- it filters out some LOAD_CMD_NORMAL
9067 // cases that we wouldn't want here.
9068 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
9069 postData = mActiveEntry->GetPostData();
9070 cacheKey = mActiveEntry->GetCacheKey();
9074 MOZ_LOG(gSHLog, LogLevel::Debug,
9075 ("Creating an active entry on nsDocShell %p to %s", this,
9076 newURI->GetSpecOrDefault().get()));
9077 if (mActiveEntry) {
9078 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, newURI);
9079 } else {
9080 mActiveEntry = MakeUnique<SessionHistoryInfo>(
9081 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
9082 newURIPartitionedPrincipalToInherit, newCsp, mContentTypeHint);
9085 // Save the postData obtained from the previous page in to the session
9086 // history entry created for the anchor page, so that any history load of
9087 // the anchor page will restore the appropriate postData.
9088 if (postData) {
9089 mActiveEntry->SetPostData(postData);
9092 // Make sure we won't just repost without hitting the
9093 // cache first
9094 if (cacheKey != 0) {
9095 mActiveEntry->SetCacheKey(cacheKey);
9098 // Set the title for the SH entry for this target url so that
9099 // SH menus in go/back/forward buttons won't be empty for this.
9100 mActiveEntry->SetTitle(mTitle);
9102 if (scrollRestorationIsManual.isSome()) {
9103 mActiveEntry->SetScrollRestorationIsManual(
9104 scrollRestorationIsManual.value());
9107 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9108 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
9109 } else {
9110 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
9111 // FIXME We should probably just compute mChildOffset in the parent
9112 // instead of passing it over IPC here.
9113 mBrowsingContext->SetActiveSessionHistoryEntry(
9114 Some(scrollPos), mActiveEntry.get(), mLoadType, cacheKey);
9115 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9120 if (locationChangeNeeded) {
9121 FireOnLocationChange(this, nullptr, newURI, locationChangeFlags);
9124 /* Restore the original LSHE if we were loading something
9125 * while same document navigation was initiated.
9127 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
9128 mLoadingEntry.swap(oldLoadingEntry);
9130 /* Set the title for the Global History entry for this anchor url.
9132 UpdateGlobalHistoryTitle(newURI);
9134 SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
9136 // Inform the favicon service that the favicon for oldURI also
9137 // applies to newURI.
9138 CopyFavicon(currentURI, newURI, UsePrivateBrowsing());
9140 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9141 RefPtr<nsGlobalWindowInner> win =
9142 scriptGlobal ? scriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
9144 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9145 // the function decides whether a scroll is appropriate based on the
9146 // arguments it receives. But even if we don't end up scrolling,
9147 // ScrollToAnchor performs other important tasks, such as informing
9148 // the presShell that we have a new hash. See bug 680257.
9149 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
9150 aState.mNewHash, aLoadState->LoadType());
9151 NS_ENSURE_SUCCESS(rv, rv);
9153 /* restore previous position of scroller(s), if we're moving
9154 * back in history (bug 59774)
9156 nscoord bx = 0;
9157 nscoord by = 0;
9158 bool needsScrollPosUpdate = false;
9159 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
9160 (aLoadState->LoadType() == LOAD_HISTORY ||
9161 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
9162 !scrollRestorationIsManual) {
9163 needsScrollPosUpdate = true;
9164 if (mozilla::SessionHistoryInParent()) {
9165 mActiveEntry->GetScrollPosition(&bx, &by);
9166 } else {
9167 mOSHE->GetScrollPosition(&bx, &by);
9171 // Dispatch the popstate and hashchange events, as appropriate.
9173 // The event dispatch below can cause us to re-enter script and
9174 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9175 // reference to avoid null derefs. See bug 914521.
9176 if (win) {
9177 // Fire a hashchange event URIs differ, and only in their hashes.
9178 bool doHashchange = aState.mSameExceptHashes &&
9179 (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
9180 !aState.mCurrentHash.Equals(aState.mNewHash));
9182 if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
9183 win->DispatchSyncPopState();
9186 if (needsScrollPosUpdate && win->HasActiveDocument()) {
9187 SetCurScrollPosEx(bx, by);
9190 if (doHashchange) {
9191 // Note that currentURI hasn't changed because it's on the
9192 // stack, so we can just use it directly as the old URI.
9193 win->DispatchAsyncHashchange(currentURI, newURI);
9197 return NS_OK;
9200 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
9201 nsDocShellLoadState* aLoadState) {
9202 if (!aLoadState->AllowFocusMove()) {
9203 return false;
9205 if (!aLoadState->HasValidUserGestureActivation()) {
9206 return false;
9208 const auto& sourceBC = aLoadState->SourceBrowsingContext();
9209 if (!sourceBC || !sourceBC->IsActive()) {
9210 // If the navigation didn't come from a foreground tab, then we don't steal
9211 // focus.
9212 return false;
9214 auto* bc = aDocShell->GetBrowsingContext();
9215 if (sourceBC.get() == bc) {
9216 // If it comes from the same tab / frame, don't steal focus either.
9217 return false;
9219 auto* fm = nsFocusManager::GetFocusManager();
9220 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
9221 // If we're already on the foreground tab of the foreground window, then we
9222 // don't need to do this. This helps to e.g. not steal focus from the
9223 // browser chrome unnecessarily.
9224 return false;
9226 if (auto* doc = aDocShell->GetExtantDocument()) {
9227 if (doc->IsInitialDocument()) {
9228 // If we're the initial load for the browsing context, the browser
9229 // chrome determines what to focus. This is important because the
9230 // browser chrome may want to e.g focus the url-bar
9231 return false;
9234 // Take loadDivertedInBackground into account so the behavior would be the
9235 // same as how the tab first opened.
9236 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9239 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
9240 BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) {
9241 MOZ_ASSERT(aLoadState->IsFormSubmission());
9243 // https://html.spec.whatwg.org/#form-submission-algorithm
9244 // 22. Let historyHandling be "push".
9245 // 23. If form document equals targetNavigable's active document, and
9246 // form document has not yet completely loaded, then set
9247 // historyHandling to "replace".
9248 return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument
9249 ? LOAD_NORMAL_REPLACE
9250 : LOAD_LINK;
9253 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
9254 Maybe<uint32_t> aCacheKey) {
9255 MOZ_ASSERT(aLoadState, "need a load state!");
9256 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
9257 "need a valid TriggeringPrincipal");
9259 if (!aLoadState->TriggeringPrincipal()) {
9260 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9261 return NS_ERROR_FAILURE;
9263 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
9264 return NS_ERROR_NOT_AVAILABLE;
9267 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
9269 mOriginalUriString.Truncate();
9271 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9272 ("DOCSHELL %p InternalLoad %s\n", this,
9273 aLoadState->URI()->GetSpecOrDefault().get()));
9275 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
9277 // Cancel loads coming from Docshells that are being destroyed.
9278 if (mIsBeingDestroyed) {
9279 return NS_ERROR_NOT_AVAILABLE;
9282 nsresult rv = EnsureScriptEnvironment();
9283 if (NS_FAILED(rv)) {
9284 return rv;
9287 // If we have a target to move to, do that now.
9288 if (!aLoadState->Target().IsEmpty()) {
9289 return PerformRetargeting(aLoadState);
9292 // This is the non-retargeting load path, we've already set the right loadtype
9293 // for form submissions in nsDocShell::OnLinkClickSync.
9294 if (aLoadState->TargetBrowsingContext().IsNull()) {
9295 aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
9298 MOZ_DIAGNOSTIC_ASSERT(
9299 aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
9300 "Load must be targeting this BrowsingContext");
9302 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
9304 // If we don't have a target, we're loading into ourselves, and our load
9305 // delegate may want to intercept that load.
9306 SameDocumentNavigationState sameDocumentNavigationState;
9307 bool sameDocument =
9308 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
9309 !aLoadState->GetPendingRedirectedChannel();
9311 // Note: We do this check both here and in BrowsingContext::
9312 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9313 // available in the process triggering the load, and we don't want the target
9314 // process to have to trust the triggering process to do the appropriate
9315 // checks for the BrowsingContext's sandbox flags.
9316 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
9318 NS_ENSURE_STATE(!HasUnloadedParent());
9320 rv = CheckLoadingPermissions();
9321 if (NS_FAILED(rv)) {
9322 return rv;
9325 if (mFiredUnloadEvent) {
9326 if (IsOKToLoadURI(aLoadState->URI())) {
9327 MOZ_ASSERT(aLoadState->Target().IsEmpty(),
9328 "Shouldn't have a window target here!");
9330 // If this is a replace load, make whatever load triggered
9331 // the unload event also a replace load, so we don't
9332 // create extra history entries.
9333 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
9334 LOAD_FLAGS_REPLACE_HISTORY)) {
9335 mLoadType = LOAD_NORMAL_REPLACE;
9338 // Do this asynchronously
9339 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
9340 return Dispatch(TaskCategory::Other, ev.forget());
9343 // Just ignore this load attempt
9344 return NS_OK;
9347 // If we are loading a URI that should inherit a security context (basically
9348 // javascript: at this point), and the caller has said that principal
9349 // inheritance is allowed, there are a few possible cases:
9351 // 1) We are provided with the principal to inherit. In that case, we just use
9352 // it.
9354 // 2) The load is coming from some other application. In this case we don't
9355 // want to inherit from whatever document we have loaded now, since the
9356 // load is unrelated to it.
9358 // 3) It's a load from our application, but does not provide an explicit
9359 // principal to inherit. In that case, we want to inherit the principal of
9360 // our current document, or of our parent document (if any) if we don't
9361 // have a current document.
9363 bool inherits;
9365 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
9366 !aLoadState->PrincipalToInherit() &&
9367 (aLoadState->HasInternalLoadFlags(
9368 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
9369 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9370 aLoadState->URI(), &inherits)) &&
9371 inherits) {
9372 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
9374 // If principalToInherit is still null (e.g. if some of the conditions of
9375 // were not satisfied), then no inheritance of any sort will happen: the
9376 // load will just get a principal based on the URI being loaded.
9379 // If this docshell is owned by a frameloader, make sure to cancel
9380 // possible frameloader initialization before loading a new page.
9381 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
9382 if (parent) {
9383 RefPtr<Document> doc = parent->GetDocument();
9384 if (doc) {
9385 doc->TryCancelFrameLoaderInitialization(this);
9389 // Before going any further vet loads initiated by external programs.
9390 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9391 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
9393 // Disallow external chrome: loads targetted at content windows
9394 if (SchemeIsChrome(aLoadState->URI())) {
9395 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9396 return NS_ERROR_FAILURE;
9399 // clear the decks to prevent context bleed-through (bug 298255)
9400 rv = CreateAboutBlankContentViewer(nullptr, nullptr, nullptr, nullptr,
9401 /* aIsInitialDocument */ false);
9402 if (NS_FAILED(rv)) {
9403 return NS_ERROR_FAILURE;
9407 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
9408 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
9409 mURIResultedInDocument = false; // reset the clock...
9411 // See if this is actually a load between two history entries for the same
9412 // document. If the process fails, or if we successfully navigate within the
9413 // same document, return.
9414 if (sameDocument) {
9415 nsresult rv =
9416 HandleSameDocumentNavigation(aLoadState, sameDocumentNavigationState);
9417 NS_ENSURE_SUCCESS(rv, rv);
9418 if (shouldTakeFocus) {
9419 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9421 return rv;
9424 // mContentViewer->PermitUnload can destroy |this| docShell, which
9425 // causes the next call of CanSavePresentation to crash.
9426 // Hold onto |this| until we return, to prevent a crash from happening.
9427 // (bug#331040)
9428 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9430 // Don't init timing for javascript:, since it generally doesn't
9431 // actually start a load or anything. If it does, we'll init
9432 // timing then, from OnStateChange.
9434 // XXXbz mTiming should know what channel it's for, so we don't
9435 // need this hackery.
9436 bool toBeReset = false;
9437 bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
9439 if (!isJavaScript) {
9440 toBeReset = MaybeInitTiming();
9442 bool isNotDownload = aLoadState->FileName().IsVoid();
9443 if (mTiming && isNotDownload) {
9444 mTiming->NotifyBeforeUnload();
9446 // Check if the page doesn't want to be unloaded. The javascript:
9447 // protocol handler deals with this for javascript: URLs.
9448 if (!isJavaScript && isNotDownload &&
9449 !aLoadState->NotifiedBeforeUnloadListeners() && mContentViewer) {
9450 bool okToUnload;
9452 // Check if request is exempted from HTTPSOnlyMode and if https-first is
9453 // enabled, if so it means:
9454 // * https-first failed to upgrade request to https
9455 // * we already asked for permission to unload and the user accepted
9456 // otherwise we wouldn't be here.
9457 bool isPrivateWin = GetOriginAttributes().mPrivateBrowsingId > 0;
9458 bool isHistoryOrReload = false;
9459 uint32_t loadType = aLoadState->LoadType();
9461 // Check if request is a reload.
9462 if (loadType == LOAD_RELOAD_NORMAL ||
9463 loadType == LOAD_RELOAD_BYPASS_CACHE ||
9464 loadType == LOAD_RELOAD_BYPASS_PROXY ||
9465 loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
9466 loadType == LOAD_HISTORY) {
9467 isHistoryOrReload = true;
9470 // If it isn't a reload, the request already failed to be upgraded and
9471 // https-first is enabled then don't ask the user again for permission to
9472 // unload and just unload.
9473 if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSOnlyMode() &&
9474 nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
9475 rv = mContentViewer->PermitUnload(
9476 nsIContentViewer::PermitUnloadAction::eDontPromptAndUnload,
9477 &okToUnload);
9478 } else {
9479 rv = mContentViewer->PermitUnload(&okToUnload);
9482 if (NS_SUCCEEDED(rv) && !okToUnload) {
9483 // The user chose not to unload the page, interrupt the
9484 // load.
9485 MaybeResetInitTiming(toBeReset);
9486 return NS_OK;
9490 if (mTiming && isNotDownload) {
9491 mTiming->NotifyUnloadAccepted(mCurrentURI);
9494 // In e10s, in the parent process, we refuse to load anything other than
9495 // "safe" resources that we ship or trust enough to give "special" URLs.
9496 // Similar check will be performed by the ParentProcessDocumentChannel if in
9497 // use.
9498 if (XRE_IsE10sParentProcess() &&
9499 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
9500 !CanLoadInParentProcess(aLoadState->URI())) {
9501 return NS_ERROR_FAILURE;
9504 // Whenever a top-level browsing context is navigated, the user agent MUST
9505 // lock the orientation of the document to the document's default
9506 // orientation. We don't explicitly check for a top-level browsing context
9507 // here because orientation is only set on top-level browsing contexts.
9508 if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) {
9509 MOZ_ASSERT(mBrowsingContext->IsTop());
9510 MOZ_ALWAYS_SUCCEEDS(
9511 mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None));
9512 if (mBrowsingContext->IsActive()) {
9513 ScreenOrientation::UpdateActiveOrientationLock(
9514 hal::ScreenOrientation::None);
9518 // Check for saving the presentation here, before calling Stop().
9519 // This is necessary so that we can catch any pending requests.
9520 // Since the new request has not been created yet, we pass null for the
9521 // new request parameter.
9522 // Also pass nullptr for the document, since it doesn't affect the return
9523 // value for our purposes here.
9524 bool savePresentation =
9525 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr,
9526 /* aReportBFCacheComboTelemetry */ true);
9528 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9529 // separate check for SHIP so that we know if there are ongoing requests
9530 // before calling Stop() below.
9531 if (mozilla::SessionHistoryInParent()) {
9532 Document* document = GetDocument();
9533 uint32_t flags = 0;
9534 if (document && !document->CanSavePresentation(nullptr, flags, true)) {
9535 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9536 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9537 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9538 // Also, we want to report all the flags to the parent process here (and
9539 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9540 // telemetry data correctly.
9541 document->DisallowBFCaching(flags);
9545 // Don't stop current network activity for javascript: URL's since
9546 // they might not result in any data, and thus nothing should be
9547 // stopped in those cases. In the case where they do result in
9548 // data, the javascript: URL channel takes care of stopping
9549 // current network activity.
9550 if (!isJavaScript && isNotDownload) {
9551 // Stop any current network activity.
9552 // Also stop content if this is a zombie doc. otherwise
9553 // the onload will be delayed by other loads initiated in the
9554 // background by the first document that
9555 // didn't fully load before the next load was initiated.
9556 // If not a zombie, don't stop content until data
9557 // starts arriving from the new URI...
9559 if ((mContentViewer && mContentViewer->GetPreviousViewer()) ||
9560 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
9561 rv = Stop(nsIWebNavigation::STOP_ALL);
9562 } else {
9563 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9566 if (NS_FAILED(rv)) {
9567 return rv;
9571 mLoadType = aLoadState->LoadType();
9573 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9574 // been called. But when loading an error page, do not clear the
9575 // mLSHE for the real page.
9576 if (mLoadType != LOAD_ERROR_PAGE) {
9577 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
9578 Nothing());
9579 if (aLoadState->LoadIsFromSessionHistory() &&
9580 !mozilla::SessionHistoryInParent()) {
9581 // We're making history navigation or a reload. Make sure our history ID
9582 // points to the same ID as SHEntry's docshell ID.
9583 nsID historyID = {};
9584 aLoadState->SHEntry()->GetDocshellID(historyID);
9586 Unused << mBrowsingContext->SetHistoryID(historyID);
9590 mSavingOldViewer = savePresentation;
9592 // If we have a saved content viewer in history, restore and show it now.
9593 if (aLoadState->LoadIsFromSessionHistory() &&
9594 (mLoadType & LOAD_CMD_HISTORY)) {
9595 // https://html.spec.whatwg.org/#history-traversal:
9596 // To traverse the history
9597 // "If entry has a different Document object than the current entry, then
9598 // run the following substeps: Remove any tasks queued by the history
9599 // traversal task source..."
9600 // Same document object case was handled already above with
9601 // HandleSameDocumentNavigation call.
9602 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
9603 if (shistory) {
9604 shistory->RemovePendingHistoryNavigations();
9606 if (!mozilla::SessionHistoryInParent()) {
9607 // It's possible that the previous viewer of mContentViewer is the
9608 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9609 // If that's the case, we need to go ahead and force it into its shentry
9610 // so we can restore it.
9611 if (mContentViewer) {
9612 nsCOMPtr<nsIContentViewer> prevViewer =
9613 mContentViewer->GetPreviousViewer();
9614 if (prevViewer) {
9615 #ifdef DEBUG
9616 nsCOMPtr<nsIContentViewer> prevPrevViewer =
9617 prevViewer->GetPreviousViewer();
9618 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9619 #endif
9620 nsCOMPtr<nsISHEntry> viewerEntry;
9621 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9622 if (viewerEntry == aLoadState->SHEntry()) {
9623 // Make sure this viewer ends up in the right place
9624 mContentViewer->SetPreviousViewer(nullptr);
9625 prevViewer->Destroy();
9629 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9630 bool restoring;
9631 rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
9632 if (restoring) {
9633 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
9634 return rv;
9636 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
9638 // We failed to restore the presentation, so clean up.
9639 // Both the old and new history entries could potentially be in
9640 // an inconsistent state.
9641 if (NS_FAILED(rv)) {
9642 if (oldEntry) {
9643 oldEntry->SyncPresentationState();
9646 aLoadState->SHEntry()->SyncPresentationState();
9651 bool isTopLevelDoc = mBrowsingContext->IsTopContent();
9653 OriginAttributes attrs = GetOriginAttributes();
9654 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
9656 PredictorLearn(aLoadState->URI(), nullptr,
9657 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
9658 PredictorPredict(aLoadState->URI(), nullptr,
9659 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
9661 nsCOMPtr<nsIRequest> req;
9662 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
9664 if (NS_SUCCEEDED(rv)) {
9665 if (shouldTakeFocus) {
9666 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9670 if (NS_FAILED(rv)) {
9671 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9672 UnblockEmbedderLoadEventForFailure();
9673 if (DisplayLoadError(rv, aLoadState->URI(), nullptr, chan) &&
9674 // FIXME: At this point code was using internal load flags, but checking
9675 // non-internal load flags?
9676 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
9677 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9680 // We won't report any error if this is an unknown protocol error. The
9681 // reason behind this is that it will allow enumeration of external
9682 // protocols if we report an error for each unknown protocol.
9683 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
9684 return NS_OK;
9688 return rv;
9691 /* static */
9692 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
9693 nsCOMPtr<nsIURI> uri = aURI;
9694 // In e10s, in the parent process, we refuse to load anything other than
9695 // "safe" resources that we ship or trust enough to give "special" URLs.
9696 bool canLoadInParent = false;
9697 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9698 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
9699 canLoadInParent) {
9700 // We allow UI resources.
9701 return true;
9703 // For about: and extension-based URIs, which don't get
9704 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9705 while (uri && uri->SchemeIs("view-source")) {
9706 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
9707 if (nested) {
9708 nested->GetInnerURI(getter_AddRefs(uri));
9709 } else {
9710 break;
9713 // Allow about: URIs, and allow moz-extension ones if we're running
9714 // extension content in the parent process.
9715 if (!uri || uri->SchemeIs("about") ||
9716 (!StaticPrefs::extensions_webextensions_remote() &&
9717 uri->SchemeIs("moz-extension"))) {
9718 return true;
9720 #ifdef MOZ_THUNDERBIRD
9721 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
9722 uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
9723 uri->SchemeIs("snews")) {
9724 return true;
9726 #endif
9727 nsAutoCString scheme;
9728 uri->GetScheme(scheme);
9729 // Allow ext+foo URIs (extension-registered custom protocols). See
9730 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9731 if (StringBeginsWith(scheme, "ext+"_ns) &&
9732 !StaticPrefs::extensions_webextensions_remote()) {
9733 return true;
9735 // Final exception for some legacy automated tests:
9736 if (xpc::IsInAutomation() &&
9737 StaticPrefs::security_allow_unsafe_parent_loads()) {
9738 return true;
9740 return false;
9743 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
9744 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
9745 RefPtr<Document> document;
9746 bool inheritedFromCurrent = false;
9748 if (aConsiderCurrentDocument && mContentViewer) {
9749 document = mContentViewer->GetDocument();
9750 inheritedFromCurrent = true;
9753 if (!document) {
9754 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9755 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9756 if (parentItem) {
9757 document = parentItem->GetDocument();
9761 if (!document) {
9762 if (!aConsiderCurrentDocument) {
9763 return nullptr;
9766 // Make sure we end up with _something_ as the principal no matter
9767 // what.If this fails, we'll just get a null docViewer and bail.
9768 EnsureContentViewer();
9769 if (!mContentViewer) {
9770 return nullptr;
9772 document = mContentViewer->GetDocument();
9775 //-- Get the document's principal
9776 if (document) {
9777 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
9778 ? document->PartitionedPrincipal()
9779 : document->NodePrincipal();
9781 // Don't allow loads in typeContent docShells to inherit the system
9782 // principal from existing documents.
9783 if (inheritedFromCurrent && mItemType == typeContent &&
9784 docPrincipal->IsSystemPrincipal()) {
9785 return nullptr;
9788 return docPrincipal;
9791 return nullptr;
9794 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9795 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9796 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9797 const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9798 nsCOMPtr<nsIChannel> channel;
9799 if (aSrcdoc.IsVoid()) {
9800 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9801 nullptr, // PerformanceStorage
9802 nullptr, // loadGroup
9803 aCallbacks, aLoadFlags));
9805 if (aBaseURI) {
9806 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9807 if (vsc) {
9808 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9811 } else if (SchemeIsViewSource(aURI)) {
9812 // Instantiate view source handler protocol, if it doesn't exist already.
9813 nsCOMPtr<nsIIOService> io(do_GetIOService());
9814 MOZ_ASSERT(io);
9815 nsCOMPtr<nsIProtocolHandler> handler;
9816 nsresult rv =
9817 io->GetProtocolHandler("view-source", getter_AddRefs(handler));
9818 if (NS_FAILED(rv)) {
9819 return rv;
9822 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9823 if (!vsh) {
9824 return NS_ERROR_FAILURE;
9827 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9828 getter_AddRefs(channel)));
9829 } else {
9830 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
9831 aSrcdoc, "text/html"_ns, aLoadInfo,
9832 true));
9833 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9834 MOZ_ASSERT(isc);
9835 isc->SetBaseURI(aBaseURI);
9838 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9839 nsresult rv = channel->SetLoadFlags(aLoadFlags);
9840 NS_ENSURE_SUCCESS(rv, rv);
9843 channel.forget(aChannel);
9844 return NS_OK;
9847 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9848 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9849 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9850 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9851 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9852 nsIChannel** aChannel) {
9853 MOZ_ASSERT(aLoadInfo);
9855 nsString srcdoc = VoidString();
9856 bool isSrcdoc =
9857 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9858 if (isSrcdoc) {
9859 srcdoc = aLoadState->SrcdocData();
9862 aLoadInfo->SetTriggeringRemoteType(
9863 aLoadState->GetEffectiveTriggeringRemoteType());
9865 if (aLoadState->PrincipalToInherit()) {
9866 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9868 aLoadInfo->SetLoadTriggeredFromExternal(
9869 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
9870 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
9871 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9872 aLoadInfo->SetOriginalFrameSrcLoad(
9873 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9875 bool inheritAttrs = false;
9876 if (aLoadState->PrincipalToInherit()) {
9877 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9878 aLoadState->PrincipalToInherit(), aLoadState->URI(),
9879 true, // aInheritForAboutBlank
9880 isSrcdoc);
9883 // Strip the target query parameters before creating the channel.
9884 aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext);
9886 OriginAttributes attrs;
9888 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9889 // true. Otherwise we just use the origin attributes from docshell.
9890 if (inheritAttrs) {
9891 MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9892 "We should have PrincipalToInherit here.");
9893 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9894 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9895 // have the same origin attributes with docshell.
9896 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9897 attrs == aOriginAttributes);
9898 } else {
9899 attrs = aOriginAttributes;
9900 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9901 aLoadState->URI());
9904 aRv = aLoadInfo->SetOriginAttributes(attrs);
9905 if (NS_WARN_IF(NS_FAILED(aRv))) {
9906 return false;
9909 if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9910 aLoadInfo->SetIsFromProcessingFrameAttributes();
9913 // Propagate the IsFormSubmission flag to the loadInfo.
9914 if (aLoadState->IsFormSubmission()) {
9915 aLoadInfo->SetIsFormSubmission(true);
9918 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
9920 nsCOMPtr<nsIChannel> channel;
9921 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9922 aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9923 aLoadState->BaseURI());
9924 NS_ENSURE_SUCCESS(aRv, false);
9926 if (!channel) {
9927 return false;
9930 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9931 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9932 // HTTPS_ONLY_EXEMPT.
9933 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
9935 // hack
9936 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9937 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9938 do_QueryInterface(channel));
9939 nsCOMPtr<nsIURI> referrer;
9940 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9941 if (referrerInfo) {
9942 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9944 if (httpChannelInternal) {
9945 if (aLoadState->HasInternalLoadFlags(
9946 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9947 aRv = httpChannelInternal->SetThirdPartyFlags(
9948 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9949 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9951 if (aLoadState->FirstParty()) {
9952 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9953 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9954 } else {
9955 aRv = httpChannelInternal->SetDocumentURI(referrer);
9956 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9958 aRv = httpChannelInternal->SetRedirectMode(
9959 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9960 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9963 if (httpChannel) {
9964 if (aLoadState->HeadersStream()) {
9965 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9967 // Set the referrer explicitly
9968 // Referrer is currenly only set for link clicks here.
9969 if (referrerInfo) {
9970 aRv = httpChannel->SetReferrerInfo(referrerInfo);
9971 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9974 // Mark the http channel as UrgentStart for top level document loading in
9975 // active tab.
9976 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9977 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9978 if (cos) {
9979 cos->AddClassFlags(nsIClassOfService::UrgentStart);
9984 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
9985 : aLoadState->URI());
9987 const nsACString& typeHint = aLoadState->TypeHint();
9988 if (!typeHint.IsVoid()) {
9989 channel->SetContentType(typeHint);
9992 const nsAString& fileName = aLoadState->FileName();
9993 if (!fileName.IsVoid()) {
9994 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9995 NS_ENSURE_SUCCESS(aRv, false);
9996 if (!fileName.IsEmpty()) {
9997 aRv = channel->SetContentDispositionFilename(fileName);
9998 NS_ENSURE_SUCCESS(aRv, false);
10002 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
10003 nsCOMPtr<nsIURI> referrer;
10004 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
10005 if (referrerInfo) {
10006 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
10008 // save true referrer for those who need it (e.g. xpinstall whitelisting)
10009 // Currently only http and ftp channels support this.
10010 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
10012 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
10013 props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true);
10017 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
10018 auto loadType = aLoadState->LoadType();
10020 if (loadType == LOAD_RELOAD_NORMAL &&
10021 StaticPrefs::
10022 browser_soft_reload_only_force_validate_top_level_document()) {
10023 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10024 if (cachingChannel) {
10025 cachingChannel->SetForceValidateCacheContent(true);
10029 // figure out if we need to set the post data stream on the channel...
10030 if (aLoadState->PostDataStream()) {
10031 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
10032 do_QueryInterface(channel)) {
10033 // XXX it's a bit of a hack to rewind the postdata stream here but
10034 // it has to be done in case the post data is being reused multiple
10035 // times.
10036 nsCOMPtr<nsISeekableStream> postDataSeekable =
10037 do_QueryInterface(aLoadState->PostDataStream());
10038 if (postDataSeekable) {
10039 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
10040 NS_ENSURE_SUCCESS(aRv, false);
10043 // we really need to have a content type associated with this stream!!
10044 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
10046 // Ownership of the stream has transferred to the channel, clear our
10047 // reference.
10048 aLoadState->SetPostDataStream(nullptr);
10051 /* If there is a valid postdata *and* it is a History Load,
10052 * set up the cache key on the channel, to retrieve the
10053 * data *only* from the cache. If it is a normal reload, the
10054 * cache is free to go to the server for updated postdata.
10056 if (cacheChannel && aCacheKey != 0) {
10057 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
10058 cacheChannel->SetCacheKey(aCacheKey);
10059 uint32_t loadFlags;
10060 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
10061 channel->SetLoadFlags(loadFlags |
10062 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
10064 } else if (loadType == LOAD_RELOAD_NORMAL) {
10065 cacheChannel->SetCacheKey(aCacheKey);
10068 } else {
10069 /* If there is no postdata, set the cache key on the channel, and
10070 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
10071 * will be free to get it from net if it is not found in cache.
10072 * New cache may use it creatively on CGI pages with GET
10073 * method and even on those that say "no-cache"
10075 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
10076 loadType == LOAD_RELOAD_CHARSET_CHANGE ||
10077 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
10078 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
10079 if (cacheChannel && aCacheKey != 0) {
10080 cacheChannel->SetCacheKey(aCacheKey);
10085 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
10086 // Allow execution against our context if the principals match
10087 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
10090 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
10091 timedChannel->SetTimingEnabled(true);
10093 nsString initiatorType;
10094 switch (aLoadInfo->InternalContentPolicyType()) {
10095 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
10096 initiatorType = u"embed"_ns;
10097 break;
10098 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
10099 initiatorType = u"object"_ns;
10100 break;
10101 default: {
10102 const auto& embedderElementType =
10103 aBrowsingContext->GetEmbedderElementType();
10104 if (embedderElementType) {
10105 initiatorType = *embedderElementType;
10107 break;
10111 if (!initiatorType.IsEmpty()) {
10112 timedChannel->SetInitiatorType(initiatorType);
10116 nsCOMPtr<nsIURI> rpURI;
10117 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10118 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
10119 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
10120 if (originalResultPrincipalURI &&
10121 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
10122 // Unconditionally override, we want the replay to be equal to what has
10123 // been captured.
10124 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
10127 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
10128 // The LOAD_REPLACE flag and its handling here will be removed as part
10129 // of bug 1319110. For now preserve its restoration here to not break
10130 // any code expecting it being set specially on redirected channels.
10131 // If the flag has originally been set to change result of
10132 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10133 // any harm.
10134 uint32_t loadFlags;
10135 aRv = channel->GetLoadFlags(&loadFlags);
10136 NS_ENSURE_SUCCESS(aRv, false);
10137 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10140 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10141 if (csp) {
10142 // Navigational requests that are same origin need to be upgraded in case
10143 // upgrade-insecure-requests is present. Please note that for document
10144 // navigations that bit is re-computed in case we encounter a server
10145 // side redirect so the navigation is not same-origin anymore.
10146 bool upgradeInsecureRequests = false;
10147 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10148 if (upgradeInsecureRequests) {
10149 // only upgrade if the navigation is same origin
10150 nsCOMPtr<nsIPrincipal> resultPrincipal;
10151 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10152 channel, getter_AddRefs(resultPrincipal));
10153 NS_ENSURE_SUCCESS(aRv, false);
10154 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10155 aLoadState->TriggeringPrincipal(), resultPrincipal)) {
10156 aLoadInfo->SetUpgradeInsecureRequests(true);
10160 // For document loads we store the CSP that potentially needs to
10161 // be inherited by the new document, e.g. in case we are loading
10162 // an opaque origin like a data: URI. The actual inheritance
10163 // check happens within Document::InitCSP().
10164 // Please create an actual copy of the CSP (do not share the same
10165 // reference) otherwise a Meta CSP of an opaque origin will
10166 // incorrectly be propagated to the embedding document.
10167 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
10168 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
10169 aLoadInfo->SetCSPToInherit(cspToInherit);
10172 channel.forget(aChannel);
10173 return true;
10176 bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
10177 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
10178 return NS_IsAboutBlank(aURI) && aInheritPrincipal &&
10179 (aPrincipalToInherit == GetInheritedPrincipal(false)) &&
10180 (!mContentViewer || !mContentViewer->GetDocument() ||
10181 mContentViewer->GetDocument()->IsInitialDocument());
10184 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
10185 Maybe<uint32_t> aCacheKey,
10186 nsIRequest** aRequest) {
10187 // Double-check that we're still around to load this URI.
10188 if (mIsBeingDestroyed) {
10189 // Return NS_OK despite not doing anything to avoid throwing exceptions
10190 // from nsLocation::SetHref if the unload handler of the existing page
10191 // tears us down.
10192 return NS_OK;
10195 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
10196 if (NS_WARN_IF(!uriLoader)) {
10197 return NS_ERROR_UNEXPECTED;
10200 // Persist and sync layout history state before we load a new uri, as this
10201 // might be our last chance to do so, in the content process.
10202 PersistLayoutHistoryState();
10203 SynchronizeLayoutHistoryState();
10205 nsresult rv;
10206 nsContentPolicyType contentPolicyType = DetermineContentType();
10208 if (IsSubframe()) {
10209 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10210 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10211 "DoURILoad thinks this is a frame and InternalLoad does not");
10213 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10214 // Only allow URLs able to return data in iframes.
10215 if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) {
10216 // The context to check user-interaction with for the purposes of
10217 // popup-blocking.
10219 // We generally want to check the context that initiated the navigation.
10220 WindowContext* sourceWindowContext = [&] {
10221 const MaybeDiscardedBrowsingContext& sourceBC =
10222 aLoadState->SourceBrowsingContext();
10223 if (!sourceBC.IsNullOrDiscarded()) {
10224 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
10225 return wc;
10228 return mBrowsingContext->GetParentWindowContext();
10229 }();
10231 MOZ_ASSERT(sourceWindowContext);
10232 // FIXME: We can't check user-interaction against an OOP window. This is
10233 // the next best thing we can really do. The load state keeps whether
10234 // the navigation had a user interaction in process
10235 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10236 // consume it, which we want to prevent popup-spamming from the same
10237 // click event.
10238 WindowContext* context =
10239 sourceWindowContext->IsInProcess()
10240 ? sourceWindowContext
10241 : mBrowsingContext->GetCurrentWindowContext();
10242 const bool popupBlocked = [&] {
10243 const bool active = mBrowsingContext->IsActive();
10245 // For same-origin-with-top windows, we grant a single free popup
10246 // without user activation, see bug 1680721.
10248 // We consume the flag now even if there's no user activation.
10249 const bool hasFreePass = [&] {
10250 if (!active ||
10251 !(context->IsInProcess() && context->SameOriginWithTop())) {
10252 return false;
10254 nsGlobalWindowInner* win =
10255 context->TopWindowContext()->GetInnerWindow();
10256 return win && win->TryOpenExternalProtocolIframe();
10257 }();
10259 if (context->IsInProcess() &&
10260 context->ConsumeTransientUserGestureActivation()) {
10261 // If the user has interacted with the page, consume it.
10262 return false;
10265 // TODO(emilio): Can we remove this check? It seems like what prompted
10266 // this code (bug 1514547) should be covered by transient user
10267 // activation, see bug 1514547.
10268 if (active &&
10269 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10270 return false;
10273 if (sourceWindowContext->CanShowPopup()) {
10274 return false;
10277 if (hasFreePass) {
10278 return false;
10281 return true;
10282 }();
10284 // No error must be returned when iframes are blocked.
10285 if (popupBlocked) {
10286 nsAutoString message;
10287 nsresult rv = nsContentUtils::GetLocalizedString(
10288 nsContentUtils::eDOM_PROPERTIES,
10289 "ExternalProtocolFrameBlockedNoUserActivation", message);
10290 if (NS_SUCCEEDED(rv)) {
10291 nsContentUtils::ReportToConsoleByWindowID(
10292 message, nsIScriptError::warningFlag, "DOM"_ns,
10293 context->InnerWindowId());
10295 return NS_OK;
10300 // Only allow view-source scheme in top-level docshells. view-source is
10301 // the only scheme to which this applies at the moment due to potential
10302 // timing attacks to read data from cross-origin iframes. If this widens
10303 // we should add a protocol flag for whether the scheme is allowed in
10304 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10305 nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
10306 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10307 while (nestedURI) {
10308 // view-source should always be an nsINestedURI, loop and check the
10309 // scheme on this and all inner URIs that are also nested URIs.
10310 if (SchemeIsViewSource(tempURI)) {
10311 return NS_ERROR_UNKNOWN_PROTOCOL;
10313 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10314 nestedURI = do_QueryInterface(tempURI);
10316 } else {
10317 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10318 "DoURILoad thinks this is a document and InternalLoad does not");
10321 // We want to inherit aLoadState->PrincipalToInherit() when:
10322 // 1. ChannelShouldInheritPrincipal returns true.
10323 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10324 // configured as unique opaque origin.
10325 bool inheritPrincipal = false;
10327 if (aLoadState->PrincipalToInherit()) {
10328 bool isSrcdoc =
10329 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
10330 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10331 aLoadState->PrincipalToInherit(), aLoadState->URI(),
10332 true, // aInheritForAboutBlank
10333 isSrcdoc);
10335 inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
10338 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
10339 const bool isAboutBlankLoadOntoInitialAboutBlank =
10340 IsAboutBlankLoadOntoInitialAboutBlank(aLoadState->URI(), inheritPrincipal,
10341 aLoadState->PrincipalToInherit());
10343 // FIXME We still have a ton of codepaths that don't pass through
10344 // DocumentLoadListener, so probably need to create session history info
10345 // in more places.
10346 if (aLoadState->GetLoadingSessionHistoryInfo()) {
10347 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
10348 } else if (isAboutBlankLoadOntoInitialAboutBlank &&
10349 mozilla::SessionHistoryInParent()) {
10350 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
10351 // loads have it, and later history behavior depends on it existing.
10352 UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
10353 aLoadState->URI(), aLoadState->TriggeringPrincipal(),
10354 aLoadState->PrincipalToInherit(),
10355 aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
10356 mContentTypeHint);
10357 mozilla::dom::LoadingSessionHistoryInfo info(*entry);
10358 SetLoadingSessionHistoryInfo(info, true);
10361 // open a channel for the url
10363 // If we have a pending channel, use the channel we've already created here.
10364 // We don't need to set up load flags for our channel, as it has already been
10365 // created.
10367 if (nsCOMPtr<nsIChannel> channel =
10368 aLoadState->GetPendingRedirectedChannel()) {
10369 // If we have a request outparameter, shove our channel into it.
10370 if (aRequest) {
10371 nsCOMPtr<nsIRequest> outRequest = channel;
10372 outRequest.forget(aRequest);
10375 return OpenRedirectedChannel(aLoadState);
10378 // There are two cases we care about:
10379 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10380 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10381 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10382 // element for the load. loadingPrincipal is the NodePrincipal of the
10383 // frame element.
10384 nsCOMPtr<nsINode> loadingNode;
10385 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10386 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10387 nsCOMPtr<nsISupports> topLevelLoadingContext;
10389 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10390 loadingNode = nullptr;
10391 loadingPrincipal = nullptr;
10392 loadingWindow = mScriptGlobal;
10393 if (XRE_IsContentProcess()) {
10394 // In e10s the child process doesn't have access to the element that
10395 // contains the browsing context (because that element is in the chrome
10396 // process).
10397 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
10398 topLevelLoadingContext = ToSupports(browserChild);
10399 } else {
10400 // This is for loading non-e10s tabs and toplevel windows of various
10401 // sorts.
10402 // For the toplevel window cases, requestingElement will be null.
10403 nsCOMPtr<Element> requestingElement =
10404 loadingWindow->GetFrameElementInternal();
10405 topLevelLoadingContext = requestingElement;
10407 } else {
10408 loadingWindow = nullptr;
10409 loadingNode = mScriptGlobal->GetFrameElementInternal();
10410 if (loadingNode) {
10411 // If we have a loading node, then use that as our loadingPrincipal.
10412 loadingPrincipal = loadingNode->NodePrincipal();
10413 #ifdef DEBUG
10414 // Get the docshell type for requestingElement.
10415 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
10416 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10417 // requestingElement docshell type = current docshell type.
10418 MOZ_ASSERT(
10419 mItemType == elementDocShell->ItemType(),
10420 "subframes should have the same docshell type as their parent");
10421 #endif
10422 } else {
10423 if (mIsBeingDestroyed) {
10424 // If this isn't a top-level load and mScriptGlobal's frame element is
10425 // null, then the element got removed from the DOM while we were trying
10426 // to load this resource. This docshell is scheduled for destruction
10427 // already, so bail out here.
10428 return NS_OK;
10430 // If we are not being destroyed and we do not have access to the loading
10431 // node, then we are a remote subframe. Set the loading principal
10432 // to be a null principal and then set it correctly in the parent.
10433 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
10437 if (!aLoadState->TriggeringPrincipal()) {
10438 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10439 return NS_ERROR_FAILURE;
10442 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
10443 nsSecurityFlags securityFlags =
10444 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
10446 if (mLoadType == LOAD_ERROR_PAGE) {
10447 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10450 if (inheritPrincipal) {
10451 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10454 // Must never have a parent for TYPE_DOCUMENT loads
10455 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10456 !mBrowsingContext->GetParent());
10457 // Subdocuments must have a parent
10458 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
10459 mBrowsingContext->GetParent());
10460 mBrowsingContext->SetTriggeringAndInheritPrincipals(
10461 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
10462 aLoadState->GetLoadIdentifier());
10463 RefPtr<LoadInfo> loadInfo =
10464 (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10465 ? new LoadInfo(loadingWindow, aLoadState->URI(),
10466 aLoadState->TriggeringPrincipal(),
10467 topLevelLoadingContext, securityFlags, sandboxFlags)
10468 : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
10469 loadingNode, securityFlags, contentPolicyType,
10470 Maybe<mozilla::dom::ClientInfo>(),
10471 Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
10472 sandboxFlags);
10473 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
10475 if (isAboutBlankLoadOntoInitialAboutBlank) {
10476 // Match the DocumentChannel case where the default for third-partiness
10477 // differs from the default in LoadInfo construction here.
10478 // toolkit/components/antitracking/test/browser/browser_aboutblank.js
10479 // fails without this.
10480 BrowsingContext* top = mBrowsingContext->Top();
10481 if (top == mBrowsingContext) {
10482 // If we're at the top, this must be a window.open()ed
10483 // window, and we can't be third-party relative to ourselves.
10484 loadInfo->SetIsThirdPartyContextToTopWindow(false);
10485 } else {
10486 if (Document* topDoc = top->GetDocument()) {
10487 bool thirdParty = false;
10488 mozilla::Unused << topDoc->GetPrincipal()->IsThirdPartyPrincipal(
10489 aLoadState->PrincipalToInherit(), &thirdParty);
10490 loadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
10491 } else {
10492 // If top is in a different process, we have to be third-party relative
10493 // to it.
10494 loadInfo->SetIsThirdPartyContextToTopWindow(true);
10499 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess() &&
10500 context->HasValidTransientUserGestureActivation()) {
10501 aLoadState->SetHasValidUserGestureActivation(true);
10504 // in case this docshell load was triggered by a valid transient user gesture,
10505 // or also the load originates from external, then we pass that information on
10506 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10507 if (aLoadState->HasValidUserGestureActivation() ||
10508 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
10509 loadInfo->SetHasValidUserGestureActivation(true);
10511 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
10512 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
10514 uint32_t cacheKey = 0;
10515 if (aCacheKey) {
10516 cacheKey = *aCacheKey;
10517 } else if (mozilla::SessionHistoryInParent()) {
10518 if (mLoadingEntry) {
10519 cacheKey = mLoadingEntry->mInfo.GetCacheKey();
10520 } else if (mActiveEntry) { // for reload cases
10521 cacheKey = mActiveEntry->GetCacheKey();
10523 } else {
10524 if (mLSHE) {
10525 cacheKey = mLSHE->GetCacheKey();
10526 } else if (mOSHE) { // for reload cases
10527 cacheKey = mOSHE->GetCacheKey();
10531 bool uriModified;
10532 if (mLSHE || mLoadingEntry) {
10533 if (mLoadingEntry) {
10534 uriModified = mLoadingEntry->mInfo.GetURIWasModified();
10535 } else {
10536 uriModified = mLSHE->GetURIWasModified();
10538 } else {
10539 uriModified = false;
10542 bool isXFOError = false;
10543 if (mFailedChannel) {
10544 nsresult status;
10545 mFailedChannel->GetStatus(&status);
10546 isXFOError = status == NS_ERROR_XFO_VIOLATION;
10549 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
10550 mBrowsingContext, Some(uriModified), Some(isXFOError));
10552 nsCOMPtr<nsIChannel> channel;
10553 if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
10554 !isAboutBlankLoadOntoInitialAboutBlank) {
10555 channel = DocumentChannel::CreateForDocument(aLoadState, loadInfo,
10556 loadFlags, this, cacheKey,
10557 uriModified, isXFOError);
10558 MOZ_ASSERT(channel);
10560 // Disable keyword fixup when using DocumentChannel, since
10561 // DocumentLoadListener will handle this for us (in the parent process).
10562 mAllowKeywordFixup = false;
10563 } else if (!CreateAndConfigureRealChannelForLoadState(
10564 mBrowsingContext, aLoadState, loadInfo, this, this,
10565 GetOriginAttributes(), loadFlags, cacheKey, rv,
10566 getter_AddRefs(channel))) {
10567 return rv;
10570 // Make sure to give the caller a channel if we managed to create one
10571 // This is important for correct error page/session history interaction
10572 if (aRequest) {
10573 NS_ADDREF(*aRequest = channel);
10576 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10577 if (csp) {
10578 // Check CSP navigate-to
10579 bool allowsNavigateTo = false;
10580 rv = csp->GetAllowsNavigateTo(aLoadState->URI(),
10581 aLoadState->IsFormSubmission(),
10582 false, /* aWasRedirected */
10583 false, /* aEnforceWhitelist */
10584 &allowsNavigateTo);
10585 NS_ENSURE_SUCCESS(rv, rv);
10587 if (!allowsNavigateTo) {
10588 return NS_ERROR_CSP_NAVIGATE_TO_VIOLATION;
10592 const nsACString& typeHint = aLoadState->TypeHint();
10593 if (!typeHint.IsVoid()) {
10594 mContentTypeHint = typeHint;
10595 } else {
10596 mContentTypeHint.Truncate();
10599 // Load attributes depend on load type...
10600 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10601 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10602 // only want to force cache load for this channel, not the whole
10603 // loadGroup.
10604 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10605 if (cachingChannel) {
10606 cachingChannel->SetAllowStaleCacheContent(true);
10610 uint32_t openFlags =
10611 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
10612 return OpenInitializedChannel(channel, uriLoader, openFlags);
10615 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10616 const char* aFromRawSegment,
10617 uint32_t aToOffset, uint32_t aCount,
10618 uint32_t* aWriteCount) {
10619 // aFromSegment now contains aCount bytes of data.
10621 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10622 buf->Append(aFromRawSegment, aCount);
10624 // Indicate that we have consumed all of aFromSegment
10625 *aWriteCount = aCount;
10626 return NS_OK;
10629 /* static */ nsresult nsDocShell::AddHeadersToChannel(
10630 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
10631 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10632 NS_ENSURE_STATE(httpChannel);
10634 uint32_t numRead;
10635 nsAutoCString headersString;
10636 nsresult rv = aHeadersData->ReadSegments(
10637 AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10638 NS_ENSURE_SUCCESS(rv, rv);
10640 // used during the manipulation of the String from the InputStream
10641 nsAutoCString headerName;
10642 nsAutoCString headerValue;
10643 int32_t crlf;
10644 int32_t colon;
10647 // Iterate over the headersString: for each "\r\n" delimited chunk,
10648 // add the value as a header to the nsIHttpChannel
10651 static const char kWhitespace[] = "\b\t\r\n ";
10652 while (true) {
10653 crlf = headersString.Find("\r\n");
10654 if (crlf == kNotFound) {
10655 return NS_OK;
10658 const nsACString& oneHeader = StringHead(headersString, crlf);
10660 colon = oneHeader.FindChar(':');
10661 if (colon == kNotFound) {
10662 return NS_ERROR_UNEXPECTED;
10665 headerName = StringHead(oneHeader, colon);
10666 headerValue = Substring(oneHeader, colon + 1);
10668 headerName.Trim(kWhitespace);
10669 headerValue.Trim(kWhitespace);
10671 headersString.Cut(0, crlf + 2);
10674 // FINALLY: we can set the header!
10677 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10678 NS_ENSURE_SUCCESS(rv, rv);
10681 MOZ_ASSERT_UNREACHABLE("oops");
10682 return NS_ERROR_UNEXPECTED;
10685 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10686 BrowsingContext* aBrowsingContext, uint32_t aLoadType) {
10687 MOZ_ASSERT(aBrowsingContext);
10689 uint32_t openFlags = 0;
10690 if (aLoadType == LOAD_LINK) {
10691 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10693 if (!aBrowsingContext->GetAllowContentRetargeting()) {
10694 openFlags |= nsIURILoader::DONT_RETARGET;
10697 return openFlags;
10700 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
10701 nsIURILoader* aURILoader,
10702 uint32_t aOpenFlags) {
10703 nsresult rv = NS_OK;
10705 // If anything fails here, make sure to clear our initial ClientSource.
10706 auto cleanupInitialClient =
10707 MakeScopeExit([&] { mInitialClientSource.reset(); });
10709 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10710 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10712 MaybeCreateInitialClientSource();
10714 // Let the client channel helper know if we are using DocumentChannel,
10715 // since redirects get handled in the parent process in that case.
10716 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
10717 if (docChannel && XRE_IsContentProcess()) {
10718 // Tell the content process nsDocumentOpenInfo to not try to do
10719 // any sort of targeting.
10720 aOpenFlags |= nsIURILoader::DONT_RETARGET;
10723 // Since we are loading a document we need to make sure the proper reserved
10724 // and initial client data is stored on the nsILoadInfo. The
10725 // ClientChannelHelper does this and ensures that it is propagated properly
10726 // on redirects. We pass no reserved client here so that the helper will
10727 // create the reserved ClientSource if necessary.
10728 Maybe<ClientInfo> noReservedClient;
10729 if (docChannel) {
10730 // When using DocumentChannel, all redirect handling is done in the parent,
10731 // so we just need the child variant to watch for the internal redirect
10732 // to the final channel.
10733 rv = AddClientChannelHelperInChild(
10734 aChannel, win->EventTargetFor(TaskCategory::Other));
10735 docChannel->SetInitialClientInfo(GetInitialClientInfo());
10736 } else {
10737 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
10738 GetInitialClientInfo(),
10739 win->EventTargetFor(TaskCategory::Other));
10741 NS_ENSURE_SUCCESS(rv, rv);
10743 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
10744 NS_ENSURE_SUCCESS(rv, rv);
10746 // We're about to load a new page and it may take time before necko
10747 // gives back any data, so main thread might have a chance to process a
10748 // collector slice
10749 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
10751 // Success. Keep the initial ClientSource if it exists.
10752 cleanupInitialClient.release();
10754 return NS_OK;
10757 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
10758 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
10759 MOZ_ASSERT(channel);
10761 // If anything fails here, make sure to clear our initial ClientSource.
10762 auto cleanupInitialClient =
10763 MakeScopeExit([&] { mInitialClientSource.reset(); });
10765 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10766 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10768 MaybeCreateInitialClientSource();
10770 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
10772 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
10773 if (loadInfo->GetExternalContentPolicyType() ==
10774 ExtContentPolicy::TYPE_DOCUMENT) {
10775 li->UpdateBrowsingContextID(mBrowsingContext->Id());
10776 } else if (loadInfo->GetExternalContentPolicyType() ==
10777 ExtContentPolicy::TYPE_SUBDOCUMENT) {
10778 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
10780 // TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
10782 // If we did a process switch, then we should have an existing allocated
10783 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10784 CreateReservedSourceIfNeeded(channel,
10785 win->EventTargetFor(TaskCategory::Other));
10787 RefPtr<nsDocumentOpenInfo> loader =
10788 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10789 channel->SetLoadGroup(mLoadGroup);
10791 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10793 nsresult rv = NS_OK;
10794 if (XRE_IsParentProcess()) {
10795 // If we're in the parent, the we don't have an nsIChildChannel, just
10796 // the original channel, which is already open in this process.
10798 // DocumentLoadListener expects to get an nsIParentChannel, so
10799 // we create a wrapper around the channel and nsIStreamListener
10800 // that forwards functionality as needed, and then we register
10801 // it under the provided identifier.
10802 RefPtr<ParentChannelWrapper> wrapper =
10803 new ParentChannelWrapper(channel, loader);
10804 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10806 mLoadGroup->AddRequest(channel, nullptr);
10807 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10808 do_QueryInterface(channel)) {
10809 // Our channel was redirected from another process, so doesn't need to
10810 // be opened again. However, it does need its listener hooked up
10811 // correctly.
10812 rv = childChannel->CompleteRedirectSetup(loader);
10813 } else {
10814 // It's possible for the redirected channel to not implement
10815 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10816 // can just open the local instance and it will work.
10817 rv = channel->AsyncOpen(loader);
10819 if (rv == NS_ERROR_NO_CONTENT) {
10820 return NS_OK;
10822 NS_ENSURE_SUCCESS(rv, rv);
10824 // Success. Keep the initial ClientSource if it exists.
10825 cleanupInitialClient.release();
10826 return NS_OK;
10829 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10830 nsACString& aNewHash, uint32_t aLoadType) {
10831 if (!mCurrentURI) {
10832 return NS_OK;
10835 RefPtr<PresShell> presShell = GetPresShell();
10836 if (!presShell) {
10837 // If we failed to get the shell, or if there is no shell,
10838 // nothing left to do here.
10839 return NS_OK;
10842 nsIScrollableFrame* rootScroll = presShell->GetRootScrollFrameAsScrollable();
10843 if (rootScroll) {
10844 rootScroll->ClearDidHistoryRestore();
10847 // If we have no new anchor, we do not want to scroll, unless there is a
10848 // current anchor and we are doing a history load. So return if we have no
10849 // new anchor, and there is no current anchor or the load is not a history
10850 // load.
10851 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef) {
10852 return NS_OK;
10855 // Both the new and current URIs refer to the same page. We can now
10856 // browse to the hash stored in the new URI.
10858 if (!aNewHash.IsEmpty()) {
10859 // anchor is there, but if it's a load from history,
10860 // we don't have any anchor jumping to do
10861 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10863 // We assume that the bytes are in UTF-8, as it says in the
10864 // spec:
10865 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
10867 // We try the UTF-8 string first, and then try the document's
10868 // charset (see below). If the string is not UTF-8,
10869 // conversion will fail and give us an empty Unicode string.
10870 // In that case, we should just fall through to using the
10871 // page's charset.
10872 nsresult rv = NS_ERROR_FAILURE;
10873 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10874 if (!uStr.IsEmpty()) {
10875 rv = presShell->GoToAnchor(uStr, scroll, ScrollFlags::ScrollSmoothAuto);
10878 if (NS_FAILED(rv)) {
10879 char* str = ToNewCString(aNewHash, mozilla::fallible);
10880 if (!str) {
10881 return NS_ERROR_OUT_OF_MEMORY;
10883 nsUnescape(str);
10884 NS_ConvertUTF8toUTF16 utf16Str(str);
10885 if (!utf16Str.IsEmpty()) {
10886 rv = presShell->GoToAnchor(utf16Str, scroll,
10887 ScrollFlags::ScrollSmoothAuto);
10889 free(str);
10892 // Above will fail if the anchor name is not UTF-8. Need to
10893 // convert from document charset to unicode.
10894 if (NS_FAILED(rv)) {
10895 // Get a document charset
10896 NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);
10897 Document* doc = mContentViewer->GetDocument();
10898 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
10899 nsAutoCString charset;
10900 doc->GetDocumentCharacterSet()->Name(charset);
10902 nsCOMPtr<nsITextToSubURI> textToSubURI =
10903 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
10904 NS_ENSURE_SUCCESS(rv, rv);
10906 // Unescape and convert to unicode
10907 nsAutoString uStr;
10909 rv = textToSubURI->UnEscapeAndConvert(charset, aNewHash, uStr);
10910 NS_ENSURE_SUCCESS(rv, rv);
10912 // Ignore return value of GoToAnchor, since it will return an error
10913 // if there is no such anchor in the document, which is actually a
10914 // success condition for us (we want to update the session history
10915 // with the new URI no matter whether we actually scrolled
10916 // somewhere).
10918 // When aNewHash contains "%00", unescaped string may be empty.
10919 // And GoToAnchor asserts if we ask it to scroll to an empty ref.
10920 presShell->GoToAnchor(uStr, scroll && !uStr.IsEmpty(),
10921 ScrollFlags::ScrollSmoothAuto);
10923 } else {
10924 // Tell the shell it's at an anchor, without scrolling.
10925 presShell->GoToAnchor(u""_ns, false);
10927 // An empty anchor was found, but if it's a load from history,
10928 // we don't have to jump to the top of the page. Scrollbar
10929 // position will be restored by the caller, based on positions
10930 // stored in session history.
10931 if (aLoadType == LOAD_HISTORY || aLoadType == LOAD_RELOAD_NORMAL) {
10932 return NS_OK;
10934 // An empty anchor. Scroll to the top of the page. Ignore the
10935 // return value; failure to scroll here (e.g. if there is no
10936 // root scrollframe) is not grounds for canceling the load!
10937 SetCurScrollPosEx(0, 0);
10940 return NS_OK;
10943 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10944 nsIPrincipal* aTriggeringPrincipal,
10945 nsIPrincipal* aPrincipalToInherit,
10946 nsIPrincipal* aPartitionedPrincipalToInherit,
10947 nsIContentSecurityPolicy* aCsp,
10948 bool aFireOnLocationChange, bool aAddToGlobalHistory,
10949 bool aCloneSHChildren) {
10950 MOZ_ASSERT(aURI, "uri is null");
10951 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10953 MOZ_ASSERT(!aPrincipalToInherit ||
10954 (aPrincipalToInherit && aTriggeringPrincipal));
10956 #if defined(DEBUG)
10957 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10958 nsAutoCString chanName;
10959 if (aChannel) {
10960 aChannel->GetName(chanName);
10961 } else {
10962 chanName.AssignLiteral("<no channel>");
10965 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10966 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10967 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10969 #endif
10971 bool equalUri = false;
10973 // Get the post data and the HTTP response code from the channel.
10974 uint32_t responseStatus = 0;
10975 nsCOMPtr<nsIInputStream> inputStream;
10976 if (aChannel) {
10977 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10979 // Check if the HTTPChannel is hiding under a multiPartChannel
10980 if (!httpChannel) {
10981 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10984 if (httpChannel) {
10985 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10986 if (uploadChannel) {
10987 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10990 // If the response status indicates an error, unlink this session
10991 // history entry from any entries sharing its document.
10992 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10993 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10994 mLSHE->AbandonBFCacheEntry();
10995 // FIXME Do the same for mLoadingEntry
11000 // Determine if this type of load should update history.
11001 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
11003 // We don't update session history on reload unless we're loading
11004 // an iframe in shift-reload case.
11005 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
11007 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
11008 // root browsing context.
11009 // FIXME If session history in the parent is enabled then we only do this if
11010 // the session history object is in process, otherwise we can't really
11011 // use the mLSHE anyway. Once session history is only stored in the
11012 // parent then this code will probably be removed anyway.
11013 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11014 if (!rootSH) {
11015 updateSHistory = false;
11016 updateGHistory = false; // XXX Why global history too?
11019 // Check if the url to be loaded is the same as the one already loaded.
11020 if (mCurrentURI) {
11021 aURI->Equals(mCurrentURI, &equalUri);
11024 #ifdef DEBUG
11025 bool shAvailable = (rootSH != nullptr);
11027 // XXX This log message is almost useless because |updateSHistory|
11028 // and |updateGHistory| are not correct at this point.
11030 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11031 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
11032 " equalURI=%i\n",
11033 shAvailable, updateSHistory, updateGHistory, equalUri));
11034 #endif
11036 /* If the url to be loaded is the same as the one already there,
11037 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
11038 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
11039 * AddToSessionHistory() won't mess with the current SHEntry and
11040 * if this page has any frame children, it also will be handled
11041 * properly. see bug 83684
11043 * NB: If mOSHE is null but we have a current URI, then it probably
11044 * means that we must be at the transient about:blank content viewer;
11045 * we should let the normal load continue, since there's nothing to
11046 * replace. Sometimes this happens after a session restore (eg process
11047 * switch) and mCurrentURI is not about:blank; we assume we can let the load
11048 * continue (Bug 1301399).
11050 * XXX Hopefully changing the loadType at this time will not hurt
11051 * anywhere. The other way to take care of sequentially repeating
11052 * frameset pages is to add new methods to nsIDocShellTreeItem.
11053 * Hopefully I don't have to do that.
11055 if (equalUri &&
11056 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
11057 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
11058 mLoadType == LOAD_STOP_CONTENT) &&
11059 !inputStream) {
11060 mLoadType = LOAD_NORMAL_REPLACE;
11063 // If this is a refresh to the currently loaded url, we don't
11064 // have to update session or global history.
11065 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
11066 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
11069 /* If the user pressed shift-reload, cache will create a new cache key
11070 * for the page. Save the new cacheKey in Session History.
11071 * see bug 90098
11073 if (aChannel && IsForceReloadType(mLoadType)) {
11074 MOZ_ASSERT(!updateSHistory || IsSubframe(),
11075 "We shouldn't be updating session history for forced"
11076 " reloads unless we're in a newly created iframe!");
11078 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
11079 uint32_t cacheKey = 0;
11080 // Get the Cache Key and store it in SH.
11081 if (cacheChannel) {
11082 cacheChannel->GetCacheKey(&cacheKey);
11084 // If we already have a loading history entry, store the new cache key
11085 // in it. Otherwise, since we're doing a reload and won't be updating
11086 // our history entry, store the cache key in our current history entry.
11087 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
11089 if (!mozilla::SessionHistoryInParent()) {
11090 // Since we're force-reloading, clear all the sub frame history.
11091 ClearFrameHistory(mLSHE);
11092 ClearFrameHistory(mOSHE);
11096 if (!mozilla::SessionHistoryInParent()) {
11097 // Clear subframe history on refresh.
11098 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
11099 // this case. One should re-validate after bug 1331865 fixed.
11100 if (mLoadType == LOAD_REFRESH) {
11101 ClearFrameHistory(mLSHE);
11102 ClearFrameHistory(mOSHE);
11105 if (updateSHistory) {
11106 // Update session history if necessary...
11107 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11108 /* This is a fresh page getting loaded for the first time
11109 *.Create a Entry for it and add it to SH, if this is the
11110 * rootDocShell
11112 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11113 aPrincipalToInherit,
11114 aPartitionedPrincipalToInherit, aCsp,
11115 aCloneSHChildren, getter_AddRefs(mLSHE));
11117 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11118 // Even if we don't add anything to SHistory, ensure the current index
11119 // points to the same SHEntry as our mLSHE.
11121 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11122 mLSHE);
11126 // If this is a POST request, we do not want to include this in global
11127 // history.
11128 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11129 !net::ChannelIsPost(aChannel)) {
11130 nsCOMPtr<nsIURI> previousURI;
11131 uint32_t previousFlags = 0;
11133 if (mLoadType & LOAD_CMD_RELOAD) {
11134 // On a reload request, we don't set redirecting flags.
11135 previousURI = aURI;
11136 } else {
11137 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11140 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11143 // If this was a history load or a refresh, or it was a history load but
11144 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11145 // in session history.
11146 if (!mozilla::SessionHistoryInParent() && rootSH &&
11147 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11148 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11149 mPreviousEntryIndex = rootSH->Index();
11150 if (!mozilla::SessionHistoryInParent()) {
11151 rootSH->LegacySHistory()->UpdateIndex();
11153 mLoadedEntryIndex = rootSH->Index();
11154 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11155 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11156 mLoadedEntryIndex));
11159 // aCloneSHChildren exactly means "we are not loading a new document".
11160 uint32_t locationFlags =
11161 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11163 bool onLocationChangeNeeded =
11164 SetCurrentURI(aURI, aChannel, aFireOnLocationChange,
11165 /* aIsInitialAboutBlank */ false, locationFlags);
11166 // Make sure to store the referrer from the channel, if any
11167 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11168 if (httpChannel) {
11169 mReferrerInfo = httpChannel->GetReferrerInfo();
11171 return onLocationChangeNeeded;
11174 Maybe<Wireframe> nsDocShell::GetWireframe() {
11175 const bool collectWireFrame =
11176 mozilla::SessionHistoryInParent() &&
11177 StaticPrefs::browser_history_collectWireframes() &&
11178 mBrowsingContext->IsTopContent() && mActiveEntry;
11180 if (!collectWireFrame) {
11181 return Nothing();
11184 RefPtr<Document> doc = mContentViewer->GetDocument();
11185 Nullable<Wireframe> wireframe;
11186 doc->GetWireframeWithoutFlushing(false, wireframe);
11187 if (wireframe.IsNull()) {
11188 return Nothing();
11190 return Some(wireframe.Value());
11193 bool nsDocShell::CollectWireframe() {
11194 Maybe<Wireframe> wireframe = GetWireframe();
11195 if (wireframe.isNothing()) {
11196 return false;
11199 if (XRE_IsParentProcess()) {
11200 SessionHistoryEntry* entry =
11201 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11202 if (entry) {
11203 entry->SetWireframe(wireframe);
11205 } else {
11206 mozilla::Unused
11207 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11208 mBrowsingContext, wireframe.ref());
11211 return true;
11214 //*****************************************************************************
11215 // nsDocShell: Session History
11216 //*****************************************************************************
11218 NS_IMETHODIMP
11219 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11220 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11221 MOZ_LOG(gSHLog, LogLevel::Debug,
11222 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11223 NS_ConvertUTF16toUTF8(aTitle).get(),
11224 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11225 // Implements History.pushState and History.replaceState
11227 // Here's what we do, roughly in the order specified by HTML5. The specific
11228 // steps we are executing are at
11229 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11230 // and
11231 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11232 // This function basically implements #dom-history-pushstate and
11233 // UpdateURLAndHistory implements #url-and-history-update-steps.
11235 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11236 // step 5.
11237 // B. If the third argument is present, #dom-history-pushstate step 7.
11238 // 7.1. Resolve the url, relative to our document.
11239 // 7.2. If (a) fails, raise a SECURITY_ERR
11240 // 7.4. Compare the resulting absolute URL to the document's address. If
11241 // any part of the URLs difer other than the <path>, <query>, and
11242 // <fragment> components, raise a SECURITY_ERR and abort.
11243 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11244 // Remove from the session history all entries after the current entry,
11245 // as we would after a regular navigation, and save the current
11246 // entry's scroll position (bug 590573).
11247 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11248 // either add a state object entry to the session history after the
11249 // current entry with the following properties, or modify the current
11250 // session history entry to set
11251 // a. cloned data as the state object,
11252 // b. if the third argument was present, the absolute URL found in
11253 // step 2
11254 // Also clear the new history entry's POST data (see bug 580069).
11255 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11256 // replaceState), notify bfcache that we've navigated to a new page.
11257 // F. If the third argument is present, set the document's current address
11258 // to the absolute URL found in step B. This is
11259 // #url-and-history-update-steps step 4.
11261 // It's important that this function not run arbitrary scripts after step A
11262 // and before completing step E. For example, if a script called
11263 // history.back() before we completed step E, bfcache might destroy an
11264 // active content viewer. Since EvictOutOfRangeContentViewers at the end of
11265 // step E might run script, we can't just put a script blocker around the
11266 // critical section.
11268 // Note that we completely ignore the aTitle parameter.
11270 nsresult rv;
11272 // Don't clobber the load type of an existing network load.
11273 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11275 // pushState effectively becomes replaceState when we've started a network
11276 // load but haven't adopted its document yet. This mirrors what we do with
11277 // changes to the hash at this stage of the game.
11278 if (JustStartedNetworkLoad()) {
11279 aReplace = true;
11282 RefPtr<Document> document = GetDocument();
11283 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11285 // Step A: Serialize aData using structured clone.
11286 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11287 // step 5.
11288 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11290 // scContainer->Init might cause arbitrary JS to run, and this code might
11291 // navigate the page we're on, potentially to a different origin! (bug
11292 // 634834) To protect against this, we abort if our principal changes due
11293 // to the InitFromJSVal() call.
11295 RefPtr<Document> origDocument = GetDocument();
11296 if (!origDocument) {
11297 return NS_ERROR_DOM_SECURITY_ERR;
11299 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11301 scContainer = new nsStructuredCloneContainer();
11302 rv = scContainer->InitFromJSVal(aData, aCx);
11303 NS_ENSURE_SUCCESS(rv, rv);
11305 RefPtr<Document> newDocument = GetDocument();
11306 if (!newDocument) {
11307 return NS_ERROR_DOM_SECURITY_ERR;
11309 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11311 bool principalsEqual = false;
11312 origPrincipal->Equals(newPrincipal, &principalsEqual);
11313 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11316 // Check that the state object isn't too long.
11317 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11318 if (maxStateObjSize < 0) {
11319 maxStateObjSize = 0;
11322 uint64_t scSize;
11323 rv = scContainer->GetSerializedNBytes(&scSize);
11324 NS_ENSURE_SUCCESS(rv, rv);
11326 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11328 // Step B: Resolve aURL.
11329 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11330 // step 7.
11331 bool equalURIs = true;
11332 nsCOMPtr<nsIURI> currentURI;
11333 if (mCurrentURI) {
11334 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11335 } else {
11336 currentURI = mCurrentURI;
11338 nsCOMPtr<nsIURI> newURI;
11339 if (aURL.Length() == 0) {
11340 newURI = currentURI;
11341 } else {
11342 // 7.1: Resolve aURL relative to mURI
11344 nsIURI* docBaseURI = document->GetDocBaseURI();
11345 if (!docBaseURI) {
11346 return NS_ERROR_FAILURE;
11349 nsAutoCString spec;
11350 docBaseURI->GetSpec(spec);
11352 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11353 document->GetDocumentCharacterSet(), docBaseURI);
11355 // 7.2: If 2a fails, raise a SECURITY_ERR
11356 if (NS_FAILED(rv)) {
11357 return NS_ERROR_DOM_SECURITY_ERR;
11360 // 7.4 and 7.5: Same-origin check.
11361 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11362 // In addition to checking that the security manager says that
11363 // the new URI has the same origin as our current URI, we also
11364 // check that the two URIs have the same userpass. (The
11365 // security manager says that |http://foo.com| and
11366 // |http://me@foo.com| have the same origin.) currentURI
11367 // won't contain the password part of the userpass, so this
11368 // means that it's never valid to specify a password in a
11369 // pushState or replaceState URI.
11371 nsCOMPtr<nsIScriptSecurityManager> secMan =
11372 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11373 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11375 // It's very important that we check that newURI is of the same
11376 // origin as currentURI, not docBaseURI, because a page can
11377 // set docBaseURI arbitrarily to any domain.
11378 nsAutoCString currentUserPass, newUserPass;
11379 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11380 NS_ERROR_FAILURE);
11381 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11382 bool isPrivateWin =
11383 document->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId >
11385 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11386 isPrivateWin)) ||
11387 !currentUserPass.Equals(newUserPass)) {
11388 return NS_ERROR_DOM_SECURITY_ERR;
11390 } else {
11391 // It's a file:// URI
11392 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11394 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11395 newURI, false, document->InnerWindowID()))) {
11396 return NS_ERROR_DOM_SECURITY_ERR;
11400 if (currentURI) {
11401 currentURI->Equals(newURI, &equalURIs);
11402 } else {
11403 equalURIs = false;
11406 } // end of same-origin check
11408 // Step 8: call "URL and history update steps"
11409 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11410 currentURI, equalURIs);
11411 NS_ENSURE_SUCCESS(rv, rv);
11413 return NS_OK;
11416 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11417 nsIStructuredCloneContainer* aData,
11418 const nsAString& aTitle, bool aReplace,
11419 nsIURI* aCurrentURI, bool aEqualURIs) {
11420 // Implements
11421 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11423 // If we have a pending title change, handle it before creating a new entry.
11424 aDocument->DoNotifyPossibleTitleChange();
11426 // Step 2, if aReplace is false: Create a new entry in the session
11427 // history. This will erase all SHEntries after the new entry and make this
11428 // entry the current one. This operation may modify mOSHE, which we need
11429 // later, so we keep a reference here.
11430 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11431 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11433 // If this push/replaceState changed the document's current URI and the new
11434 // URI differs from the old URI in more than the hash, or if the old
11435 // SHEntry's URI was modified in this way by a push/replaceState call
11436 // set URIWasModified to true for the current SHEntry (bug 669671).
11437 bool sameExceptHashes = true;
11438 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11439 bool uriWasModified;
11440 if (sameExceptHashes) {
11441 if (mozilla::SessionHistoryInParent()) {
11442 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11443 } else {
11444 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11446 } else {
11447 uriWasModified = true;
11450 mLoadType = LOAD_PUSHSTATE;
11452 nsCOMPtr<nsISHEntry> newSHEntry;
11453 if (!aReplace) {
11454 // Step 2.
11456 // Step 2.2, "Remove any tasks queued by the history traversal task
11457 // source that are associated with any Document objects in the
11458 // top-level browsing context's document family." This is very hard in
11459 // SessionHistoryInParent since we can't synchronously access the
11460 // pending navigations that are already sent to the parent. We can
11461 // abort any AsyncGo navigations that are waiting to be sent. If we
11462 // send a message to the parent, it would be processed after any
11463 // navigations previously sent. So long as we consider the "history
11464 // traversal task source" to be the list in this process we match the
11465 // spec. If we move the entire list to the parent, we can handle the
11466 // aborting of loads there, but we don't have a way to synchronously
11467 // remove entries as we do here for non-SHIP.
11468 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11469 if (shistory) {
11470 shistory->RemovePendingHistoryNavigations();
11473 nsPoint scrollPos = GetCurScrollPos();
11475 bool scrollRestorationIsManual;
11476 if (mozilla::SessionHistoryInParent()) {
11477 // FIXME Need to save the current scroll position on mActiveEntry.
11478 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11479 } else {
11480 // Save the current scroll position (bug 590573). Step 2.3.
11481 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11483 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11486 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11488 if (mozilla::SessionHistoryInParent()) {
11489 MOZ_LOG(gSHLog, LogLevel::Debug,
11490 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11491 nsString title(mActiveEntry->GetTitle());
11492 UpdateActiveEntry(false,
11493 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11494 /* aOriginalURI = */ nullptr,
11495 /* aReferrerInfo = */ nullptr,
11496 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11497 csp, title, scrollRestorationIsManual, aData,
11498 uriWasModified);
11499 } else {
11500 // Since we're not changing which page we have loaded, pass
11501 // true for aCloneChildren.
11502 nsresult rv = AddToSessionHistory(
11503 aNewURI, nullptr,
11504 aDocument->NodePrincipal(), // triggeringPrincipal
11505 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11506 NS_ENSURE_SUCCESS(rv, rv);
11508 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11510 // Session history entries created by pushState inherit scroll restoration
11511 // mode from the current entry.
11512 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11514 nsString title;
11515 mOSHE->GetTitle(title);
11517 // Set the new SHEntry's title (bug 655273).
11518 newSHEntry->SetTitle(title);
11520 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11521 // two entries correspond to the same document.
11522 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11523 NS_ERROR_FAILURE);
11525 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11526 // we'll just set mOSHE here.
11527 mOSHE = newSHEntry;
11529 } else if (mozilla::SessionHistoryInParent()) {
11530 MOZ_LOG(gSHLog, LogLevel::Debug,
11531 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11532 this, mActiveEntry.get()));
11533 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11534 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11535 // in our case. We could also set it to aNewURI, with the same result.
11536 // We don't use aTitle here, see bug 544535.
11537 nsString title;
11538 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11539 if (mActiveEntry) {
11540 title = mActiveEntry->GetTitle();
11541 referrerInfo = mActiveEntry->GetReferrerInfo();
11542 } else {
11543 referrerInfo = nullptr;
11545 UpdateActiveEntry(
11546 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11547 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
11548 aDocument->GetCsp(), title,
11549 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11550 uriWasModified);
11551 } else {
11552 // Step 3.
11553 newSHEntry = mOSHE;
11555 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11556 // Since we're not changing which page we have loaded, pass
11557 // true for aCloneChildren.
11558 if (!newSHEntry) {
11559 nsresult rv = AddToSessionHistory(
11560 aNewURI, nullptr,
11561 aDocument->NodePrincipal(), // triggeringPrincipal
11562 nullptr, nullptr, aDocument->GetCsp(), true,
11563 getter_AddRefs(newSHEntry));
11564 NS_ENSURE_SUCCESS(rv, rv);
11565 mOSHE = newSHEntry;
11568 newSHEntry->SetURI(aNewURI);
11569 newSHEntry->SetOriginalURI(aNewURI);
11570 // We replaced the URI of the entry, clear the unstripped URI as it
11571 // shouldn't be used for reloads anymore.
11572 newSHEntry->SetUnstrippedURI(nullptr);
11573 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11574 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11575 // in our case. We could also set it to aNewURI, with the same result.
11576 newSHEntry->SetResultPrincipalURI(nullptr);
11577 newSHEntry->SetLoadReplace(false);
11580 if (!mozilla::SessionHistoryInParent()) {
11581 // Step 2.4 and 3: Modify new/original session history entry and clear its
11582 // POST data, if there is any.
11583 newSHEntry->SetStateData(aData);
11584 newSHEntry->SetPostData(nullptr);
11586 newSHEntry->SetURIWasModified(uriWasModified);
11588 // Step E as described at the top of AddState: If aReplace is false,
11589 // indicating that we're doing a pushState rather than a replaceState,
11590 // notify bfcache that we've added a page to the history so it can evict
11591 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11592 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11593 // root session history if this call is coming from a document.open() in a
11594 // docshell subtree that disables session history.
11595 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11596 if (rootSH) {
11597 rootSH->LegacySHistory()->EvictContentViewersOrReplaceEntry(newSHEntry,
11598 aReplace);
11602 // Step 4: If the document's URI changed, update document's URI and update
11603 // global history.
11605 // We need to call FireOnLocationChange so that the browser's address bar
11606 // gets updated and the back button is enabled, but we only need to
11607 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11608 // since SetCurrentURI will call FireOnLocationChange for us.
11610 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11611 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11612 // notification is allowed only when we know docshell is not loading a new
11613 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11614 // FireOnLocationChange(...) breaks security UI.
11616 // If the docshell is shutting down, don't update the document URI, as we
11617 // can't load into a docshell that is being destroyed.
11618 if (!aEqualURIs && !mIsBeingDestroyed) {
11619 aDocument->SetDocumentURI(aNewURI);
11620 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11621 /* aIsInitialAboutBlank */ false,
11622 GetSameDocumentNavigationFlags(aNewURI));
11624 AddURIVisit(aNewURI, aCurrentURI, 0);
11626 // AddURIVisit doesn't set the title for the new URI in global history,
11627 // so do that here.
11628 UpdateGlobalHistoryTitle(aNewURI);
11630 // Inform the favicon service that our old favicon applies to this new
11631 // URI.
11632 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11633 } else {
11634 FireDummyOnLocationChange();
11636 aDocument->SetStateObject(aData);
11638 return NS_OK;
11641 NS_IMETHODIMP
11642 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11643 if (mozilla::SessionHistoryInParent()) {
11644 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11645 return NS_OK;
11648 *aIsManual = false;
11649 if (mOSHE) {
11650 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11653 return NS_OK;
11656 NS_IMETHODIMP
11657 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11658 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11660 return NS_OK;
11663 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11664 nsISHEntry* aSHEntry, bool aIsManual) {
11665 if (aSHEntry) {
11666 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11669 if (mActiveEntry && mBrowsingContext) {
11670 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11671 if (XRE_IsParentProcess()) {
11672 SessionHistoryEntry* entry =
11673 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11674 if (entry) {
11675 entry->SetScrollRestorationIsManual(aIsManual);
11677 } else {
11678 mozilla::Unused << ContentChild::GetSingleton()
11679 ->SendSessionHistoryEntryScrollRestorationIsManual(
11680 mBrowsingContext, aIsManual);
11685 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11686 uint32_t aCacheKey) {
11687 if (aSHEntry) {
11688 aSHEntry->SetCacheKey(aCacheKey);
11691 if (mActiveEntry && mBrowsingContext) {
11692 mActiveEntry->SetCacheKey(aCacheKey);
11693 if (XRE_IsParentProcess()) {
11694 SessionHistoryEntry* entry =
11695 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11696 if (entry) {
11697 entry->SetCacheKey(aCacheKey);
11699 } else {
11700 mozilla::Unused
11701 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11702 mBrowsingContext, aCacheKey);
11707 /* static */
11708 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11709 // I believe none of the about: urls should go in the history. But then
11710 // that could just be me... If the intent is only deny about:blank then we
11711 // should just do a spec compare, rather than two gets of the scheme and
11712 // then the path. -Gagan
11713 nsresult rv;
11714 nsAutoCString buf;
11716 rv = aURI->GetScheme(buf);
11717 if (NS_FAILED(rv)) {
11718 return false;
11721 if (buf.EqualsLiteral("about")) {
11722 rv = aURI->GetPathQueryRef(buf);
11723 if (NS_FAILED(rv)) {
11724 return false;
11727 if (buf.EqualsLiteral("blank")) {
11728 return false;
11730 // We only want to add about:newtab if it's not privileged, and
11731 // if it is not configured to show the blank page.
11732 if (buf.EqualsLiteral("newtab")) {
11733 if (!StaticPrefs::browser_newtabpage_enabled()) {
11734 return false;
11737 NS_ENSURE_TRUE(aChannel, false);
11738 nsCOMPtr<nsIPrincipal> resultPrincipal;
11739 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11740 aChannel, getter_AddRefs(resultPrincipal));
11741 NS_ENSURE_SUCCESS(rv, false);
11742 return !resultPrincipal->IsSystemPrincipal();
11746 return true;
11749 nsresult nsDocShell::AddToSessionHistory(
11750 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11751 nsIPrincipal* aPrincipalToInherit,
11752 nsIPrincipal* aPartitionedPrincipalToInherit,
11753 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11754 nsISHEntry** aNewEntry) {
11755 MOZ_ASSERT(aURI, "uri is null");
11756 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11757 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11759 #if defined(DEBUG)
11760 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11761 nsAutoCString chanName;
11762 if (aChannel) {
11763 aChannel->GetName(chanName);
11764 } else {
11765 chanName.AssignLiteral("<no channel>");
11768 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11769 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11770 aURI->GetSpecOrDefault().get(), chanName.get()));
11772 #endif
11774 nsresult rv = NS_OK;
11775 nsCOMPtr<nsISHEntry> entry;
11778 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11779 * the existing SH entry in the page and replace the url and
11780 * other vitalities.
11782 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11783 !mBrowsingContext->IsTop()) {
11784 // This is a subframe
11785 entry = mOSHE;
11786 if (entry) {
11787 entry->ClearEntry();
11791 // Create a new entry if necessary.
11792 if (!entry) {
11793 entry = new nsSHEntry();
11796 // Get the post data & referrer
11797 nsCOMPtr<nsIInputStream> inputStream;
11798 nsCOMPtr<nsIURI> originalURI;
11799 nsCOMPtr<nsIURI> resultPrincipalURI;
11800 nsCOMPtr<nsIURI> unstrippedURI;
11801 bool loadReplace = false;
11802 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11803 uint32_t cacheKey = 0;
11804 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11805 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11806 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11807 aPartitionedPrincipalToInherit;
11808 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11809 bool expired = false; // by default the page is not expired
11810 bool discardLayoutState = false;
11811 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11812 bool userActivation = false;
11814 if (aChannel) {
11815 cacheChannel = do_QueryInterface(aChannel);
11817 /* If there is a caching channel, get the Cache Key and store it
11818 * in SH.
11820 if (cacheChannel) {
11821 cacheChannel->GetCacheKey(&cacheKey);
11823 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11825 // Check if the httpChannel is hiding under a multipartChannel
11826 if (!httpChannel) {
11827 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11829 if (httpChannel) {
11830 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11831 if (uploadChannel) {
11832 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11834 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11835 uint32_t loadFlags;
11836 aChannel->GetLoadFlags(&loadFlags);
11837 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11838 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11839 MOZ_ASSERT(NS_SUCCEEDED(rv));
11841 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11844 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11845 if (!triggeringPrincipal) {
11846 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11848 if (!csp) {
11849 csp = loadInfo->GetCspToInherit();
11852 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11854 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
11856 userActivation = loadInfo->GetHasValidUserGestureActivation();
11858 // For now keep storing just the principal in the SHEntry.
11859 if (!principalToInherit) {
11860 if (loadInfo->GetLoadingSandboxed()) {
11861 if (loadInfo->GetLoadingPrincipal()) {
11862 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11863 loadInfo->GetLoadingPrincipal());
11864 } else {
11865 // get the OriginAttributes
11866 OriginAttributes attrs;
11867 loadInfo->GetOriginAttributes(&attrs);
11868 principalToInherit = NullPrincipal::Create(attrs);
11870 } else {
11871 principalToInherit = loadInfo->PrincipalToInherit();
11875 if (!partitionedPrincipalToInherit) {
11876 // XXXehsan is it correct to fall back to the principal to inherit in all
11877 // cases? For example, what about the cases where we are using the load
11878 // info's principal to inherit? Do we need to add a similar concept to
11879 // load info for partitioned principal?
11880 partitionedPrincipalToInherit = principalToInherit;
11884 nsAutoString srcdoc;
11885 bool srcdocEntry = false;
11886 nsCOMPtr<nsIURI> baseURI;
11888 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11889 if (inStrmChan) {
11890 bool isSrcdocChannel;
11891 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11892 if (isSrcdocChannel) {
11893 inStrmChan->GetSrcdocData(srcdoc);
11894 srcdocEntry = true;
11895 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11896 } else {
11897 srcdoc.SetIsVoid(true);
11900 /* If cache got a 'no-store', ask SH not to store
11901 * HistoryLayoutState. By default, SH will set this
11902 * flag to true and save HistoryLayoutState.
11904 bool saveLayoutState = !discardLayoutState;
11906 if (cacheChannel) {
11907 // Check if the page has expired from cache
11908 uint32_t expTime = 0;
11909 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11910 uint32_t now = PRTimeToSeconds(PR_Now());
11911 if (expTime <= now) {
11912 expired = true;
11916 // Title is set in nsDocShell::SetTitle()
11917 entry->Create(aURI, // uri
11918 u""_ns, // Title
11919 inputStream, // Post data stream
11920 cacheKey, // CacheKey
11921 mContentTypeHint, // Content-type
11922 triggeringPrincipal, // Channel or provided principal
11923 principalToInherit, partitionedPrincipalToInherit, csp,
11924 HistoryID(), GetCreatedDynamically(), originalURI,
11925 resultPrincipalURI, unstrippedURI, loadReplace, referrerInfo,
11926 srcdoc, srcdocEntry, baseURI, saveLayoutState, expired,
11927 userActivation);
11929 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11930 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11931 Maybe<int32_t> previousEntryIndex;
11932 Maybe<int32_t> loadedEntryIndex;
11933 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11934 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11935 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11937 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11938 if (previousEntryIndex.isSome()) {
11939 mPreviousEntryIndex = previousEntryIndex.value();
11941 if (loadedEntryIndex.isSome()) {
11942 mLoadedEntryIndex = loadedEntryIndex.value();
11945 // aCloneChildren implies that we are retaining the same document, thus we
11946 // need to signal to the top WC that the new SHEntry may receive a fresh
11947 // user interaction flag.
11948 if (aCloneChildren) {
11949 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11950 if (topWc && !topWc->IsDiscarded()) {
11951 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11954 } else {
11955 // This is a subframe, make sure that this new SHEntry will be
11956 // marked with user interaction.
11957 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11958 if (topWc && !topWc->IsDiscarded()) {
11959 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11961 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11962 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11963 aCloneChildren);
11967 // Return the new SH entry...
11968 if (aNewEntry) {
11969 *aNewEntry = nullptr;
11970 if (NS_SUCCEEDED(rv)) {
11971 entry.forget(aNewEntry);
11975 return rv;
11978 void nsDocShell::UpdateActiveEntry(
11979 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11980 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
11981 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
11982 const nsAString& aTitle, bool aScrollRestorationIsManual,
11983 nsIStructuredCloneContainer* aData, bool aURIWasModified) {
11984 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11985 MOZ_ASSERT(aURI, "uri is null");
11986 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
11987 "This code only deals with pushState");
11988 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
11990 MOZ_LOG(gSHLog, LogLevel::Debug,
11991 ("Creating an active entry on nsDocShell %p to %s", this,
11992 aURI->GetSpecOrDefault().get()));
11994 // Even if we're replacing an existing entry we create new a
11995 // SessionHistoryInfo. In the parent process we'll keep the existing
11996 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11997 // entry keeps identity but its data is replaced.
11998 bool replace = aReplace && mActiveEntry;
12000 if (!replace) {
12001 CollectWireframe();
12004 if (mActiveEntry) {
12005 // Link this entry to the previous active entry.
12006 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
12007 } else {
12008 mActiveEntry = MakeUnique<SessionHistoryInfo>(
12009 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
12011 mActiveEntry->SetOriginalURI(aOriginalURI);
12012 mActiveEntry->SetUnstrippedURI(nullptr);
12013 mActiveEntry->SetReferrerInfo(aReferrerInfo);
12014 mActiveEntry->SetTitle(aTitle);
12015 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
12016 mActiveEntry->SetURIWasModified(aURIWasModified);
12017 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
12019 if (replace) {
12020 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
12021 } else {
12022 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
12023 // FIXME We should probably just compute mChildOffset in the parent
12024 // instead of passing it over IPC here.
12025 mBrowsingContext->SetActiveSessionHistoryEntry(
12026 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
12027 /* aCacheKey = */ 0);
12028 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
12032 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
12033 bool aUserActivation) {
12034 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
12036 nsresult rv;
12037 RefPtr<nsDocShellLoadState> loadState;
12038 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
12039 NS_ENSURE_SUCCESS(rv, rv);
12041 // Calling CreateAboutBlankContentViewer can set mOSHE to null, and if
12042 // that's the only thing holding a ref to aEntry that will cause aEntry to
12043 // die while we're loading it. So hold a strong ref to aEntry here, just
12044 // in case.
12045 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
12047 loadState->SetHasValidUserGestureActivation(
12048 loadState->HasValidUserGestureActivation() || aUserActivation);
12050 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
12053 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
12054 uint32_t aLoadType,
12055 bool aUserActivation) {
12056 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
12057 loadState->SetHasValidUserGestureActivation(
12058 loadState->HasValidUserGestureActivation() || aUserActivation);
12060 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
12063 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
12064 uint32_t aLoadType,
12065 bool aLoadingCurrentEntry) {
12066 if (!IsNavigationAllowed()) {
12067 return NS_OK;
12070 // We are setting load type afterwards so we don't have to
12071 // send it in an IPC message
12072 aLoadState->SetLoadType(aLoadType);
12074 nsresult rv;
12075 if (SchemeIsJavascript(aLoadState->URI())) {
12076 // We're loading a URL that will execute script from inside asyncOpen.
12077 // Replace the current document with about:blank now to prevent
12078 // anything from the current document from leaking into any JavaScript
12079 // code in the URL.
12080 // Don't cache the presentation if we're going to just reload the
12081 // current entry. Caching would lead to trying to save the different
12082 // content viewers in the same nsISHEntry object.
12083 rv = CreateAboutBlankContentViewer(
12084 aLoadState->PrincipalToInherit(),
12085 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
12086 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
12088 if (NS_FAILED(rv)) {
12089 // The creation of the intermittent about:blank content
12090 // viewer failed for some reason (potentially because the
12091 // user prevented it). Interrupt the history load.
12092 return NS_OK;
12095 if (!aLoadState->TriggeringPrincipal()) {
12096 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
12097 // URIs will pick it up from the about:blank page we just loaded,
12098 // and we don't really want even that in this case.
12099 nsCOMPtr<nsIPrincipal> principal =
12100 NullPrincipal::Create(GetOriginAttributes());
12101 aLoadState->SetTriggeringPrincipal(principal);
12105 /* If there is a valid postdata *and* the user pressed
12106 * reload or shift-reload, take user's permission before we
12107 * repost the data to the server.
12109 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
12110 bool repost;
12111 rv = ConfirmRepost(&repost);
12112 if (NS_FAILED(rv)) {
12113 return rv;
12116 // If the user pressed cancel in the dialog, return. We're done here.
12117 if (!repost) {
12118 return NS_BINDING_ABORTED;
12122 // If there is no valid triggeringPrincipal, we deny the load
12123 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
12124 "need a valid triggeringPrincipal to load from history");
12125 if (!aLoadState->TriggeringPrincipal()) {
12126 return NS_ERROR_FAILURE;
12129 return InternalLoad(aLoadState); // No nsIRequest
12132 NS_IMETHODIMP
12133 nsDocShell::PersistLayoutHistoryState() {
12134 nsresult rv = NS_OK;
12136 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12137 bool scrollRestorationIsManual;
12138 if (mozilla::SessionHistoryInParent()) {
12139 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12140 } else {
12141 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12143 nsCOMPtr<nsILayoutHistoryState> layoutState;
12144 if (RefPtr<PresShell> presShell = GetPresShell()) {
12145 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12146 } else if (scrollRestorationIsManual) {
12147 // Even if we don't have layout anymore, we may want to reset the
12148 // current scroll state in layout history.
12149 GetLayoutHistoryState(getter_AddRefs(layoutState));
12152 if (scrollRestorationIsManual && layoutState) {
12153 layoutState->ResetScrollState();
12157 return rv;
12160 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12161 nsISHEntry* aNewEntry) {
12162 if (aOldEntry == mOSHE) {
12163 mOSHE = aNewEntry;
12166 if (aOldEntry == mLSHE) {
12167 mLSHE = aNewEntry;
12171 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12172 const Maybe<nsISHEntry*>& aOSHE) {
12173 // We want to hold on to the reference in mLSHE before we update it.
12174 // Otherwise, SetHistoryEntry could release the last reference to
12175 // the entry while aOSHE is pointing to it.
12176 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12177 if (aLSHE.isSome()) {
12178 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12179 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12181 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12182 if (aOSHE.isSome()) {
12183 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12184 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12188 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12189 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12190 // We need to sync up the docshell and session history trees for
12191 // subframe navigation. If the load was in a subframe, we forward up to
12192 // the root docshell, which will then recursively sync up all docshells
12193 // to their corresponding entries in the new session history tree.
12194 // If we don't do this, then we can cache a content viewer on the wrong
12195 // cloned entry, and subsequently restore it at the wrong time.
12196 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12197 if (topBC->IsDiscarded()) {
12198 topBC = nullptr;
12200 RefPtr<BrowsingContext> currBC =
12201 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12202 if (topBC && *aPtr) {
12203 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12205 nsCOMPtr<nsISHEntry> entry(aEntry);
12206 entry.swap(*aPtr);
12207 return entry.forget();
12210 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12211 RefPtr<ChildSHistory> childSHistory =
12212 mBrowsingContext->Top()->GetChildSessionHistory();
12213 return childSHistory.forget();
12216 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12217 nsIHttpChannel** aReturn) {
12218 NS_ENSURE_ARG_POINTER(aReturn);
12219 if (!aChannel) {
12220 return NS_ERROR_FAILURE;
12223 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12224 if (multiPartChannel) {
12225 nsCOMPtr<nsIChannel> baseChannel;
12226 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12227 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12228 *aReturn = httpChannel;
12229 NS_IF_ADDREF(*aReturn);
12231 return NS_OK;
12234 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12235 // By default layout State will be saved.
12236 if (!aChannel) {
12237 return false;
12240 // figure out if SH should be saving layout state
12241 bool noStore = false;
12242 Unused << aChannel->IsNoStoreResponse(&noStore);
12243 return noStore;
12246 NS_IMETHODIMP
12247 nsDocShell::GetEditor(nsIEditor** aEditor) {
12248 NS_ENSURE_ARG_POINTER(aEditor);
12249 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12250 htmlEditor.forget(aEditor);
12251 return NS_OK;
12254 NS_IMETHODIMP
12255 nsDocShell::SetEditor(nsIEditor* aEditor) {
12256 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12257 // If TextEditor comes, throw an error.
12258 if (aEditor && !htmlEditor) {
12259 return NS_ERROR_INVALID_ARG;
12261 return SetHTMLEditorInternal(htmlEditor);
12264 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12265 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12268 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12269 if (!aHTMLEditor && !mEditorData) {
12270 return NS_OK;
12273 nsresult rv = EnsureEditorData();
12274 if (NS_FAILED(rv)) {
12275 return rv;
12278 return mEditorData->SetHTMLEditor(aHTMLEditor);
12281 NS_IMETHODIMP
12282 nsDocShell::GetEditable(bool* aEditable) {
12283 NS_ENSURE_ARG_POINTER(aEditable);
12284 *aEditable = mEditorData && mEditorData->GetEditable();
12285 return NS_OK;
12288 NS_IMETHODIMP
12289 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12290 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12292 if (mEditorData) {
12293 *aHasEditingSession = !!mEditorData->GetEditingSession();
12294 } else {
12295 *aHasEditingSession = false;
12298 return NS_OK;
12301 NS_IMETHODIMP
12302 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12303 nsresult rv = EnsureEditorData();
12304 if (NS_FAILED(rv)) {
12305 return rv;
12308 return mEditorData->MakeEditable(aInWaitForUriLoad);
12311 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12312 bool needToAddURIVisit = true;
12313 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12314 if (props) {
12315 mozilla::Unused << props->GetPropertyAsBool(
12316 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12319 return needToAddURIVisit;
12322 /* static */ void nsDocShell::ExtractLastVisit(
12323 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12324 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12325 if (!props) {
12326 return;
12329 nsresult rv;
12330 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12331 if (NS_SUCCEEDED(rv)) {
12332 uri.forget(aURI);
12334 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12335 aChannelRedirectFlags);
12337 NS_WARNING_ASSERTION(
12338 NS_SUCCEEDED(rv),
12339 "Could not fetch previous flags, URI will be treated like referrer");
12341 } else {
12342 // There is no last visit for this channel, so this must be the first
12343 // link. Link the visit to the referrer of this request, if any.
12344 // Treat referrer as null if there is an error getting it.
12345 NS_GetReferrerFromChannel(aChannel, aURI);
12349 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12350 uint32_t aChannelRedirectFlags) {
12351 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12352 if (!props || !aURI) {
12353 return;
12356 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12357 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12358 aChannelRedirectFlags);
12361 /* static */ void nsDocShell::InternalAddURIVisit(
12362 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12363 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12364 nsIWidget* aWidget, uint32_t aLoadType) {
12365 MOZ_ASSERT(aURI, "Visited URI is null!");
12366 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12367 "Do not add error or bypass pages to global history");
12369 bool usePrivateBrowsing = false;
12370 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12372 // Only content-type docshells save URI visits. Also don't do
12373 // anything here if we're not supposed to use global history.
12374 if (!aBrowsingContext->IsContent() ||
12375 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12376 return;
12379 nsCOMPtr<IHistory> history = components::History::Service();
12381 if (history) {
12382 uint32_t visitURIFlags = 0;
12384 if (aBrowsingContext->IsTop()) {
12385 visitURIFlags |= IHistory::TOP_LEVEL;
12388 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12389 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12390 } else if (aChannelRedirectFlags &
12391 nsIChannelEventSink::REDIRECT_PERMANENT) {
12392 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12393 } else {
12394 MOZ_ASSERT(!aChannelRedirectFlags,
12395 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12396 "if any flags in aChannelRedirectFlags is set.");
12399 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12400 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12401 if (aResponseStatus == 301 || aResponseStatus == 308) {
12402 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12405 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12406 // simple retry attempt by the user is unlikely to solve them.
12407 // 408 is special cased, since may actually indicate a temporary
12408 // connection problem.
12409 else if (aResponseStatus != 408 &&
12410 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12411 aResponseStatus == 505)) {
12412 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12415 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12416 visitURIFlags,
12417 aBrowsingContext->BrowserId());
12421 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12422 uint32_t aChannelRedirectFlags,
12423 uint32_t aResponseStatus) {
12424 nsPIDOMWindowOuter* outer = GetWindow();
12425 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12427 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12428 aResponseStatus, mBrowsingContext, widget, mLoadType);
12431 //*****************************************************************************
12432 // nsDocShell: Helper Routines
12433 //*****************************************************************************
12435 NS_IMETHODIMP
12436 nsDocShell::SetLoadType(uint32_t aLoadType) {
12437 mLoadType = aLoadType;
12438 return NS_OK;
12441 NS_IMETHODIMP
12442 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12443 *aLoadType = mLoadType;
12444 return NS_OK;
12447 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12448 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12449 *aRepost = true;
12450 return NS_OK;
12453 nsCOMPtr<nsIPromptCollection> prompter =
12454 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12455 if (!prompter) {
12456 return NS_ERROR_NOT_AVAILABLE;
12459 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12462 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12463 nsIStringBundle** aStringBundle) {
12464 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12465 NS_ERROR_FAILURE);
12467 nsCOMPtr<nsIStringBundleService> stringBundleService =
12468 mozilla::components::StringBundle::Service();
12469 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12471 NS_ENSURE_SUCCESS(
12472 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12473 NS_ERROR_FAILURE);
12475 return NS_OK;
12478 nsIScrollableFrame* nsDocShell::GetRootScrollFrame() {
12479 PresShell* presShell = GetPresShell();
12480 NS_ENSURE_TRUE(presShell, nullptr);
12482 return presShell->GetRootScrollFrameAsScrollable();
12485 nsresult nsDocShell::EnsureScriptEnvironment() {
12486 if (mScriptGlobal) {
12487 return NS_OK;
12490 if (mIsBeingDestroyed) {
12491 return NS_ERROR_NOT_AVAILABLE;
12494 #ifdef DEBUG
12495 NS_ASSERTION(!mInEnsureScriptEnv,
12496 "Infinite loop! Calling EnsureScriptEnvironment() from "
12497 "within EnsureScriptEnvironment()!");
12499 // Yeah, this isn't re-entrant safe, but that's ok since if we
12500 // re-enter this method, we'll infinitely loop...
12501 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12502 mInEnsureScriptEnv = true;
12503 #endif
12505 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12506 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12508 uint32_t chromeFlags;
12509 browserChrome->GetChromeFlags(&chromeFlags);
12511 // If our window is modal and we're not opened as chrome, make
12512 // this window a modal content window.
12513 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12514 MOZ_ASSERT(mScriptGlobal);
12516 // Ensure the script object is set up to run script.
12517 return mScriptGlobal->EnsureScriptEnvironment();
12520 nsresult nsDocShell::EnsureEditorData() {
12521 MOZ_ASSERT(!mIsBeingDestroyed);
12523 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12524 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12525 // We shouldn't recreate the editor data if it already exists, or
12526 // we're shutting down, or we already have a detached editor data
12527 // stored in the session history. We should only have one editordata
12528 // per docshell.
12529 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12532 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12535 nsresult nsDocShell::EnsureFind() {
12536 if (!mFind) {
12537 mFind = new nsWebBrowserFind();
12540 // we promise that the nsIWebBrowserFind that we return has been set
12541 // up to point to the focused, or content window, so we have to
12542 // set that up each time.
12544 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12545 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12547 // default to our window
12548 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12549 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12550 nsFocusManager::GetFocusedDescendant(ourWindow,
12551 nsFocusManager::eIncludeAllDescendants,
12552 getter_AddRefs(windowToSearch));
12554 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12555 if (!findInFrames) {
12556 return NS_ERROR_NO_INTERFACE;
12559 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12560 if (NS_FAILED(rv)) {
12561 return rv;
12563 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12564 if (NS_FAILED(rv)) {
12565 return rv;
12568 return NS_OK;
12571 NS_IMETHODIMP
12572 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12573 NS_ENSURE_ARG(aDoomed);
12574 *aDoomed = mIsBeingDestroyed;
12575 return NS_OK;
12578 NS_IMETHODIMP
12579 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12580 NS_ENSURE_ARG(aResult);
12581 *aResult = mIsExecutingOnLoadHandler;
12582 return NS_OK;
12585 NS_IMETHODIMP
12586 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12587 nsCOMPtr<nsILayoutHistoryState> state;
12588 if (mozilla::SessionHistoryInParent()) {
12589 if (mActiveEntry) {
12590 state = mActiveEntry->GetLayoutHistoryState();
12592 } else {
12593 if (mOSHE) {
12594 state = mOSHE->GetLayoutHistoryState();
12597 state.forget(aLayoutHistoryState);
12598 return NS_OK;
12601 NS_IMETHODIMP
12602 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12603 if (mOSHE) {
12604 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12606 if (mActiveEntry) {
12607 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12609 return NS_OK;
12612 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12613 nsIInterfaceRequestor* aRequestor) {
12614 if (aRequestor) {
12615 mWeakPtr = do_GetWeakReference(aRequestor);
12619 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12620 mWeakPtr = nullptr;
12623 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12625 NS_IMETHODIMP
12626 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12627 void** aSink) {
12628 NS_ENSURE_ARG_POINTER(aSink);
12629 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12630 if (ifReq) {
12631 return ifReq->GetInterface(aIID, aSink);
12633 *aSink = nullptr;
12634 return NS_NOINTERFACE;
12637 //*****************************************************************************
12638 // nsDocShell::nsIAuthPromptProvider
12639 //*****************************************************************************
12641 NS_IMETHODIMP
12642 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12643 void** aResult) {
12644 // a priority prompt request will override a false mAllowAuth setting
12645 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12647 if (!mAllowAuth && !priorityPrompt) {
12648 return NS_ERROR_NOT_AVAILABLE;
12651 // we're either allowing auth, or it's a proxy request
12652 nsresult rv;
12653 nsCOMPtr<nsIPromptFactory> wwatch =
12654 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12655 NS_ENSURE_SUCCESS(rv, rv);
12657 rv = EnsureScriptEnvironment();
12658 NS_ENSURE_SUCCESS(rv, rv);
12660 // Get the an auth prompter for our window so that the parenting
12661 // of the dialogs works as it should when using tabs.
12663 return wwatch->GetPrompt(mScriptGlobal, aIID,
12664 reinterpret_cast<void**>(aResult));
12667 //*****************************************************************************
12668 // nsDocShell::nsILoadContext
12669 //*****************************************************************************
12671 NS_IMETHODIMP
12672 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12673 CallGetInterface(this, aWindow);
12674 return NS_OK;
12677 NS_IMETHODIMP
12678 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12679 return mBrowsingContext->GetTopWindow(aWindow);
12682 NS_IMETHODIMP
12683 nsDocShell::GetTopFrameElement(Element** aElement) {
12684 return mBrowsingContext->GetTopFrameElement(aElement);
12687 NS_IMETHODIMP
12688 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12689 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12692 NS_IMETHODIMP
12693 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12694 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12697 NS_IMETHODIMP
12698 nsDocShell::GetIsContent(bool* aIsContent) {
12699 *aIsContent = (mItemType == typeContent);
12700 return NS_OK;
12703 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12704 MOZ_ASSERT(aURI, "Must have a URI!");
12706 if (!mFiredUnloadEvent) {
12707 return true;
12710 if (!mLoadingURI) {
12711 return false;
12714 bool isPrivateWin = false;
12715 Document* doc = GetDocument();
12716 if (doc) {
12717 isPrivateWin =
12718 doc->NodePrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;
12721 nsCOMPtr<nsIScriptSecurityManager> secMan =
12722 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12723 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12724 aURI, mLoadingURI, false, isPrivateWin));
12728 // Routines for selection and clipboard
12730 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12731 nsIController** aResult) {
12732 NS_ENSURE_ARG_POINTER(aResult);
12733 *aResult = nullptr;
12735 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12737 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12738 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12740 return root->GetControllerForCommand(aCommand, false /* for any window */,
12741 aResult);
12744 NS_IMETHODIMP
12745 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12746 NS_ENSURE_ARG_POINTER(aResult);
12747 *aResult = false;
12749 nsresult rv = NS_ERROR_FAILURE;
12751 nsCOMPtr<nsIController> controller;
12752 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12753 if (controller) {
12754 rv = controller->IsCommandEnabled(aCommand, aResult);
12757 return rv;
12760 NS_IMETHODIMP
12761 nsDocShell::DoCommand(const char* aCommand) {
12762 nsresult rv = NS_ERROR_FAILURE;
12764 nsCOMPtr<nsIController> controller;
12765 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12766 if (controller) {
12767 rv = controller->DoCommand(aCommand);
12770 return rv;
12773 NS_IMETHODIMP
12774 nsDocShell::DoCommandWithParams(const char* aCommand,
12775 nsICommandParams* aParams) {
12776 nsCOMPtr<nsIController> controller;
12777 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12778 if (NS_WARN_IF(NS_FAILED(rv))) {
12779 return rv;
12782 nsCOMPtr<nsICommandController> commandController =
12783 do_QueryInterface(controller, &rv);
12784 if (NS_WARN_IF(NS_FAILED(rv))) {
12785 return rv;
12788 return commandController->DoCommandWithParams(aCommand, aParams);
12791 nsresult nsDocShell::EnsureCommandHandler() {
12792 if (!mCommandManager) {
12793 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12794 mCommandManager = new nsCommandManager(domWindow);
12797 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12800 // link handling
12802 class OnLinkClickEvent : public Runnable {
12803 public:
12804 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12805 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12806 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12808 NS_IMETHOD Run() override {
12809 AutoPopupStatePusher popupStatePusher(mPopupState);
12811 // We need to set up an AutoJSAPI here for the following reason: When we
12812 // do OnLinkClickSync we'll eventually end up in
12813 // nsGlobalWindow::OpenInternal which only does popup blocking if
12814 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12815 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12816 // concerned.
12817 AutoJSAPI jsapi;
12818 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12819 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12820 mTriggeringPrincipal);
12822 return NS_OK;
12825 private:
12826 RefPtr<nsDocShell> mHandler;
12827 nsCOMPtr<nsIContent> mContent;
12828 RefPtr<nsDocShellLoadState> mLoadState;
12829 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12830 PopupBlocker::PopupControlState mPopupState;
12831 bool mNoOpenerImplied;
12832 bool mIsTrusted;
12835 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12836 nsDocShellLoadState* aLoadState,
12837 bool aNoOpenerImplied, bool aIsTrusted,
12838 nsIPrincipal* aTriggeringPrincipal)
12839 : mozilla::Runnable("OnLinkClickEvent"),
12840 mHandler(aHandler),
12841 mContent(aContent),
12842 mLoadState(aLoadState),
12843 mTriggeringPrincipal(aTriggeringPrincipal),
12844 mPopupState(PopupBlocker::GetPopupControlState()),
12845 mNoOpenerImplied(aNoOpenerImplied),
12846 mIsTrusted(aIsTrusted) {}
12848 nsresult nsDocShell::OnLinkClick(
12849 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12850 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12851 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12852 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12853 #ifndef ANDROID
12854 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12855 #endif
12856 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12858 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12859 return NS_OK;
12862 // On history navigation through Back/Forward buttons, don't execute
12863 // automatic JavaScript redirection such as |anchorElement.click()| or
12864 // |formElement.submit()|.
12866 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12867 // nsDocShell::OnLinkClickSync(...) instead.
12868 if (ShouldBlockLoadingForBackButton()) {
12869 return NS_OK;
12872 if (aContent->IsEditable()) {
12873 return NS_OK;
12876 Document* ownerDoc = aContent->OwnerDoc();
12877 if (nsContentUtils::IsExternalProtocol(aURI)) {
12878 ownerDoc->EnsureNotEnteringAndExitFullscreen();
12881 bool noOpenerImplied = false;
12882 nsAutoString target(aTargetSpec);
12883 if (aFileName.IsVoid() &&
12884 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
12885 target = u"_blank";
12886 if (!aTargetSpec.Equals(target)) {
12887 noOpenerImplied = true;
12891 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12892 loadState->SetTarget(target);
12893 loadState->SetFileName(aFileName);
12894 loadState->SetPostDataStream(aPostDataStream);
12895 loadState->SetHeadersStream(aHeadersDataStream);
12896 loadState->SetFirstParty(true);
12897 loadState->SetTriggeringPrincipal(
12898 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12899 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12900 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12901 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12903 nsCOMPtr<nsIRunnable> ev =
12904 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12905 aIsTrusted, aTriggeringPrincipal);
12906 return Dispatch(TaskCategory::UI, ev.forget());
12909 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12910 nsIURI* aLinkURI, nsIContent* aContent,
12911 bool aIsUserTriggered) {
12912 if (net::SchemeIsJavascript(aLinkURI)) {
12913 return false;
12916 // External links from within app tabs should always open in new tabs
12917 // instead of replacing the app tab's page (Bug 575561)
12918 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12919 // get either host, just return false to use the original target.
12920 nsAutoCString linkHost;
12921 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12922 return false;
12925 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12926 // privileged code to change the default targeting behaviour. In particular,
12927 // if a user-initiated link click for the (or targetting the) top-level frame
12928 // is detected, we default the target to "_blank" to give it a new
12929 // top-level BrowsingContext.
12930 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
12931 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
12932 aOriginalTarget == u"_top"_ns)) {
12933 return true;
12936 // Don't modify non-default targets.
12937 if (!aOriginalTarget.IsEmpty()) {
12938 return false;
12941 // Only check targets that are in extension panels or app tabs.
12942 // (isAppTab will be false for app tab subframes).
12943 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12944 if (!mmGroup.EqualsLiteral("webext-browsers") &&
12945 !mBrowsingContext->IsAppTab()) {
12946 return false;
12949 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12950 if (!docURI) {
12951 return false;
12954 nsAutoCString docHost;
12955 if (NS_FAILED(docURI->GetHost(docHost))) {
12956 return false;
12959 if (linkHost.Equals(docHost)) {
12960 return false;
12963 // Special case: ignore "www" prefix if it is part of host string
12964 return linkHost.Length() < docHost.Length()
12965 ? !docHost.Equals("www."_ns + linkHost)
12966 : !linkHost.Equals("www."_ns + docHost);
12969 static bool ElementCanHaveNoopener(nsIContent* aContent) {
12970 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12971 // the HTML, XHTML, or SVG namespace.
12972 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
12973 nsGkAtoms::form) ||
12974 aContent->IsSVGElement(nsGkAtoms::a);
12977 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12978 nsDocShellLoadState* aLoadState,
12979 bool aNoOpenerImplied,
12980 nsIPrincipal* aTriggeringPrincipal) {
12981 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12982 return NS_OK;
12985 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12986 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12987 // but |HTMLFormElement::SubmitSubmission(...)|.
12988 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
12989 ShouldBlockLoadingForBackButton()) {
12990 return NS_OK;
12993 if (aContent->IsEditable()) {
12994 return NS_OK;
12997 // if the triggeringPrincipal is not passed explicitly, then we
12998 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12999 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
13000 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
13003 // defer to an external protocol handler if necessary...
13004 nsCOMPtr<nsIExternalProtocolService> extProtService =
13005 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
13006 if (extProtService) {
13007 nsAutoCString scheme;
13008 aLoadState->URI()->GetScheme(scheme);
13009 if (!scheme.IsEmpty()) {
13010 // if the URL scheme does not correspond to an exposed protocol, then
13011 // we need to hand this link click over to the external protocol
13012 // handler.
13013 bool isExposed;
13014 nsresult rv =
13015 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
13016 if (NS_SUCCEEDED(rv) && !isExposed) {
13017 return extProtService->LoadURI(
13018 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
13019 /* aTriggeredExternally */
13020 false,
13021 /* aHasValidUserGestureActivation */
13022 aContent->OwnerDoc()->HasValidTransientUserGestureActivation());
13027 uint32_t triggeringSandboxFlags = 0;
13028 if (mBrowsingContext) {
13029 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
13032 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
13033 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
13034 bool triggeringPrincipalIsSystemPrincipal =
13035 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
13036 if (elementCanHaveNoopener) {
13037 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
13038 nsAutoString relString;
13039 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
13040 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
13041 relString);
13043 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
13044 bool explicitOpenerSet = false;
13046 // The opener behaviour follows a hierarchy, such that if a higher
13047 // priority behaviour is specified, it always takes priority. That
13048 // priority is currently: norefrerer > noopener > opener > default
13050 while (tok.hasMoreTokens()) {
13051 const nsAString& token = tok.nextToken();
13052 if (token.LowerCaseEqualsLiteral("noreferrer")) {
13053 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
13054 INTERNAL_LOAD_FLAGS_NO_OPENER;
13055 // noreferrer cannot be overwritten by a 'rel=opener'.
13056 explicitOpenerSet = true;
13057 break;
13060 if (token.LowerCaseEqualsLiteral("noopener")) {
13061 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13062 explicitOpenerSet = true;
13065 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
13066 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
13067 explicitOpenerSet = true;
13071 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
13072 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
13073 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13076 if (aNoOpenerImplied) {
13077 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13081 // Get the owner document of the link that was clicked, this will be
13082 // the document that the link is in, or the last document that the
13083 // link was in. From that document, we'll get the URI to use as the
13084 // referrer, since the current URI in this docshell may be a
13085 // new document that we're in the process of loading.
13086 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
13088 // Now check that the referrerDoc's inner window is the current inner
13089 // window for mScriptGlobal. If it's not, then we don't want to
13090 // follow this link.
13091 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
13092 if (!mScriptGlobal || !referrerInner ||
13093 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
13094 // We're no longer the current inner window
13095 return NS_OK;
13098 // referrer could be null here in some odd cases, but that's ok,
13099 // we'll just load the link w/o sending a referrer in those cases.
13101 // If this is an anchor element, grab its type property to use as a hint
13102 nsAutoString typeHint;
13103 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13104 if (anchor) {
13105 anchor->GetType(typeHint);
13106 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13107 nsAutoCString type, dummy;
13108 NS_ParseRequestContentType(utf8Hint, type, dummy);
13109 CopyUTF8toUTF16(type, typeHint);
13112 uint32_t loadType = LOAD_LINK;
13113 if (aLoadState->IsFormSubmission()) {
13114 if (aLoadState->Target().IsEmpty()) {
13115 // We set the right load type here for form submissions with an empty
13116 // target. Form submission with a non-empty target are handled in
13117 // nsDocShell::PerformRetargeting after we've selected the correct target
13118 // BC.
13119 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
13121 } else {
13122 // Link click can be triggered inside an onload handler, and we don't want
13123 // to add history entry in this case.
13124 bool inOnLoadHandler = false;
13125 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13126 if (inOnLoadHandler) {
13127 loadType = LOAD_NORMAL_REPLACE;
13131 nsCOMPtr<nsIReferrerInfo> referrerInfo =
13132 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
13133 : new ReferrerInfo(*referrerDoc);
13134 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
13136 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
13137 aLoadState->SetReferrerInfo(referrerInfo);
13138 aLoadState->SetInternalLoadFlags(flags);
13139 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
13140 aLoadState->SetLoadType(loadType);
13141 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
13142 aLoadState->SetHasValidUserGestureActivation(
13143 context && context->HasValidTransientUserGestureActivation());
13145 nsresult rv = InternalLoad(aLoadState);
13147 if (NS_SUCCEEDED(rv)) {
13148 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
13149 referrerInfo);
13152 return rv;
13155 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13156 const nsAString& aTargetSpec) {
13157 if (aContent->IsEditable()) {
13158 return NS_OK;
13161 nsresult rv = NS_ERROR_FAILURE;
13163 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13164 if (!browserChrome) {
13165 return rv;
13168 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13169 nsAutoCString spec;
13170 rv = exposableURI->GetDisplaySpec(spec);
13171 NS_ENSURE_SUCCESS(rv, rv);
13173 NS_ConvertUTF8toUTF16 uStr(spec);
13175 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13176 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13178 rv = browserChrome->SetLinkStatus(uStr);
13179 return rv;
13182 nsresult nsDocShell::OnLeaveLink() {
13183 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13184 nsresult rv = NS_ERROR_FAILURE;
13186 if (browserChrome) {
13187 rv = browserChrome->SetLinkStatus(u""_ns);
13189 return rv;
13192 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13193 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13194 UserActivation::IsHandlingUserInput() ||
13195 !Preferences::GetBool("accessibility.blockjsredirection")) {
13196 return false;
13199 bool canGoForward = false;
13200 GetCanGoForward(&canGoForward);
13201 return canGoForward;
13204 bool nsDocShell::PluginsAllowedInCurrentDoc() {
13205 if (!mContentViewer) {
13206 return false;
13209 Document* doc = mContentViewer->GetDocument();
13210 if (!doc) {
13211 return false;
13214 return doc->GetAllowPlugins();
13217 //----------------------------------------------------------------------
13218 // Web Shell Services API
13220 // This functions is only called when a new charset is detected in loading a
13221 // document.
13222 nsresult nsDocShell::CharsetChangeReloadDocument(
13223 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13224 // XXX hack. keep the aCharset and aSource wait to pick it up
13225 nsCOMPtr<nsIContentViewer> cv;
13226 NS_ENSURE_SUCCESS(GetContentViewer(getter_AddRefs(cv)), NS_ERROR_FAILURE);
13227 if (cv) {
13228 int32_t source;
13229 Unused << cv->GetReloadEncodingAndSource(&source);
13230 if (aSource > source) {
13231 cv->SetReloadEncodingAndSource(aEncoding, aSource);
13232 if (eCharsetReloadRequested != mCharsetReloadState) {
13233 mCharsetReloadState = eCharsetReloadRequested;
13234 switch (mLoadType) {
13235 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13236 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13237 LOAD_FLAGS_BYPASS_PROXY);
13238 case LOAD_RELOAD_BYPASS_CACHE:
13239 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13240 default:
13241 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13246 // return failure if this request is not accepted due to mCharsetReloadState
13247 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13250 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13251 if (eCharsetReloadRequested != mCharsetReloadState) {
13252 Stop(nsIWebNavigation::STOP_ALL);
13253 return NS_OK;
13255 // return failer if this request is not accepted due to mCharsetReloadState
13256 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13259 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13260 #if NS_PRINT_PREVIEW
13261 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mContentViewer);
13262 return viewer->ExitPrintPreview();
13263 #else
13264 return NS_OK;
13265 #endif
13268 /* [infallible] */
13269 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13270 bool* aIsTopLevelContentDocShell) {
13271 *aIsTopLevelContentDocShell = false;
13273 if (mItemType == typeContent) {
13274 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13277 return NS_OK;
13280 // Implements nsILoadContext.originAttributes
13281 NS_IMETHODIMP
13282 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13283 JS::MutableHandle<JS::Value> aVal) {
13284 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13287 // Implements nsIDocShell.GetOriginAttributes()
13288 NS_IMETHODIMP
13289 nsDocShell::GetOriginAttributes(JSContext* aCx,
13290 JS::MutableHandle<JS::Value> aVal) {
13291 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13294 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13295 nsIURI* aURI) {
13296 MOZ_ASSERT(aPrincipal);
13297 MOZ_ASSERT(aURI);
13299 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13300 return false;
13303 nsCOMPtr<nsIDocShellTreeItem> parent;
13304 GetInProcessSameTypeParent(getter_AddRefs(parent));
13305 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13306 nsPIDOMWindowInner* parentInner =
13307 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13309 StorageAccess storage =
13310 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13312 // If the partitioned service worker is enabled, service worker is allowed to
13313 // control the window if partition is enabled.
13314 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13315 RefPtr<Document> doc = parentInner->GetExtantDoc();
13317 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13318 return true;
13322 return storage == StorageAccess::eAllow;
13325 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13326 MOZ_ASSERT(!mIsBeingDestroyed);
13327 return mBrowsingContext->SetOriginAttributes(aAttrs);
13330 NS_IMETHODIMP
13331 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13332 RefPtr<nsDocShell> self = this;
13333 RefPtr<ChildProcessChannelListener> cpcl =
13334 ChildProcessChannelListener::GetSingleton();
13336 // Call into InternalLoad with the pending channel when it is received.
13337 cpcl->RegisterCallback(
13338 aIdentifier, [self, aHistoryIndex](
13339 nsDocShellLoadState* aLoadState,
13340 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13341 aStreamFilterEndpoints,
13342 nsDOMNavigationTiming* aTiming) {
13343 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13344 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13345 aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
13346 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
13347 return NS_BINDING_ABORTED;
13350 self->mLoadType = aLoadState->LoadType();
13351 nsCOMPtr<nsIURI> previousURI;
13352 uint32_t previousFlags = 0;
13353 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13354 getter_AddRefs(previousURI), &previousFlags);
13355 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13356 previousURI, previousFlags);
13358 if (aTiming) {
13359 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13360 self->mBlankTiming = false;
13363 // If we're performing a history load, locate the correct history entry,
13364 // and set the relevant bits on our loadState.
13365 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13366 !mozilla::SessionHistoryInParent()) {
13367 nsCOMPtr<nsISHistory> legacySHistory =
13368 self->GetSessionHistory()->LegacySHistory();
13370 nsCOMPtr<nsISHEntry> entry;
13371 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13372 getter_AddRefs(entry));
13373 if (NS_SUCCEEDED(rv)) {
13374 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13375 aLoadState->SetLoadType(LOAD_HISTORY);
13376 aLoadState->SetSHEntry(entry);
13380 self->InternalLoad(aLoadState);
13382 if (aLoadState->GetOriginalURIString().isSome()) {
13383 // Save URI string in case it's needed later when
13384 // sending to search engine service in EndPageLoad()
13385 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13388 for (auto& endpoint : aStreamFilterEndpoints) {
13389 extensions::StreamFilterParent::Attach(
13390 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13393 // If the channel isn't pending, then it means that InternalLoad
13394 // never connected it, and we shouldn't try to continue. This
13395 // can happen even if InternalLoad returned NS_OK.
13396 bool pending = false;
13397 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13398 NS_ASSERTION(pending, "We should have connected the pending channel!");
13399 if (!pending) {
13400 return NS_BINDING_ABORTED;
13402 return NS_OK;
13404 return NS_OK;
13407 NS_IMETHODIMP
13408 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13409 JSContext* aCx) {
13410 OriginAttributes attrs;
13411 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13412 return NS_ERROR_INVALID_ARG;
13415 return SetOriginAttributes(attrs);
13418 NS_IMETHODIMP
13419 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13420 if (PresShell* presShell = GetPresShell()) {
13421 *aOut = presShell->AsyncPanZoomEnabled();
13422 return NS_OK;
13425 // If we don't have a presShell, fall back to the default platform value of
13426 // whether or not APZ is enabled.
13427 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13428 return NS_OK;
13431 bool nsDocShell::HasUnloadedParent() {
13432 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13433 wc = wc->GetParentWindowContext()) {
13434 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13435 wc->GetBrowsingContext()->IsDiscarded()) {
13436 // If a parent is OOP and the parent WindowContext is no
13437 // longer current, we can assume the parent was unloaded.
13438 return true;
13441 if (wc->GetBrowsingContext()->IsInProcess() &&
13442 (!wc->GetBrowsingContext()->GetDocShell() ||
13443 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13444 return true;
13447 return false;
13450 /* static */
13451 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13452 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13453 aLoadType & LOAD_CMD_HISTORY);
13456 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13457 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13458 return;
13461 // Global history is interested into sub-frame visits only for link-coloring
13462 // purposes, thus title updates are skipped for those.
13464 // Moreover, some iframe documents (such as the ones created via
13465 // document.open()) inherit the document uri of the caller, which would cause
13466 // us to override a previously set page title with one from the subframe.
13467 if (IsSubframe()) {
13468 return;
13471 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13472 history->SetURITitle(aURI, mTitle);
13476 bool nsDocShell::IsInvisible() { return mInvisible; }
13478 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13480 // The caller owns |aAsyncCause| here.
13481 void nsDocShell::NotifyJSRunToCompletionStart(const char* aReason,
13482 const nsAString& aFunctionName,
13483 const nsAString& aFilename,
13484 const uint32_t aLineNumber,
13485 JS::Handle<JS::Value> aAsyncStack,
13486 const char* aAsyncCause) {
13487 // If first start, mark interval start.
13488 if (mJSRunToCompletionDepth == 0 && TimelineConsumers::HasConsumer(this)) {
13489 TimelineConsumers::AddMarkerForDocShell(
13490 this, mozilla::MakeUnique<JavascriptTimelineMarker>(
13491 aReason, aFunctionName, aFilename, aLineNumber,
13492 MarkerTracingType::START, aAsyncStack, aAsyncCause));
13495 mJSRunToCompletionDepth++;
13498 void nsDocShell::NotifyJSRunToCompletionStop() {
13499 mJSRunToCompletionDepth--;
13501 // If last stop, mark interval end.
13502 if (mJSRunToCompletionDepth == 0 && TimelineConsumers::HasConsumer(this)) {
13503 TimelineConsumers::AddMarkerForDocShell(this, "Javascript",
13504 MarkerTracingType::END);
13508 /* static */
13509 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13510 const nsString& aKeyword) {
13511 if (aProvider.IsEmpty()) {
13512 return;
13514 nsresult rv;
13515 nsCOMPtr<nsISupportsString> isupportsString =
13516 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13517 NS_ENSURE_SUCCESS_VOID(rv);
13519 rv = isupportsString->SetData(aProvider);
13520 NS_ENSURE_SUCCESS_VOID(rv);
13522 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13523 if (obsSvc) {
13524 // Note that "keyword-search" refers to a search via the url
13525 // bar, not a bookmarks keyword search.
13526 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13530 NS_IMETHODIMP
13531 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13532 bool* aShouldIntercept) {
13533 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13534 aShouldIntercept);
13537 NS_IMETHODIMP
13538 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13539 return mInterceptController->ChannelIntercepted(aChannel);
13542 bool nsDocShell::InFrameSwap() {
13543 RefPtr<nsDocShell> shell = this;
13544 do {
13545 if (shell->mInFrameSwap) {
13546 return true;
13548 shell = shell->GetInProcessParentDocshell();
13549 } while (shell);
13550 return false;
13553 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13554 return std::move(mInitialClientSource);
13557 NS_IMETHODIMP
13558 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13559 if (!NS_SUCCEEDED(EnsureEditorData())) {
13560 return NS_ERROR_FAILURE;
13563 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13564 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13567 NS_IMETHODIMP
13568 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13569 *aBrowserChild = GetBrowserChild().take();
13570 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13573 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13574 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13575 return tc.forget();
13578 nsCommandManager* nsDocShell::GetCommandManager() {
13579 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13580 return mCommandManager;
13583 NS_IMETHODIMP_(void)
13584 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13585 mBrowsingContext->GetOriginAttributes(aAttrs);
13588 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13589 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13590 return docShell->GetHTMLEditorInternal();
13593 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13594 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13595 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13598 #define MATRIX_LENGTH 20
13600 NS_IMETHODIMP
13601 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13602 if (aMatrix.Length() == MATRIX_LENGTH) {
13603 mColorMatrix.reset(new gfx::Matrix5x4());
13604 static_assert(
13605 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13606 "Size mismatch for our memcpy");
13607 memcpy(mColorMatrix->components, aMatrix.Elements(),
13608 sizeof(mColorMatrix->components));
13609 } else if (aMatrix.Length() == 0) {
13610 mColorMatrix.reset();
13611 } else {
13612 return NS_ERROR_INVALID_ARG;
13615 PresShell* presShell = GetPresShell();
13616 if (!presShell) {
13617 return NS_ERROR_FAILURE;
13620 nsIFrame* frame = presShell->GetRootFrame();
13621 if (!frame) {
13622 return NS_ERROR_FAILURE;
13625 frame->SchedulePaint();
13627 return NS_OK;
13630 NS_IMETHODIMP
13631 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13632 if (mColorMatrix) {
13633 aMatrix.SetLength(MATRIX_LENGTH);
13634 static_assert(
13635 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13636 "Size mismatch for our memcpy");
13637 memcpy(aMatrix.Elements(), mColorMatrix->components,
13638 MATRIX_LENGTH * sizeof(float));
13641 return NS_OK;
13644 #undef MATRIX_LENGTH
13646 NS_IMETHODIMP
13647 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13648 *aForceReload = IsForceReloading();
13649 return NS_OK;
13652 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13654 NS_IMETHODIMP
13655 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13656 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13657 return NS_OK;
13660 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13662 bool nsDocShell::GetIsAttemptingToNavigate() {
13663 // XXXbz the document.open spec says to abort even if there's just a
13664 // queued navigation task, sort of. It's not clear whether browsers
13665 // actually do that, and we didn't use to do it, so for now let's
13666 // not do that.
13667 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13668 if (mDocumentRequest) {
13669 // There's definitely a navigation in progress.
13670 return true;
13673 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13674 // until the script runs, which means they're not sending loadgroup
13675 // notifications and hence not getting set as mDocumentRequest. Look through
13676 // our loadgroup for document-level javascript: loads.
13677 if (!mLoadGroup) {
13678 return false;
13681 nsCOMPtr<nsISimpleEnumerator> requests;
13682 mLoadGroup->GetRequests(getter_AddRefs(requests));
13683 bool hasMore = false;
13684 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13685 nsCOMPtr<nsISupports> elem;
13686 requests->GetNext(getter_AddRefs(elem));
13687 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13688 if (!scriptChannel) {
13689 continue;
13692 if (scriptChannel->GetIsDocumentLoad()) {
13693 // This is a javascript: load that might lead to a new document,
13694 // hence a navigation.
13695 return true;
13699 return mCheckingSessionHistory;
13702 void nsDocShell::SetLoadingSessionHistoryInfo(
13703 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
13704 bool aNeedToReportActiveAfterLoadingBecomesActive) {
13705 // FIXME Would like to assert this, but can't yet.
13706 // MOZ_ASSERT(!mLoadingEntry);
13707 MOZ_LOG(gSHLog, LogLevel::Debug,
13708 ("Setting the loading entry on nsDocShell %p to %s", this,
13709 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13710 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13711 mNeedToReportActiveAfterLoadingBecomesActive =
13712 aNeedToReportActiveAfterLoadingBecomesActive;
13715 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13716 uint32_t aCacheKey,
13717 nsIURI* aPreviousURI) {
13718 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13720 MOZ_LOG(gSHLog, LogLevel::Debug,
13721 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13723 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
13724 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13725 mActiveEntryIsLoadingFromSessionHistory =
13726 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13727 if (mLoadingEntry) {
13728 MOZ_LOG(gSHLog, LogLevel::Debug,
13729 ("Moving the loading entry to the active entry on nsDocShell %p "
13730 "to %s",
13731 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13732 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13733 mLoadingEntry.swap(loadingEntry);
13734 if (!mActiveEntryIsLoadingFromSessionHistory) {
13735 if (mNeedToReportActiveAfterLoadingBecomesActive) {
13736 // Needed to pass various history length WPTs.
13737 mBrowsingContext->SetActiveSessionHistoryEntry(
13738 mozilla::Nothing(), mActiveEntry.get(), mLoadType,
13739 /* aUpdatedCacheKey = */ 0, false);
13741 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13744 mNeedToReportActiveAfterLoadingBecomesActive = false;
13746 if (mActiveEntry) {
13747 if (aCacheKey != 0) {
13748 mActiveEntry->SetCacheKey(aCacheKey);
13750 MOZ_ASSERT(loadingEntry);
13751 uint32_t loadType =
13752 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13754 if (loadingEntry->mLoadId != UINT64_MAX) {
13755 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13756 // does require a non-null uri if this is for a refresh load of the same
13757 // URI, but in that case mCurrentURI won't be null here.
13758 mBrowsingContext->SessionHistoryCommit(
13759 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
13760 aPersist, false, aExpired, aCacheKey);
13765 static bool IsFaviconLoad(nsIRequest* aRequest) {
13766 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13767 if (!channel) {
13768 return false;
13771 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13772 return li && li->InternalContentPolicyType() ==
13773 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13776 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13777 nsIRequest* aRequest) {
13778 // Ignore favicon loads, they don't need to block caching.
13779 if (IsFaviconLoad(aRequest)) {
13780 return;
13783 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13785 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13787 if (mBrowsingContext->GetCurrentWindowContext()) {
13788 // We have three states: no request, one request with an id and
13789 // eiher one request without an id or multiple requests. Nothing() is no
13790 // request, Some(non-zero) is one request with an id and Some(0) is one
13791 // request without an id or multiple requests.
13792 Maybe<uint64_t> singleChannelId;
13793 if (mRequestForBlockingFromBFCacheCount > 1) {
13794 singleChannelId = Some(0);
13795 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13796 nsCOMPtr<nsIIdentChannel> identChannel;
13797 if (aStartRequest) {
13798 identChannel = do_QueryInterface(aRequest);
13799 } else {
13800 // aChannel is the channel that's being removed, but we need to check if
13801 // the remaining channel in the loadgroup has an id.
13802 nsCOMPtr<nsISimpleEnumerator> requests;
13803 mLoadGroup->GetRequests(getter_AddRefs(requests));
13804 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13805 if (!IsFaviconLoad(request) &&
13806 !!(identChannel = do_QueryInterface(request))) {
13807 break;
13812 if (identChannel) {
13813 singleChannelId = Some(identChannel->ChannelId());
13814 } else {
13815 singleChannelId = Some(0);
13817 } else {
13818 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13819 singleChannelId = Nothing();
13822 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13823 nsAutoCString uri("[no uri]");
13824 if (mCurrentURI) {
13825 uri = mCurrentURI->GetSpecOrDefault();
13827 if (singleChannelId.isNothing()) {
13828 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13829 ("Loadgroup for %s doesn't have any requests relevant for "
13830 "blocking BFCache",
13831 uri.get()));
13832 } else if (singleChannelId.value() == 0) {
13833 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13834 ("Loadgroup for %s has multiple requests relevant for blocking "
13835 "BFCache",
13836 uri.get()));
13837 } else {
13838 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13839 ("Loadgroup for %s has one request with id %" PRIu64
13840 " relevant for blocking BFCache",
13841 uri.get(), singleChannelId.value()));
13845 if (mSingleChannelId != singleChannelId) {
13846 mSingleChannelId = singleChannelId;
13847 WindowGlobalChild* wgc =
13848 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13849 if (wgc) {
13850 wgc->SendSetSingleChannelId(singleChannelId);
13856 NS_IMETHODIMP
13857 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13858 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13859 nsAutoCString uri("[no uri]");
13860 if (mCurrentURI) {
13861 uri = mCurrentURI->GetSpecOrDefault();
13863 nsAutoCString name;
13864 aRequest->GetName(name);
13865 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13866 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13868 RecordSingleChannelId(true, aRequest);
13869 return nsDocLoader::OnStartRequest(aRequest);
13872 NS_IMETHODIMP
13873 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13874 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13875 nsAutoCString uri("[no uri]");
13876 if (mCurrentURI) {
13877 uri = mCurrentURI->GetSpecOrDefault();
13879 nsAutoCString name;
13880 aRequest->GetName(name);
13881 MOZ_LOG(
13882 gSHIPBFCacheLog, LogLevel::Verbose,
13883 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13885 RecordSingleChannelId(false, aRequest);
13886 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13889 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13890 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13892 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13893 nsCOMPtr<nsISimpleEnumerator> requests;
13894 mLoadGroup->GetRequests(getter_AddRefs(requests));
13895 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13896 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13897 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13898 static_cast<DocumentChannelChild*>(channel.get())
13899 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13902 mChannelToDisconnectOnPageHide = 0;