Backed out 2 changesets (bug 1908320) for causing wr failures on align-items-baseline...
[gecko.git] / docshell / base / nsDocShell.cpp
blob84cfe13e2083faf283cfa44558c86517c9cda90f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsDocShell.h"
9 #include <algorithm>
11 #ifdef XP_WIN
12 # include <process.h>
13 # define getpid _getpid
14 #else
15 # include <unistd.h> // for getpid()
16 #endif
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/AutoRestore.h"
21 #include "mozilla/BasePrincipal.h"
22 #include "mozilla/Casting.h"
23 #include "mozilla/CheckedInt.h"
24 #include "mozilla/Components.h"
25 #include "mozilla/DebugOnly.h"
26 #include "mozilla/Encoding.h"
27 #include "mozilla/EventStateManager.h"
28 #include "mozilla/HTMLEditor.h"
29 #include "mozilla/InputTaskManager.h"
30 #include "mozilla/LoadInfo.h"
31 #include "mozilla/Logging.h"
32 #include "mozilla/MediaFeatureChange.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/PresShell.h"
35 #include "mozilla/ResultExtensions.h"
36 #include "mozilla/SchedulerGroup.h"
37 #include "mozilla/ScopeExit.h"
38 #include "mozilla/ScrollContainerFrame.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/FragmentDirective.h"
69 #include "mozilla/dom/HTMLAnchorElement.h"
70 #include "mozilla/dom/HTMLIFrameElement.h"
71 #include "mozilla/dom/PerformanceNavigation.h"
72 #include "mozilla/dom/PermissionMessageUtils.h"
73 #include "mozilla/dom/PopupBlocker.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 "nsIController.h"
111 #include "nsIDocShellTreeItem.h"
112 #include "nsIDocShellTreeOwner.h"
113 #include "nsIDocumentViewer.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 "nsScriptSecurityManager.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 "nsIWebBrowserFind.h"
172 #include "nsIWebProgress.h"
173 #include "nsIWidget.h"
174 #include "nsIWindowWatcher.h"
175 #include "nsIWritablePropertyBag2.h"
176 #include "nsIX509Cert.h"
177 #include "nsIXULRuntime.h"
179 #include "nsCommandManager.h"
180 #include "nsPIDOMWindow.h"
181 #include "nsPIWindowRoot.h"
183 #include "IHistory.h"
184 #include "IUrlClassifierUITelemetry.h"
186 #include "nsArray.h"
187 #include "nsArrayUtils.h"
188 #include "nsCExternalHandlerService.h"
189 #include "nsContentDLF.h"
190 #include "nsContentPolicyUtils.h" // NS_CheckContentLoadPolicy(...)
191 #include "nsContentSecurityManager.h"
192 #include "nsContentSecurityUtils.h"
193 #include "nsContentUtils.h"
194 #include "nsCURILoader.h"
195 #include "nsDocShellCID.h"
196 #include "nsDocShellEditorData.h"
197 #include "nsDocShellEnumerator.h"
198 #include "nsDocShellLoadState.h"
199 #include "nsDocShellLoadTypes.h"
200 #include "nsDOMCID.h"
201 #include "nsDOMNavigationTiming.h"
202 #include "nsDSURIContentListener.h"
203 #include "nsEditingSession.h"
204 #include "nsError.h"
205 #include "nsEscape.h"
206 #include "nsFocusManager.h"
207 #include "nsGlobalWindowInner.h"
208 #include "nsGlobalWindowOuter.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 "CertVerifier.h"
236 #include "ThirdPartyUtil.h"
237 #include "GeckoProfiler.h"
238 #include "mozilla/NullPrincipal.h"
239 #include "Navigator.h"
240 #include "prenv.h"
241 #include "mozilla/ipc/URIUtils.h"
242 #include "sslerr.h"
243 #include "mozpkix/pkix.h"
244 #include "NSSErrorsService.h"
246 #include "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 mChannelToDisconnectOnPageHide(0),
338 mCreatingDocument(false),
339 #ifdef DEBUG
340 mInEnsureScriptEnv(false),
341 #endif
342 mInitialized(false),
343 mAllowSubframes(true),
344 mAllowMetaRedirects(true),
345 mAllowImages(true),
346 mAllowMedia(true),
347 mAllowDNSPrefetch(true),
348 mAllowWindowControl(true),
349 mCSSErrorReportingEnabled(false),
350 mAllowAuth(mItemType == typeContent),
351 mAllowKeywordFixup(false),
352 mDisableMetaRefreshWhenInactive(false),
353 mWindowDraggingAllowed(false),
354 mInFrameSwap(false),
355 mFiredUnloadEvent(false),
356 mEODForCurrentDocument(false),
357 mURIResultedInDocument(false),
358 mIsBeingDestroyed(false),
359 mIsExecutingOnLoadHandler(false),
360 mSavingOldViewer(false),
361 mInvisible(false),
362 mHasLoadedNonBlankURI(false),
363 mBlankTiming(false),
364 mTitleValidForCurrentURI(false),
365 mWillChangeProcess(false),
366 mIsNavigating(false),
367 mForcedAutodetection(false),
368 mCheckingSessionHistory(false),
369 mNeedToReportActiveAfterLoadingBecomesActive(false) {
370 // If no outer window ID was provided, generate a new one.
371 if (aContentWindowID == 0) {
372 mContentWindowID = nsContentUtils::GenerateWindowId();
375 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p created\n", this));
377 #ifdef DEBUG
378 mDocShellID = gDocshellIDCounter++;
379 // We're counting the number of |nsDocShells| to help find leaks
380 ++gNumberOfDocShells;
381 MOZ_LOG(gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
382 ("++DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "]\n", (void*)this,
383 gNumberOfDocShells, getpid(), mDocShellID));
384 #endif
387 nsDocShell::~nsDocShell() {
388 // Avoid notifying observers while we're in the dtor.
389 mIsBeingDestroyed = true;
391 Destroy();
393 if (mDocumentViewer) {
394 mDocumentViewer->Close(nullptr);
395 mDocumentViewer->Destroy();
396 mDocumentViewer = nullptr;
399 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug, ("DOCSHELL %p destroyed\n", this));
401 #ifdef DEBUG
402 if (MOZ_LOG_TEST(gDocShellAndDOMWindowLeakLogging, LogLevel::Info)) {
403 nsAutoCString url;
404 if (mLastOpenedURI) {
405 url = mLastOpenedURI->GetSpecOrDefault();
407 // Data URLs can be very long, so truncate to avoid flooding the log.
408 const uint32_t maxURLLength = 1000;
409 if (url.Length() > maxURLLength) {
410 url.Truncate(maxURLLength);
414 // We're counting the number of |nsDocShells| to help find leaks
415 --gNumberOfDocShells;
416 MOZ_LOG(
417 gDocShellAndDOMWindowLeakLogging, LogLevel::Info,
418 ("--DOCSHELL %p == %ld [pid = %d] [id = %" PRIu64 "] [url = %s]\n",
419 (void*)this, gNumberOfDocShells, getpid(), mDocShellID, url.get()));
421 #endif
424 bool nsDocShell::Initialize() {
425 if (mInitialized) {
426 // We've already been initialized.
427 return true;
430 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
431 "Unexpected item type in docshell");
433 NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
434 mInitialized = true;
436 mDisableMetaRefreshWhenInactive =
437 Preferences::GetBool("browser.meta_refresh_when_inactive.disabled",
438 mDisableMetaRefreshWhenInactive);
440 if (nsCOMPtr<nsIObserverService> serv = services::GetObserverService()) {
441 const char* msg = mItemType == typeContent ? NS_WEBNAVIGATION_CREATE
442 : NS_CHROME_WEBNAVIGATION_CREATE;
443 serv->NotifyWhenScriptSafe(GetAsSupports(this), msg, nullptr);
446 return true;
449 /* static */
450 already_AddRefed<nsDocShell> nsDocShell::Create(
451 BrowsingContext* aBrowsingContext, uint64_t aContentWindowID) {
452 MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
454 nsresult rv;
455 RefPtr<nsDocShell> ds = new nsDocShell(aBrowsingContext, aContentWindowID);
457 // Initialize the underlying nsDocLoader.
458 rv = ds->nsDocLoader::InitWithBrowsingContext(aBrowsingContext);
459 if (NS_WARN_IF(NS_FAILED(rv))) {
460 return nullptr;
463 // Create our ContentListener
464 ds->mContentListener = new nsDSURIContentListener(ds);
466 // We enable if we're in the parent process in order to support non-e10s
467 // configurations.
468 // Note: This check is duplicated in SharedWorkerInterfaceRequestor's
469 // constructor.
470 if (XRE_IsParentProcess()) {
471 ds->mInterceptController = new ServiceWorkerInterceptController();
474 // We want to hold a strong ref to the loadgroup, so it better hold a weak
475 // ref to us... use an InterfaceRequestorProxy to do this.
476 nsCOMPtr<nsIInterfaceRequestor> proxy = new InterfaceRequestorProxy(ds);
477 ds->mLoadGroup->SetNotificationCallbacks(proxy);
479 // XXX(nika): We have our BrowsingContext, so we might be able to skip this.
480 // It could be nice to directly set up our DocLoader tree?
481 rv = nsDocLoader::AddDocLoaderAsChildOfRoot(ds);
482 if (NS_WARN_IF(NS_FAILED(rv))) {
483 return nullptr;
486 // Add |ds| as a progress listener to itself. A little weird, but simpler
487 // than reproducing all the listener-notification logic in overrides of the
488 // various methods via which nsDocLoader can be notified. Note that this
489 // holds an nsWeakPtr to |ds|, so it's ok.
490 rv = ds->AddProgressListener(ds, nsIWebProgress::NOTIFY_STATE_DOCUMENT |
491 nsIWebProgress::NOTIFY_STATE_NETWORK |
492 nsIWebProgress::NOTIFY_LOCATION);
493 if (NS_WARN_IF(NS_FAILED(rv))) {
494 return nullptr;
497 // If our BrowsingContext has private browsing enabled, update the number of
498 // private browsing docshells.
499 if (aBrowsingContext->UsePrivateBrowsing()) {
500 ds->NotifyPrivateBrowsingChanged();
503 // If our parent window is present in this process, set up our parent now.
504 RefPtr<WindowContext> parentWC = aBrowsingContext->GetParentWindowContext();
505 if (parentWC && parentWC->IsInProcess()) {
506 // If we don't have a parent element anymore, we can't finish this load!
507 // How'd we get here?
508 RefPtr<Element> parentElement = aBrowsingContext->GetEmbedderElement();
509 if (!parentElement) {
510 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentElement");
511 return nullptr;
514 // We have an in-process parent window, but don't have a parent nsDocShell?
515 // How'd we get here!
516 nsCOMPtr<nsIDocShell> parentShell =
517 parentElement->OwnerDoc()->GetDocShell();
518 if (!parentShell) {
519 MOZ_ASSERT_UNREACHABLE("nsDocShell::Create() - !parentShell");
520 return nullptr;
522 parentShell->AddChild(ds);
525 // Make |ds| the primary DocShell for the given context.
526 aBrowsingContext->SetDocShell(ds);
528 // Set |ds| default load flags on load group.
529 ds->SetLoadGroupDefaultLoadFlags(aBrowsingContext->GetDefaultLoadFlags());
531 if (XRE_IsParentProcess()) {
532 aBrowsingContext->Canonical()->MaybeAddAsProgressListener(ds);
535 return ds.forget();
538 void nsDocShell::DestroyChildren() {
539 for (auto* child : mChildList.ForwardRange()) {
540 nsCOMPtr<nsIDocShellTreeItem> shell = do_QueryObject(child);
541 NS_ASSERTION(shell, "docshell has null child");
543 if (shell) {
544 shell->SetTreeOwner(nullptr);
548 nsDocLoader::DestroyChildren();
551 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsDocShell, nsDocLoader,
552 mScriptGlobal, mInitialClientSource,
553 mBrowsingContext,
554 mChromeEventHandler)
556 NS_IMPL_ADDREF_INHERITED(nsDocShell, nsDocLoader)
557 NS_IMPL_RELEASE_INHERITED(nsDocShell, nsDocLoader)
559 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDocShell)
560 NS_INTERFACE_MAP_ENTRY(nsIDocShell)
561 NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
562 NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
563 NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
564 NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
565 NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
566 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
567 NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
568 NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
569 NS_INTERFACE_MAP_ENTRY(nsILoadContext)
570 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsINetworkInterceptController,
571 mInterceptController)
572 NS_INTERFACE_MAP_END_INHERITING(nsDocLoader)
574 NS_IMETHODIMP
575 nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
576 MOZ_ASSERT(aSink, "null out param");
578 *aSink = nullptr;
580 if (aIID.Equals(NS_GET_IID(nsICommandManager))) {
581 NS_ENSURE_SUCCESS(EnsureCommandHandler(), NS_ERROR_FAILURE);
582 *aSink = static_cast<nsICommandManager*>(mCommandManager.get());
583 } else if (aIID.Equals(NS_GET_IID(nsIURIContentListener))) {
584 *aSink = mContentListener;
585 } else if ((aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) ||
586 aIID.Equals(NS_GET_IID(nsIGlobalObject)) ||
587 aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter)) ||
588 aIID.Equals(NS_GET_IID(mozIDOMWindowProxy)) ||
589 aIID.Equals(NS_GET_IID(nsIDOMWindow))) &&
590 NS_SUCCEEDED(EnsureScriptEnvironment())) {
591 return mScriptGlobal->QueryInterface(aIID, aSink);
592 } else if (aIID.Equals(NS_GET_IID(Document)) &&
593 NS_SUCCEEDED(EnsureDocumentViewer())) {
594 RefPtr<Document> doc = mDocumentViewer->GetDocument();
595 doc.forget(aSink);
596 return *aSink ? NS_OK : NS_NOINTERFACE;
597 } else if (aIID.Equals(NS_GET_IID(nsIPrompt)) &&
598 NS_SUCCEEDED(EnsureScriptEnvironment())) {
599 nsresult rv;
600 nsCOMPtr<nsIWindowWatcher> wwatch =
601 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
602 NS_ENSURE_SUCCESS(rv, rv);
604 // Get the an auth prompter for our window so that the parenting
605 // of the dialogs works as it should when using tabs.
606 nsIPrompt* prompt;
607 rv = wwatch->GetNewPrompter(mScriptGlobal, &prompt);
608 NS_ENSURE_SUCCESS(rv, rv);
610 *aSink = prompt;
611 return NS_OK;
612 } else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
613 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
614 return NS_SUCCEEDED(GetAuthPrompt(PROMPT_NORMAL, aIID, aSink))
615 ? NS_OK
616 : NS_NOINTERFACE;
617 } else if (aIID.Equals(NS_GET_IID(nsISHistory))) {
618 // This is deprecated, you should instead directly get
619 // ChildSHistory from the browsing context.
620 MOZ_DIAGNOSTIC_ASSERT(
621 false, "Do not try to get a nsISHistory interface from nsIDocShell");
622 return NS_NOINTERFACE;
623 } else if (aIID.Equals(NS_GET_IID(nsIWebBrowserFind))) {
624 nsresult rv = EnsureFind();
625 if (NS_FAILED(rv)) {
626 return rv;
629 *aSink = mFind;
630 NS_ADDREF((nsISupports*)*aSink);
631 return NS_OK;
632 } else if (aIID.Equals(NS_GET_IID(nsISelectionDisplay))) {
633 if (PresShell* presShell = GetPresShell()) {
634 return presShell->QueryInterface(aIID, aSink);
636 } else if (aIID.Equals(NS_GET_IID(nsIDocShellTreeOwner))) {
637 nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
638 nsresult rv = GetTreeOwner(getter_AddRefs(treeOwner));
639 if (NS_SUCCEEDED(rv) && treeOwner) {
640 return treeOwner->QueryInterface(aIID, aSink);
642 } else if (aIID.Equals(NS_GET_IID(nsIBrowserChild))) {
643 *aSink = GetBrowserChild().take();
644 return *aSink ? NS_OK : NS_ERROR_FAILURE;
645 } else {
646 return nsDocLoader::GetInterface(aIID, aSink);
649 NS_IF_ADDREF(((nsISupports*)*aSink));
650 return *aSink ? NS_OK : NS_NOINTERFACE;
653 NS_IMETHODIMP
654 nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
655 // Note: this gets called fairly early (before a pageload actually starts).
656 // We could probably defer this even longer.
657 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
658 static_cast<BrowserChild*>(browserChild.get())
659 ->SetCancelContentJSEpoch(aEpoch);
660 return NS_OK;
663 nsresult nsDocShell::CheckDisallowedJavascriptLoad(
664 nsDocShellLoadState* aLoadState) {
665 if (!net::SchemeIsJavascript(aLoadState->URI())) {
666 return NS_OK;
669 if (nsCOMPtr<nsIPrincipal> targetPrincipal =
670 GetInheritedPrincipal(/* aConsiderCurrentDocument */ true)) {
671 if (!aLoadState->TriggeringPrincipal()->Subsumes(targetPrincipal)) {
672 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
674 return NS_OK;
676 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI;
679 NS_IMETHODIMP
680 nsDocShell::LoadURI(nsDocShellLoadState* aLoadState, bool aSetNavigating) {
681 return LoadURI(aLoadState, aSetNavigating, false);
684 nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
685 bool aSetNavigating,
686 bool aContinueHandlingSubframeHistory) {
687 MOZ_ASSERT(aLoadState, "Must have a valid load state!");
688 // NOTE: This comparison between what appears to be internal/external load
689 // flags is intentional, as it's ensuring that the caller isn't using any of
690 // the flags reserved for implementations by the `nsIWebNavigation` interface.
691 // In the future, this check may be dropped.
692 MOZ_ASSERT(
693 (aLoadState->LoadFlags() & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0,
694 "Should not have these flags set");
695 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
696 "Targeting doesn't occur until InternalLoad");
698 if (!aLoadState->TriggeringPrincipal()) {
699 MOZ_ASSERT(false, "LoadURI must have a triggering principal");
700 return NS_ERROR_FAILURE;
703 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
705 bool oldIsNavigating = mIsNavigating;
706 auto cleanupIsNavigating =
707 MakeScopeExit([&]() { mIsNavigating = oldIsNavigating; });
708 if (aSetNavigating) {
709 mIsNavigating = true;
712 PopupBlocker::PopupControlState popupState = PopupBlocker::openOverridden;
713 if (aLoadState->HasLoadFlags(LOAD_FLAGS_ALLOW_POPUPS)) {
714 popupState = PopupBlocker::openAllowed;
715 // If we allow popups as part of the navigation, ensure we fake a user
716 // interaction, so that popups can, in fact, be allowed to open.
717 if (WindowContext* wc = mBrowsingContext->GetCurrentWindowContext()) {
718 wc->NotifyUserGestureActivation();
722 AutoPopupStatePusher statePusher(popupState);
724 if (aLoadState->GetCancelContentJSEpoch().isSome()) {
725 SetCancelContentJSEpoch(*aLoadState->GetCancelContentJSEpoch());
728 // Note: we allow loads to get through here even if mFiredUnloadEvent is
729 // true; that case will get handled in LoadInternal or LoadHistoryEntry,
730 // so we pass false as the second parameter to IsNavigationAllowed.
731 // However, we don't allow the page to change location *in the middle of*
732 // firing beforeunload, so we do need to check if *beforeunload* is currently
733 // firing, so we call IsNavigationAllowed rather than just IsPrintingOrPP.
734 if (!IsNavigationAllowed(true, false)) {
735 return NS_OK; // JS may not handle returning of an error code
738 nsLoadFlags defaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
739 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FORCE_TRR)) {
740 defaultLoadFlags |= nsIRequest::LOAD_TRR_ONLY_MODE;
741 } else if (aLoadState->HasLoadFlags(LOAD_FLAGS_DISABLE_TRR)) {
742 defaultLoadFlags |= nsIRequest::LOAD_TRR_DISABLED_MODE;
745 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetDefaultLoadFlags(defaultLoadFlags));
747 if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
748 mItemType == typeContent && !NS_IsAboutBlank(aLoadState->URI())) {
749 StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
752 // LoadType used to be set to a default value here, if no LoadInfo/LoadState
753 // object was passed in. That functionality has been removed as of bug
754 // 1492648. LoadType should now be set up by the caller at the time they
755 // create their nsDocShellLoadState object to pass into LoadURI.
757 MOZ_LOG(
758 gDocShellLeakLog, LogLevel::Debug,
759 ("nsDocShell[%p]: loading %s with flags 0x%08x", this,
760 aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
762 if ((!aLoadState->LoadIsFromSessionHistory() &&
763 !LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
764 LOAD_FLAGS_REPLACE_HISTORY)) ||
765 aContinueHandlingSubframeHistory) {
766 // This is possibly a subframe, so handle it accordingly.
768 // If history exists, it will be loaded into the aLoadState object, and the
769 // LoadType will be changed.
770 if (MaybeHandleSubframeHistory(aLoadState,
771 aContinueHandlingSubframeHistory)) {
772 // MaybeHandleSubframeHistory returns true if we need to continue loading
773 // asynchronously.
774 return NS_OK;
778 if (aLoadState->LoadIsFromSessionHistory()) {
779 MOZ_LOG(gSHLog, LogLevel::Debug,
780 ("nsDocShell[%p]: loading from session history", this));
782 if (!mozilla::SessionHistoryInParent()) {
783 nsCOMPtr<nsISHEntry> entry = aLoadState->SHEntry();
784 return LoadHistoryEntry(entry, aLoadState->LoadType(),
785 aLoadState->HasValidUserGestureActivation());
788 // FIXME Null check aLoadState->GetLoadingSessionHistoryInfo()?
789 return LoadHistoryEntry(*aLoadState->GetLoadingSessionHistoryInfo(),
790 aLoadState->LoadType(),
791 aLoadState->HasValidUserGestureActivation());
794 // On history navigation via Back/Forward buttons, don't execute
795 // automatic JavaScript redirection such as |location.href = ...| or
796 // |window.open()|
798 // LOAD_NORMAL: window.open(...) etc.
799 // LOAD_STOP_CONTENT: location.href = ..., location.assign(...)
800 if ((aLoadState->LoadType() == LOAD_NORMAL ||
801 aLoadState->LoadType() == LOAD_STOP_CONTENT) &&
802 ShouldBlockLoadingForBackButton()) {
803 return NS_OK;
806 BrowsingContext::Type bcType = mBrowsingContext->GetType();
808 // Set up the inheriting principal in LoadState.
809 nsresult rv = aLoadState->SetupInheritingPrincipal(
810 bcType, mBrowsingContext->OriginAttributesRef());
811 NS_ENSURE_SUCCESS(rv, rv);
813 rv = aLoadState->SetupTriggeringPrincipal(
814 mBrowsingContext->OriginAttributesRef());
815 NS_ENSURE_SUCCESS(rv, rv);
817 aLoadState->CalculateLoadURIFlags();
819 MOZ_ASSERT(aLoadState->TypeHint().IsVoid(),
820 "Typehint should be null when calling InternalLoad from LoadURI");
821 MOZ_ASSERT(aLoadState->FileName().IsVoid(),
822 "FileName should be null when calling InternalLoad from LoadURI");
823 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory(),
824 "Shouldn't be loading from an entry when calling InternalLoad "
825 "from LoadURI");
827 // If we have a system triggering principal, we can assume that this load was
828 // triggered by some UI in the browser chrome, such as the URL bar or
829 // bookmark bar. This should count as a user interaction for the current sh
830 // entry, so that the user may navigate back to the current entry, from the
831 // entry that is going to be added as part of this load.
832 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
833 aLoadState->TriggeringPrincipal();
834 if (triggeringPrincipal && triggeringPrincipal->IsSystemPrincipal()) {
835 if (mozilla::SessionHistoryInParent()) {
836 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
837 if (topWc && !topWc->IsDiscarded()) {
838 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(true));
840 } else {
841 bool oshe = false;
842 nsCOMPtr<nsISHEntry> currentSHEntry;
843 GetCurrentSHEntry(getter_AddRefs(currentSHEntry), &oshe);
844 if (currentSHEntry) {
845 currentSHEntry->SetHasUserInteraction(true);
850 rv = InternalLoad(aLoadState);
851 NS_ENSURE_SUCCESS(rv, rv);
853 if (aLoadState->GetOriginalURIString().isSome()) {
854 // Save URI string in case it's needed later when
855 // sending to search engine service in EndPageLoad()
856 mOriginalUriString = *aLoadState->GetOriginalURIString();
859 return NS_OK;
862 bool nsDocShell::IsLoadingFromSessionHistory() {
863 return mActiveEntryIsLoadingFromSessionHistory;
866 // StopDetector is modeled similarly to OnloadBlocker; it is a rather
867 // dummy nsIRequest implementation which can be added to an nsILoadGroup to
868 // detect Cancel calls.
869 class StopDetector final : public nsIRequest {
870 public:
871 StopDetector() = default;
873 NS_DECL_ISUPPORTS
874 NS_DECL_NSIREQUEST
876 bool Canceled() { return mCanceled; }
878 private:
879 ~StopDetector() = default;
881 bool mCanceled = false;
884 NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
886 NS_IMETHODIMP
887 StopDetector::GetName(nsACString& aResult) {
888 aResult.AssignLiteral("about:stop-detector");
889 return NS_OK;
892 NS_IMETHODIMP
893 StopDetector::IsPending(bool* aRetVal) {
894 *aRetVal = true;
895 return NS_OK;
898 NS_IMETHODIMP
899 StopDetector::GetStatus(nsresult* aStatus) {
900 *aStatus = NS_OK;
901 return NS_OK;
904 NS_IMETHODIMP StopDetector::SetCanceledReason(const nsACString& aReason) {
905 return SetCanceledReasonImpl(aReason);
908 NS_IMETHODIMP StopDetector::GetCanceledReason(nsACString& aReason) {
909 return GetCanceledReasonImpl(aReason);
912 NS_IMETHODIMP StopDetector::CancelWithReason(nsresult aStatus,
913 const nsACString& aReason) {
914 return CancelWithReasonImpl(aStatus, aReason);
917 NS_IMETHODIMP
918 StopDetector::Cancel(nsresult aStatus) {
919 mCanceled = true;
920 return NS_OK;
923 NS_IMETHODIMP
924 StopDetector::Suspend(void) { return NS_OK; }
925 NS_IMETHODIMP
926 StopDetector::Resume(void) { return NS_OK; }
928 NS_IMETHODIMP
929 StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
930 *aLoadGroup = nullptr;
931 return NS_OK;
934 NS_IMETHODIMP
935 StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
937 NS_IMETHODIMP
938 StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
939 *aLoadFlags = nsIRequest::LOAD_NORMAL;
940 return NS_OK;
943 NS_IMETHODIMP
944 StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
945 return GetTRRModeImpl(aTRRMode);
948 NS_IMETHODIMP
949 StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
950 return SetTRRModeImpl(aTRRMode);
953 NS_IMETHODIMP
954 StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
956 bool nsDocShell::MaybeHandleSubframeHistory(
957 nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
958 // First, verify if this is a subframe.
959 // Note, it is ok to rely on docshell here and not browsing context since when
960 // an iframe is created, it has first in-process docshell.
961 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
962 GetInProcessSameTypeParent(getter_AddRefs(parentAsItem));
963 nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
965 if (!parentDS || parentDS == static_cast<nsIDocShell*>(this)) {
966 if (mBrowsingContext && mBrowsingContext->IsTop()) {
967 // This is the root docshell. If we got here while
968 // executing an onLoad Handler,this load will not go
969 // into session history.
970 // XXX Why is this code in a method which deals with iframes!
971 if (aLoadState->IsFormSubmission()) {
972 #ifdef DEBUG
973 if (!mEODForCurrentDocument) {
974 const MaybeDiscarded<BrowsingContext>& targetBC =
975 aLoadState->TargetBrowsingContext();
976 MOZ_ASSERT_IF(GetBrowsingContext() == targetBC.get(),
977 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
979 #endif
980 } else {
981 bool inOnLoadHandler = false;
982 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
983 if (inOnLoadHandler) {
984 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
988 return false;
991 /* OK. It is a subframe. Checkout the parent's loadtype. If the parent was
992 * loaded through a history mechanism, then get the SH entry for the child
993 * from the parent. This is done to restore frameset navigation while going
994 * back/forward. If the parent was loaded through any other loadType, set the
995 * child's loadType too accordingly, so that session history does not get
996 * confused.
999 // Get the parent's load type
1000 uint32_t parentLoadType;
1001 parentDS->GetLoadType(&parentLoadType);
1003 if (!aContinueHandlingSubframeHistory) {
1004 if (mozilla::SessionHistoryInParent()) {
1005 if (nsDocShell::Cast(parentDS.get())->IsLoadingFromSessionHistory() &&
1006 !GetCreatedDynamically()) {
1007 if (XRE_IsContentProcess()) {
1008 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
1009 nsCOMPtr<nsILoadGroup> loadGroup;
1010 GetLoadGroup(getter_AddRefs(loadGroup));
1011 if (contentChild && loadGroup && !mCheckingSessionHistory) {
1012 RefPtr<Document> parentDoc = parentDS->GetDocument();
1013 parentDoc->BlockOnload();
1014 RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
1015 Maybe<uint64_t> currentLoadIdentifier =
1016 mBrowsingContext->GetCurrentLoadIdentifier();
1017 RefPtr<nsDocShellLoadState> loadState = aLoadState;
1018 bool isNavigating = mIsNavigating;
1019 RefPtr<StopDetector> stopDetector = new StopDetector();
1020 loadGroup->AddRequest(stopDetector, nullptr);
1021 // Need to set mCheckingSessionHistory so that
1022 // GetIsAttemptingToNavigate() returns true.
1023 mCheckingSessionHistory = true;
1025 auto resolve =
1026 [currentLoadIdentifier, browsingContext, parentDoc, loadState,
1027 isNavigating, loadGroup, stopDetector](
1028 mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
1029 RefPtr<nsDocShell> docShell =
1030 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1031 auto unblockParent = MakeScopeExit(
1032 [loadGroup, stopDetector, parentDoc, docShell]() {
1033 if (docShell) {
1034 docShell->mCheckingSessionHistory = false;
1036 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1037 parentDoc->UnblockOnload(false);
1040 if (!docShell || !docShell->mCheckingSessionHistory) {
1041 return;
1044 if (stopDetector->Canceled()) {
1045 return;
1047 if (currentLoadIdentifier ==
1048 browsingContext->GetCurrentLoadIdentifier() &&
1049 aResult.isSome()) {
1050 loadState->SetLoadingSessionHistoryInfo(aResult.value());
1051 // This is an initial subframe load from the session
1052 // history, index doesn't need to be updated.
1053 loadState->SetLoadIsFromSessionHistory(0, false);
1056 // We got the results back from the parent process, call
1057 // LoadURI again with the possibly updated data.
1058 docShell->LoadURI(loadState, isNavigating, true);
1060 auto reject = [loadGroup, stopDetector, browsingContext,
1061 parentDoc](mozilla::ipc::ResponseRejectReason) {
1062 RefPtr<nsDocShell> docShell =
1063 static_cast<nsDocShell*>(browsingContext->GetDocShell());
1064 if (docShell) {
1065 docShell->mCheckingSessionHistory = false;
1067 // In practise reject shouldn't be called ever.
1068 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
1069 parentDoc->UnblockOnload(false);
1071 contentChild->SendGetLoadingSessionHistoryInfoFromParent(
1072 mBrowsingContext, std::move(resolve), std::move(reject));
1073 return true;
1075 } else {
1076 Maybe<LoadingSessionHistoryInfo> info;
1077 mBrowsingContext->Canonical()->GetLoadingSessionHistoryInfoFromParent(
1078 info);
1079 if (info.isSome()) {
1080 aLoadState->SetLoadingSessionHistoryInfo(info.value());
1081 // This is an initial subframe load from the session
1082 // history, index doesn't need to be updated.
1083 aLoadState->SetLoadIsFromSessionHistory(0, false);
1087 } else {
1088 // Get the ShEntry for the child from the parent
1089 nsCOMPtr<nsISHEntry> currentSH;
1090 bool oshe = false;
1091 parentDS->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
1092 bool dynamicallyAddedChild = GetCreatedDynamically();
1094 if (!dynamicallyAddedChild && !oshe && currentSH) {
1095 // Only use the old SHEntry, if we're sure enough that
1096 // it wasn't originally for some other frame.
1097 nsCOMPtr<nsISHEntry> shEntry;
1098 currentSH->GetChildSHEntryIfHasNoDynamicallyAddedChild(
1099 mBrowsingContext->ChildOffset(), getter_AddRefs(shEntry));
1100 if (shEntry) {
1101 aLoadState->SetSHEntry(shEntry);
1107 // Make some decisions on the child frame's loadType based on the
1108 // parent's loadType, if the subframe hasn't loaded anything into it.
1110 // In some cases privileged scripts may try to get the DOMWindow
1111 // reference of this docshell before the loading starts, causing the
1112 // initial about:blank content viewer being created and mCurrentURI being
1113 // set. To handle this case we check if mCurrentURI is about:blank and
1114 // currentSHEntry is null.
1115 bool oshe = false;
1116 nsCOMPtr<nsISHEntry> currentChildEntry;
1117 GetCurrentSHEntry(getter_AddRefs(currentChildEntry), &oshe);
1119 if (mCurrentURI && (!NS_IsAboutBlank(mCurrentURI) || currentChildEntry ||
1120 mLoadingEntry || mActiveEntry)) {
1121 // This is a pre-existing subframe. If
1122 // 1. The load of this frame was not originally initiated by session
1123 // history directly (i.e. (!shEntry) condition succeeded, but it can
1124 // still be a history load on parent which causes this frame being
1125 // loaded), which we checked with the above assert, and
1126 // 2. mCurrentURI is not null, nor the initial about:blank,
1127 // it is possible that a parent's onLoadHandler or even self's
1128 // onLoadHandler is loading a new page in this child. Check parent's and
1129 // self's busy flag and if it is set, we don't want this onLoadHandler
1130 // load to get in to session history.
1131 BusyFlags parentBusy = parentDS->GetBusyFlags();
1132 BusyFlags selfBusy = GetBusyFlags();
1134 if (parentBusy & BUSY_FLAGS_BUSY || selfBusy & BUSY_FLAGS_BUSY) {
1135 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1136 aLoadState->ClearLoadIsFromSessionHistory();
1138 return false;
1141 // This is a newly created frame. Check for exception cases first.
1142 // By default the subframe will inherit the parent's loadType.
1143 if (aLoadState->LoadIsFromSessionHistory() &&
1144 (parentLoadType == LOAD_NORMAL || parentLoadType == LOAD_LINK)) {
1145 // The parent was loaded normally. In this case, this *brand new*
1146 // child really shouldn't have a SHEntry. If it does, it could be
1147 // because the parent is replacing an existing frame with a new frame,
1148 // in the onLoadHandler. We don't want this url to get into session
1149 // history. Clear off shEntry, and set load type to
1150 // LOAD_BYPASS_HISTORY.
1151 bool inOnLoadHandler = false;
1152 parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
1153 if (inOnLoadHandler) {
1154 aLoadState->SetLoadType(LOAD_NORMAL_REPLACE);
1155 aLoadState->ClearLoadIsFromSessionHistory();
1157 } else if (parentLoadType == LOAD_REFRESH) {
1158 // Clear shEntry. For refresh loads, we have to load
1159 // what comes through the pipe, not what's in history.
1160 aLoadState->ClearLoadIsFromSessionHistory();
1161 } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
1162 (aLoadState->LoadIsFromSessionHistory() &&
1163 ((parentLoadType & LOAD_CMD_HISTORY) ||
1164 (parentLoadType == LOAD_RELOAD_NORMAL) ||
1165 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
1166 (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
1167 (parentLoadType ==
1168 LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
1169 // If the parent url, bypassed history or was loaded from
1170 // history, pass on the parent's loadType to the new child
1171 // frame too, so that the child frame will also
1172 // avoid getting into history.
1173 aLoadState->SetLoadType(parentLoadType);
1174 } else if (parentLoadType == LOAD_ERROR_PAGE) {
1175 // If the parent document is an error page, we don't
1176 // want to update global/session history. However,
1177 // this child frame is not an error page.
1178 aLoadState->SetLoadType(LOAD_BYPASS_HISTORY);
1179 } else if ((parentLoadType == LOAD_RELOAD_BYPASS_CACHE) ||
1180 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY) ||
1181 (parentLoadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE)) {
1182 // the new frame should inherit the parent's load type so that it also
1183 // bypasses the cache and/or proxy
1184 aLoadState->SetLoadType(parentLoadType);
1187 return false;
1191 * Reset state to a new content model within the current document and the
1192 * document viewer. Called by the document before initiating an out of band
1193 * document.write().
1195 NS_IMETHODIMP
1196 nsDocShell::PrepareForNewContentModel() {
1197 // Clear out our form control state, because the state of controls
1198 // in the pre-open() document should not affect the state of
1199 // controls that are now going to be written.
1200 SetLayoutHistoryState(nullptr);
1201 mEODForCurrentDocument = false;
1202 return NS_OK;
1205 NS_IMETHODIMP
1206 nsDocShell::FirePageHideNotification(bool aIsUnload) {
1207 FirePageHideNotificationInternal(aIsUnload, false);
1208 return NS_OK;
1211 void nsDocShell::FirePageHideNotificationInternal(
1212 bool aIsUnload, bool aSkipCheckingDynEntries) {
1213 if (mDocumentViewer && !mFiredUnloadEvent) {
1214 // Keep an explicit reference since calling PageHide could release
1215 // mDocumentViewer
1216 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
1217 mFiredUnloadEvent = true;
1219 if (mTiming) {
1220 mTiming->NotifyUnloadEventStart();
1223 viewer->PageHide(aIsUnload);
1225 if (mTiming) {
1226 mTiming->NotifyUnloadEventEnd();
1229 AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
1230 uint32_t n = mChildList.Length();
1231 kids.SetCapacity(n);
1232 for (uint32_t i = 0; i < n; i++) {
1233 kids.AppendElement(do_QueryInterface(ChildAt(i)));
1236 n = kids.Length();
1237 for (uint32_t i = 0; i < n; ++i) {
1238 RefPtr<nsDocShell> child = static_cast<nsDocShell*>(kids[i].get());
1239 if (child) {
1240 // Skip checking dynamic subframe entries in our children.
1241 child->FirePageHideNotificationInternal(aIsUnload, true);
1245 // If the document is unloading, remove all dynamic subframe entries.
1246 if (aIsUnload && !aSkipCheckingDynEntries) {
1247 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
1248 if (rootSH) {
1249 MOZ_LOG(
1250 gSHLog, LogLevel::Debug,
1251 ("nsDocShell %p unloading, remove dynamic subframe entries", this));
1252 if (mozilla::SessionHistoryInParent()) {
1253 if (mActiveEntry) {
1254 mBrowsingContext->RemoveDynEntriesFromActiveSessionHistoryEntry();
1256 MOZ_LOG(gSHLog, LogLevel::Debug,
1257 ("nsDocShell %p unloading, no active entries", this));
1258 } else if (mOSHE) {
1259 int32_t index = rootSH->Index();
1260 rootSH->LegacySHistory()->RemoveDynEntries(index, mOSHE);
1265 // Now make sure our editor, if any, is detached before we go
1266 // any farther.
1267 DetachEditorFromWindow();
1271 void nsDocShell::ThawFreezeNonRecursive(bool aThaw) {
1272 MOZ_ASSERT(mozilla::BFCacheInParent());
1274 if (!mScriptGlobal) {
1275 return;
1278 if (RefPtr<nsGlobalWindowInner> inner =
1279 nsGlobalWindowInner::Cast(mScriptGlobal->GetCurrentInnerWindow())) {
1280 if (aThaw) {
1281 inner->Thaw(false);
1282 } else {
1283 inner->Freeze(false);
1288 void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
1289 MOZ_ASSERT(mozilla::BFCacheInParent());
1291 if (!mDocumentViewer) {
1292 return;
1295 // Emulate what non-SHIP BFCache does too. In pageshow case
1296 // add and remove a request and before that call SetCurrentURI to get
1297 // the location change notification.
1298 // For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
1299 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
1300 if (aShow) {
1301 viewer->SetIsHidden(false);
1302 mRefreshURIList = std::move(mBFCachedRefreshURIList);
1303 RefreshURIFromQueue();
1304 mFiredUnloadEvent = false;
1305 RefPtr<Document> doc = viewer->GetDocument();
1306 if (doc) {
1307 doc->NotifyActivityChanged();
1308 nsCOMPtr<nsPIDOMWindowInner> inner =
1309 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
1310 if (mBrowsingContext->IsTop()) {
1311 doc->NotifyPossibleTitleChange(false);
1312 doc->SetLoadingOrRestoredFromBFCacheTimeStampToNow();
1313 if (inner) {
1314 // Now that we have found the inner window of the page restored
1315 // from the history, we have to make sure that
1316 // performance.navigation.type is 2.
1317 // Traditionally this type change has been done to the top level page
1318 // only.
1319 Performance* performance = inner->GetPerformance();
1320 if (performance) {
1321 performance->GetDOMTiming()->NotifyRestoreStart();
1326 nsCOMPtr<nsIChannel> channel = doc->GetChannel();
1327 if (channel) {
1328 SetLoadType(LOAD_HISTORY);
1329 mEODForCurrentDocument = false;
1330 mIsRestoringDocument = true;
1331 mLoadGroup->AddRequest(channel, nullptr);
1332 SetCurrentURI(doc->GetDocumentURI(), channel,
1333 /* aFireOnLocationChange */ true,
1334 /* aIsInitialAboutBlank */ false,
1335 /* aLocationFlags */ 0);
1336 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
1337 mIsRestoringDocument = false;
1339 RefPtr<PresShell> presShell = GetPresShell();
1340 if (presShell) {
1341 presShell->Thaw(false);
1344 if (inner) {
1345 inner->FireDelayedDOMEvents(false);
1348 } else if (!mFiredUnloadEvent) {
1349 // XXXBFCache check again that the page can enter bfcache.
1350 // XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
1352 if (mRefreshURIList) {
1353 RefreshURIToQueue();
1354 mBFCachedRefreshURIList = std::move(mRefreshURIList);
1355 } else {
1356 // If Stop was called, the list was moved to mSavedRefreshURIList after
1357 // calling SuspendRefreshURIs, which calls RefreshURIToQueue.
1358 mBFCachedRefreshURIList = std::move(mSavedRefreshURIList);
1361 mFiredUnloadEvent = true;
1362 viewer->PageHide(false);
1364 RefPtr<PresShell> presShell = GetPresShell();
1365 if (presShell) {
1366 presShell->Freeze(false);
1371 nsresult nsDocShell::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) {
1372 nsCOMPtr<nsIRunnable> runnable(aRunnable);
1373 if (NS_WARN_IF(!GetWindow())) {
1374 // Window should only be unavailable after destroyed.
1375 MOZ_ASSERT(mIsBeingDestroyed);
1376 return NS_ERROR_FAILURE;
1378 return SchedulerGroup::Dispatch(runnable.forget());
1381 NS_IMETHODIMP
1382 nsDocShell::DispatchLocationChangeEvent() {
1383 return Dispatch(NewRunnableMethod("nsDocShell::FireDummyOnLocationChange",
1384 this,
1385 &nsDocShell::FireDummyOnLocationChange));
1388 NS_IMETHODIMP
1389 nsDocShell::StartDelayedAutoplayMediaComponents() {
1390 RefPtr<nsPIDOMWindowOuter> outerWindow = GetWindow();
1391 if (outerWindow) {
1392 outerWindow->ActivateMediaComponents();
1394 return NS_OK;
1397 bool nsDocShell::MaybeInitTiming() {
1398 if (mTiming && !mBlankTiming) {
1399 return false;
1402 bool canBeReset = false;
1404 if (mScriptGlobal && mBlankTiming) {
1405 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
1406 if (innerWin && innerWin->GetPerformance()) {
1407 mTiming = innerWin->GetPerformance()->GetDOMTiming();
1408 mBlankTiming = false;
1412 if (!mTiming) {
1413 mTiming = new nsDOMNavigationTiming(this);
1414 canBeReset = true;
1417 mTiming->NotifyNavigationStart(
1418 mBrowsingContext->IsActive()
1419 ? nsDOMNavigationTiming::DocShellState::eActive
1420 : nsDOMNavigationTiming::DocShellState::eInactive);
1422 return canBeReset;
1425 void nsDocShell::MaybeResetInitTiming(bool aReset) {
1426 if (aReset) {
1427 mTiming = nullptr;
1431 nsDOMNavigationTiming* nsDocShell::GetNavigationTiming() const {
1432 return mTiming;
1435 nsPresContext* nsDocShell::GetEldestPresContext() {
1436 nsIDocumentViewer* viewer = mDocumentViewer;
1437 while (viewer) {
1438 nsIDocumentViewer* prevViewer = viewer->GetPreviousViewer();
1439 if (!prevViewer) {
1440 return viewer->GetPresContext();
1442 viewer = prevViewer;
1445 return nullptr;
1448 nsPresContext* nsDocShell::GetPresContext() {
1449 if (!mDocumentViewer) {
1450 return nullptr;
1453 return mDocumentViewer->GetPresContext();
1456 PresShell* nsDocShell::GetPresShell() {
1457 nsPresContext* presContext = GetPresContext();
1458 return presContext ? presContext->GetPresShell() : nullptr;
1461 PresShell* nsDocShell::GetEldestPresShell() {
1462 nsPresContext* presContext = GetEldestPresContext();
1464 if (presContext) {
1465 return presContext->GetPresShell();
1468 return nullptr;
1471 NS_IMETHODIMP
1472 nsDocShell::GetDocViewer(nsIDocumentViewer** aDocumentViewer) {
1473 NS_ENSURE_ARG_POINTER(aDocumentViewer);
1475 *aDocumentViewer = mDocumentViewer;
1476 NS_IF_ADDREF(*aDocumentViewer);
1477 return NS_OK;
1480 NS_IMETHODIMP
1481 nsDocShell::GetOuterWindowID(uint64_t* aWindowID) {
1482 *aWindowID = mContentWindowID;
1483 return NS_OK;
1486 NS_IMETHODIMP
1487 nsDocShell::SetChromeEventHandler(EventTarget* aChromeEventHandler) {
1488 mChromeEventHandler = aChromeEventHandler;
1490 if (mScriptGlobal) {
1491 mScriptGlobal->SetChromeEventHandler(mChromeEventHandler);
1494 return NS_OK;
1497 NS_IMETHODIMP
1498 nsDocShell::GetChromeEventHandler(EventTarget** aChromeEventHandler) {
1499 NS_ENSURE_ARG_POINTER(aChromeEventHandler);
1500 RefPtr<EventTarget> handler = mChromeEventHandler;
1501 handler.forget(aChromeEventHandler);
1502 return NS_OK;
1505 NS_IMETHODIMP
1506 nsDocShell::SetCurrentURIForSessionStore(nsIURI* aURI) {
1507 // Note that securityUI will set STATE_IS_INSECURE, even if
1508 // the scheme of |aURI| is "https".
1509 SetCurrentURI(aURI, nullptr,
1510 /* aFireOnLocationChange */
1511 true,
1512 /* aIsInitialAboutBlank */
1513 false,
1514 /* aLocationFlags */
1515 nsIWebProgressListener::LOCATION_CHANGE_SESSION_STORE);
1516 return NS_OK;
1519 bool nsDocShell::SetCurrentURI(nsIURI* aURI, nsIRequest* aRequest,
1520 bool aFireOnLocationChange,
1521 bool aIsInitialAboutBlank,
1522 uint32_t aLocationFlags) {
1523 MOZ_ASSERT(!mIsBeingDestroyed);
1525 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
1526 ("DOCSHELL %p SetCurrentURI %s\n", this,
1527 aURI ? aURI->GetSpecOrDefault().get() : ""));
1529 // We don't want to send a location change when we're displaying an error
1530 // page, and we don't want to change our idea of "current URI" either
1531 if (mLoadType == LOAD_ERROR_PAGE) {
1532 return false;
1535 bool uriIsEqual = false;
1536 if (!mCurrentURI || !aURI ||
1537 NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
1538 mTitleValidForCurrentURI = false;
1541 SetCurrentURIInternal(aURI);
1543 #ifdef DEBUG
1544 mLastOpenedURI = aURI;
1545 #endif
1547 if (!NS_IsAboutBlank(mCurrentURI)) {
1548 mHasLoadedNonBlankURI = true;
1551 // Don't fire onLocationChange when creating a subframe's initial about:blank
1552 // document, as this can happen when it's not safe for us to run script.
1553 if (aIsInitialAboutBlank && !mHasLoadedNonBlankURI &&
1554 !mBrowsingContext->IsTop()) {
1555 MOZ_ASSERT(!aRequest && aLocationFlags == 0);
1556 return false;
1559 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
1561 if (aFireOnLocationChange) {
1562 FireOnLocationChange(this, aRequest, aURI, aLocationFlags);
1564 return !aFireOnLocationChange;
1567 void nsDocShell::SetCurrentURIInternal(nsIURI* aURI) {
1568 mCurrentURI = aURI;
1569 if (mBrowsingContext) {
1570 mBrowsingContext->ClearCachedValuesOfLocations();
1574 NS_IMETHODIMP
1575 nsDocShell::GetCharset(nsACString& aCharset) {
1576 aCharset.Truncate();
1578 PresShell* presShell = GetPresShell();
1579 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
1580 Document* doc = presShell->GetDocument();
1581 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1582 doc->GetDocumentCharacterSet()->Name(aCharset);
1583 return NS_OK;
1586 NS_IMETHODIMP
1587 nsDocShell::ForceEncodingDetection() {
1588 nsCOMPtr<nsIDocumentViewer> viewer;
1589 GetDocViewer(getter_AddRefs(viewer));
1590 if (!viewer) {
1591 return NS_OK;
1594 Document* doc = viewer->GetDocument();
1595 if (!doc || doc->WillIgnoreCharsetOverride()) {
1596 return NS_OK;
1599 mForcedAutodetection = true;
1601 nsIURI* url = doc->GetOriginalURI();
1602 bool isFileURL = url && SchemeIsFile(url);
1604 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
1605 auto encoding = doc->GetDocumentCharacterSet();
1606 // AsHTMLDocument is valid, because we called
1607 // WillIgnoreCharsetOverride() above.
1608 if (doc->AsHTMLDocument()->IsPlainText()) {
1609 switch (charsetSource) {
1610 case kCharsetFromInitialAutoDetectionASCII:
1611 // Deliberately no final version
1612 LOGCHARSETMENU(("TEXT:UnlabeledAscii"));
1613 Telemetry::AccumulateCategorical(
1614 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledAscii);
1615 break;
1616 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1617 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1618 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1619 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1620 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1621 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1622 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8"));
1623 Telemetry::AccumulateCategorical(
1624 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1625 UnlabeledNonUtf8);
1626 break;
1627 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1628 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1629 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1630 LOGCHARSETMENU(("TEXT:UnlabeledNonUtf8TLD"));
1631 Telemetry::AccumulateCategorical(
1632 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1633 UnlabeledNonUtf8TLD);
1634 break;
1635 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1636 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1637 LOGCHARSETMENU(("TEXT:UnlabeledUtf8"));
1638 Telemetry::AccumulateCategorical(
1639 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::UnlabeledUtf8);
1640 break;
1641 case kCharsetFromChannel:
1642 if (encoding == UTF_8_ENCODING) {
1643 LOGCHARSETMENU(("TEXT:ChannelUtf8"));
1644 Telemetry::AccumulateCategorical(
1645 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::ChannelUtf8);
1646 } else {
1647 LOGCHARSETMENU(("TEXT:ChannelNonUtf8"));
1648 Telemetry::AccumulateCategorical(
1649 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::
1650 ChannelNonUtf8);
1652 break;
1653 default:
1654 LOGCHARSETMENU(("TEXT:Bug"));
1655 Telemetry::AccumulateCategorical(
1656 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_TEXT::Bug);
1657 break;
1659 } else {
1660 switch (charsetSource) {
1661 case kCharsetFromInitialAutoDetectionASCII:
1662 // Deliberately no final version
1663 LOGCHARSETMENU(("HTML:UnlabeledAscii"));
1664 Telemetry::AccumulateCategorical(
1665 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledAscii);
1666 break;
1667 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Generic:
1668 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Generic:
1669 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8GenericInitialWasASCII:
1670 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8Content:
1671 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8Content:
1672 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8ContentInitialWasASCII:
1673 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8"));
1674 Telemetry::AccumulateCategorical(
1675 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1676 UnlabeledNonUtf8);
1677 break;
1678 case kCharsetFromInitialAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1679 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLD:
1680 case kCharsetFromFinalAutoDetectionWouldNotHaveBeenUTF8DependedOnTLDInitialWasASCII:
1681 LOGCHARSETMENU(("HTML:UnlabeledNonUtf8TLD"));
1682 Telemetry::AccumulateCategorical(
1683 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1684 UnlabeledNonUtf8TLD);
1685 break;
1686 case kCharsetFromInitialAutoDetectionWouldHaveBeenUTF8:
1687 case kCharsetFromFinalAutoDetectionWouldHaveBeenUTF8InitialWasASCII:
1688 LOGCHARSETMENU(("HTML:UnlabeledUtf8"));
1689 Telemetry::AccumulateCategorical(
1690 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::UnlabeledUtf8);
1691 break;
1692 case kCharsetFromChannel:
1693 if (encoding == UTF_8_ENCODING) {
1694 LOGCHARSETMENU(("HTML:ChannelUtf8"));
1695 Telemetry::AccumulateCategorical(
1696 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::ChannelUtf8);
1697 } else {
1698 LOGCHARSETMENU(("HTML:ChannelNonUtf8"));
1699 Telemetry::AccumulateCategorical(
1700 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1701 ChannelNonUtf8);
1703 break;
1704 case kCharsetFromXmlDeclaration:
1705 case kCharsetFromMetaTag:
1706 if (isFileURL) {
1707 LOGCHARSETMENU(("HTML:LocalLabeled"));
1708 Telemetry::AccumulateCategorical(
1709 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::LocalLabeled);
1710 } else if (encoding == UTF_8_ENCODING) {
1711 LOGCHARSETMENU(("HTML:MetaUtf8"));
1712 Telemetry::AccumulateCategorical(
1713 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::InternalUtf8);
1714 } else {
1715 LOGCHARSETMENU(("HTML:MetaNonUtf8"));
1716 Telemetry::AccumulateCategorical(
1717 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::
1718 InternalNonUtf8);
1720 break;
1721 default:
1722 LOGCHARSETMENU(("HTML:Bug"));
1723 Telemetry::AccumulateCategorical(
1724 Telemetry::LABELS_ENCODING_OVERRIDE_SITUATION_HTML::Bug);
1725 break;
1728 return NS_OK;
1731 void nsDocShell::SetParentCharset(const Encoding*& aCharset,
1732 int32_t aCharsetSource,
1733 nsIPrincipal* aPrincipal) {
1734 mParentCharset = aCharset;
1735 mParentCharsetSource = aCharsetSource;
1736 mParentCharsetPrincipal = aPrincipal;
1739 void nsDocShell::GetParentCharset(const Encoding*& aCharset,
1740 int32_t* aCharsetSource,
1741 nsIPrincipal** aPrincipal) {
1742 aCharset = mParentCharset;
1743 *aCharsetSource = mParentCharsetSource;
1744 NS_IF_ADDREF(*aPrincipal = mParentCharsetPrincipal);
1747 NS_IMETHODIMP
1748 nsDocShell::GetHasTrackingContentBlocked(Promise** aPromise) {
1749 MOZ_ASSERT(aPromise);
1751 ErrorResult rv;
1752 RefPtr<Document> doc(GetDocument());
1753 RefPtr<Promise> retPromise = Promise::Create(doc->GetOwnerGlobal(), rv);
1754 if (NS_WARN_IF(rv.Failed())) {
1755 return rv.StealNSResult();
1758 // Retrieve the document's content blocking events from the parent process.
1759 RefPtr<Document::GetContentBlockingEventsPromise> promise =
1760 doc->GetContentBlockingEvents();
1761 if (promise) {
1762 promise->Then(
1763 GetCurrentSerialEventTarget(), __func__,
1764 [retPromise](const Document::GetContentBlockingEventsPromise::
1765 ResolveOrRejectValue& aValue) {
1766 if (aValue.IsResolve()) {
1767 bool has = aValue.ResolveValue() &
1768 nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
1769 retPromise->MaybeResolve(has);
1770 } else {
1771 retPromise->MaybeResolve(false);
1774 } else {
1775 retPromise->MaybeResolve(false);
1778 retPromise.forget(aPromise);
1779 return NS_OK;
1782 NS_IMETHODIMP
1783 nsDocShell::GetCssErrorReportingEnabled(bool* aEnabled) {
1784 MOZ_ASSERT(aEnabled);
1785 *aEnabled = mCSSErrorReportingEnabled;
1786 return NS_OK;
1789 NS_IMETHODIMP
1790 nsDocShell::SetCssErrorReportingEnabled(bool aEnabled) {
1791 mCSSErrorReportingEnabled = aEnabled;
1792 return NS_OK;
1795 NS_IMETHODIMP
1796 nsDocShell::GetUsePrivateBrowsing(bool* aUsePrivateBrowsing) {
1797 NS_ENSURE_ARG_POINTER(aUsePrivateBrowsing);
1798 return mBrowsingContext->GetUsePrivateBrowsing(aUsePrivateBrowsing);
1801 void nsDocShell::NotifyPrivateBrowsingChanged() {
1802 MOZ_ASSERT(!mIsBeingDestroyed);
1804 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mPrivacyObservers);
1805 while (iter.HasMore()) {
1806 nsWeakPtr ref = iter.GetNext();
1807 nsCOMPtr<nsIPrivacyTransitionObserver> obs = do_QueryReferent(ref);
1808 if (!obs) {
1809 iter.Remove();
1810 } else {
1811 obs->PrivateModeChanged(UsePrivateBrowsing());
1816 NS_IMETHODIMP
1817 nsDocShell::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) {
1818 return mBrowsingContext->SetUsePrivateBrowsing(aUsePrivateBrowsing);
1821 NS_IMETHODIMP
1822 nsDocShell::SetPrivateBrowsing(bool aUsePrivateBrowsing) {
1823 return mBrowsingContext->SetPrivateBrowsing(aUsePrivateBrowsing);
1826 NS_IMETHODIMP
1827 nsDocShell::GetHasLoadedNonBlankURI(bool* aResult) {
1828 NS_ENSURE_ARG_POINTER(aResult);
1830 *aResult = mHasLoadedNonBlankURI;
1831 return NS_OK;
1834 NS_IMETHODIMP
1835 nsDocShell::GetUseRemoteTabs(bool* aUseRemoteTabs) {
1836 NS_ENSURE_ARG_POINTER(aUseRemoteTabs);
1837 return mBrowsingContext->GetUseRemoteTabs(aUseRemoteTabs);
1840 NS_IMETHODIMP
1841 nsDocShell::SetRemoteTabs(bool aUseRemoteTabs) {
1842 return mBrowsingContext->SetRemoteTabs(aUseRemoteTabs);
1845 NS_IMETHODIMP
1846 nsDocShell::GetUseRemoteSubframes(bool* aUseRemoteSubframes) {
1847 NS_ENSURE_ARG_POINTER(aUseRemoteSubframes);
1848 return mBrowsingContext->GetUseRemoteSubframes(aUseRemoteSubframes);
1851 NS_IMETHODIMP
1852 nsDocShell::SetRemoteSubframes(bool aUseRemoteSubframes) {
1853 return mBrowsingContext->SetRemoteSubframes(aUseRemoteSubframes);
1856 NS_IMETHODIMP
1857 nsDocShell::AddWeakPrivacyTransitionObserver(
1858 nsIPrivacyTransitionObserver* aObserver) {
1859 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1860 if (!weakObs) {
1861 return NS_ERROR_NOT_AVAILABLE;
1863 mPrivacyObservers.AppendElement(weakObs);
1864 return NS_OK;
1867 NS_IMETHODIMP
1868 nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver) {
1869 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
1870 if (!weakObs) {
1871 return NS_ERROR_FAILURE;
1873 mReflowObservers.AppendElement(weakObs);
1874 return NS_OK;
1877 NS_IMETHODIMP
1878 nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver) {
1879 nsWeakPtr obs = do_GetWeakReference(aObserver);
1880 return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
1883 NS_IMETHODIMP
1884 nsDocShell::NotifyReflowObservers(bool aInterruptible,
1885 DOMHighResTimeStamp aStart,
1886 DOMHighResTimeStamp aEnd) {
1887 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
1888 while (iter.HasMore()) {
1889 nsWeakPtr ref = iter.GetNext();
1890 nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
1891 if (!obs) {
1892 iter.Remove();
1893 } else if (aInterruptible) {
1894 obs->ReflowInterruptible(aStart, aEnd);
1895 } else {
1896 obs->Reflow(aStart, aEnd);
1899 return NS_OK;
1902 NS_IMETHODIMP
1903 nsDocShell::GetAllowMetaRedirects(bool* aReturn) {
1904 NS_ENSURE_ARG_POINTER(aReturn);
1906 *aReturn = mAllowMetaRedirects;
1907 return NS_OK;
1910 NS_IMETHODIMP
1911 nsDocShell::SetAllowMetaRedirects(bool aValue) {
1912 mAllowMetaRedirects = aValue;
1913 return NS_OK;
1916 NS_IMETHODIMP
1917 nsDocShell::GetAllowSubframes(bool* aAllowSubframes) {
1918 NS_ENSURE_ARG_POINTER(aAllowSubframes);
1920 *aAllowSubframes = mAllowSubframes;
1921 return NS_OK;
1924 NS_IMETHODIMP
1925 nsDocShell::SetAllowSubframes(bool aAllowSubframes) {
1926 mAllowSubframes = aAllowSubframes;
1927 return NS_OK;
1930 NS_IMETHODIMP
1931 nsDocShell::GetAllowImages(bool* aAllowImages) {
1932 NS_ENSURE_ARG_POINTER(aAllowImages);
1934 *aAllowImages = mAllowImages;
1935 return NS_OK;
1938 NS_IMETHODIMP
1939 nsDocShell::SetAllowImages(bool aAllowImages) {
1940 mAllowImages = aAllowImages;
1941 return NS_OK;
1944 NS_IMETHODIMP
1945 nsDocShell::GetAllowMedia(bool* aAllowMedia) {
1946 *aAllowMedia = mAllowMedia;
1947 return NS_OK;
1950 NS_IMETHODIMP
1951 nsDocShell::SetAllowMedia(bool aAllowMedia) {
1952 mAllowMedia = aAllowMedia;
1954 // Mute or unmute audio contexts attached to the inner window.
1955 if (mScriptGlobal) {
1956 if (nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow()) {
1957 if (aAllowMedia) {
1958 innerWin->UnmuteAudioContexts();
1959 } else {
1960 innerWin->MuteAudioContexts();
1965 return NS_OK;
1968 NS_IMETHODIMP
1969 nsDocShell::GetAllowDNSPrefetch(bool* aAllowDNSPrefetch) {
1970 *aAllowDNSPrefetch = mAllowDNSPrefetch;
1971 return NS_OK;
1974 NS_IMETHODIMP
1975 nsDocShell::SetAllowDNSPrefetch(bool aAllowDNSPrefetch) {
1976 mAllowDNSPrefetch = aAllowDNSPrefetch;
1977 return NS_OK;
1980 NS_IMETHODIMP
1981 nsDocShell::GetAllowWindowControl(bool* aAllowWindowControl) {
1982 *aAllowWindowControl = mAllowWindowControl;
1983 return NS_OK;
1986 NS_IMETHODIMP
1987 nsDocShell::SetAllowWindowControl(bool aAllowWindowControl) {
1988 mAllowWindowControl = aAllowWindowControl;
1989 return NS_OK;
1992 NS_IMETHODIMP
1993 nsDocShell::GetAllowContentRetargeting(bool* aAllowContentRetargeting) {
1994 *aAllowContentRetargeting = mBrowsingContext->GetAllowContentRetargeting();
1995 return NS_OK;
1998 NS_IMETHODIMP
1999 nsDocShell::SetAllowContentRetargeting(bool aAllowContentRetargeting) {
2000 BrowsingContext::Transaction txn;
2001 txn.SetAllowContentRetargeting(aAllowContentRetargeting);
2002 txn.SetAllowContentRetargetingOnChildren(aAllowContentRetargeting);
2003 return txn.Commit(mBrowsingContext);
2006 NS_IMETHODIMP
2007 nsDocShell::GetAllowContentRetargetingOnChildren(
2008 bool* aAllowContentRetargetingOnChildren) {
2009 *aAllowContentRetargetingOnChildren =
2010 mBrowsingContext->GetAllowContentRetargetingOnChildren();
2011 return NS_OK;
2014 NS_IMETHODIMP
2015 nsDocShell::SetAllowContentRetargetingOnChildren(
2016 bool aAllowContentRetargetingOnChildren) {
2017 return mBrowsingContext->SetAllowContentRetargetingOnChildren(
2018 aAllowContentRetargetingOnChildren);
2021 NS_IMETHODIMP
2022 nsDocShell::GetMayEnableCharacterEncodingMenu(
2023 bool* aMayEnableCharacterEncodingMenu) {
2024 *aMayEnableCharacterEncodingMenu = false;
2025 if (!mDocumentViewer) {
2026 return NS_OK;
2028 Document* doc = mDocumentViewer->GetDocument();
2029 if (!doc) {
2030 return NS_OK;
2032 if (doc->WillIgnoreCharsetOverride()) {
2033 return NS_OK;
2036 *aMayEnableCharacterEncodingMenu = true;
2037 return NS_OK;
2040 NS_IMETHODIMP
2041 nsDocShell::GetAllDocShellsInSubtree(int32_t aItemType,
2042 DocShellEnumeratorDirection aDirection,
2043 nsTArray<RefPtr<nsIDocShell>>& aResult) {
2044 aResult.Clear();
2046 nsDocShellEnumerator docShellEnum(
2047 (aDirection == ENUMERATE_FORWARDS)
2048 ? nsDocShellEnumerator::EnumerationDirection::Forwards
2049 : nsDocShellEnumerator::EnumerationDirection::Backwards,
2050 aItemType, *this);
2052 nsresult rv = docShellEnum.BuildDocShellArray(aResult);
2053 if (NS_FAILED(rv)) {
2054 return rv;
2057 return NS_OK;
2060 NS_IMETHODIMP
2061 nsDocShell::GetAppType(AppType* aAppType) {
2062 *aAppType = mAppType;
2063 return NS_OK;
2066 NS_IMETHODIMP
2067 nsDocShell::SetAppType(AppType aAppType) {
2068 mAppType = aAppType;
2069 return NS_OK;
2072 NS_IMETHODIMP
2073 nsDocShell::GetAllowAuth(bool* aAllowAuth) {
2074 *aAllowAuth = mAllowAuth;
2075 return NS_OK;
2078 NS_IMETHODIMP
2079 nsDocShell::SetAllowAuth(bool aAllowAuth) {
2080 mAllowAuth = aAllowAuth;
2081 return NS_OK;
2084 NS_IMETHODIMP
2085 nsDocShell::GetZoom(float* aZoom) {
2086 NS_ENSURE_ARG_POINTER(aZoom);
2087 *aZoom = 1.0f;
2088 return NS_OK;
2091 NS_IMETHODIMP
2092 nsDocShell::SetZoom(float aZoom) { return NS_ERROR_NOT_IMPLEMENTED; }
2094 NS_IMETHODIMP
2095 nsDocShell::GetBusyFlags(BusyFlags* aBusyFlags) {
2096 NS_ENSURE_ARG_POINTER(aBusyFlags);
2098 *aBusyFlags = mBusyFlags;
2099 return NS_OK;
2102 NS_IMETHODIMP
2103 nsDocShell::GetLoadURIDelegate(nsILoadURIDelegate** aLoadURIDelegate) {
2104 nsCOMPtr<nsILoadURIDelegate> delegate = GetLoadURIDelegate();
2105 delegate.forget(aLoadURIDelegate);
2106 return NS_OK;
2109 already_AddRefed<nsILoadURIDelegate> nsDocShell::GetLoadURIDelegate() {
2110 if (nsCOMPtr<nsILoadURIDelegate> result =
2111 do_QueryActor("LoadURIDelegate", GetDocument())) {
2112 return result.forget();
2115 return nullptr;
2118 NS_IMETHODIMP
2119 nsDocShell::GetUseErrorPages(bool* aUseErrorPages) {
2120 *aUseErrorPages = mBrowsingContext->GetUseErrorPages();
2121 return NS_OK;
2124 NS_IMETHODIMP
2125 nsDocShell::SetUseErrorPages(bool aUseErrorPages) {
2126 return mBrowsingContext->SetUseErrorPages(aUseErrorPages);
2129 NS_IMETHODIMP
2130 nsDocShell::GetPreviousEntryIndex(int32_t* aPreviousEntryIndex) {
2131 *aPreviousEntryIndex = mPreviousEntryIndex;
2132 return NS_OK;
2135 NS_IMETHODIMP
2136 nsDocShell::GetLoadedEntryIndex(int32_t* aLoadedEntryIndex) {
2137 *aLoadedEntryIndex = mLoadedEntryIndex;
2138 return NS_OK;
2141 NS_IMETHODIMP
2142 nsDocShell::HistoryPurged(int32_t aNumEntries) {
2143 // These indices are used for fastback cache eviction, to determine
2144 // which session history entries are candidates for content viewer
2145 // eviction. We need to adjust by the number of entries that we
2146 // just purged from history, so that we look at the right session history
2147 // entries during eviction.
2148 mPreviousEntryIndex = std::max(-1, mPreviousEntryIndex - aNumEntries);
2149 mLoadedEntryIndex = std::max(0, mLoadedEntryIndex - aNumEntries);
2151 for (auto* child : mChildList.ForwardRange()) {
2152 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2153 if (shell) {
2154 shell->HistoryPurged(aNumEntries);
2158 return NS_OK;
2161 nsresult nsDocShell::HistoryEntryRemoved(int32_t aIndex) {
2162 // These indices are used for fastback cache eviction, to determine
2163 // which session history entries are candidates for content viewer
2164 // eviction. We need to adjust by the number of entries that we
2165 // just purged from history, so that we look at the right session history
2166 // entries during eviction.
2167 if (aIndex == mPreviousEntryIndex) {
2168 mPreviousEntryIndex = -1;
2169 } else if (aIndex < mPreviousEntryIndex) {
2170 --mPreviousEntryIndex;
2172 if (mLoadedEntryIndex == aIndex) {
2173 mLoadedEntryIndex = 0;
2174 } else if (aIndex < mLoadedEntryIndex) {
2175 --mLoadedEntryIndex;
2178 for (auto* child : mChildList.ForwardRange()) {
2179 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
2180 if (shell) {
2181 static_cast<nsDocShell*>(shell.get())->HistoryEntryRemoved(aIndex);
2185 return NS_OK;
2188 nsresult nsDocShell::Now(DOMHighResTimeStamp* aWhen) {
2189 *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation()).ToMilliseconds();
2190 return NS_OK;
2193 NS_IMETHODIMP
2194 nsDocShell::SetWindowDraggingAllowed(bool aValue) {
2195 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2196 if (!aValue && mItemType == typeChrome && !parent) {
2197 // Window dragging is always allowed for top level
2198 // chrome docshells.
2199 return NS_ERROR_FAILURE;
2201 mWindowDraggingAllowed = aValue;
2202 return NS_OK;
2205 NS_IMETHODIMP
2206 nsDocShell::GetWindowDraggingAllowed(bool* aValue) {
2207 // window dragging regions in CSS (-moz-window-drag:drag)
2208 // can be slow. Default behavior is to only allow it for
2209 // chrome top level windows.
2210 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
2211 if (mItemType == typeChrome && !parent) {
2212 // Top level chrome window
2213 *aValue = true;
2214 } else {
2215 *aValue = mWindowDraggingAllowed;
2217 return NS_OK;
2220 NS_IMETHODIMP
2221 nsDocShell::GetCurrentDocumentChannel(nsIChannel** aResult) {
2222 NS_IF_ADDREF(*aResult = GetCurrentDocChannel());
2223 return NS_OK;
2226 nsIChannel* nsDocShell::GetCurrentDocChannel() {
2227 if (mDocumentViewer) {
2228 Document* doc = mDocumentViewer->GetDocument();
2229 if (doc) {
2230 return doc->GetChannel();
2233 return nullptr;
2236 NS_IMETHODIMP
2237 nsDocShell::AddWeakScrollObserver(nsIScrollObserver* aObserver) {
2238 nsWeakPtr weakObs = do_GetWeakReference(aObserver);
2239 if (!weakObs) {
2240 return NS_ERROR_FAILURE;
2242 mScrollObservers.AppendElement(weakObs);
2243 return NS_OK;
2246 NS_IMETHODIMP
2247 nsDocShell::RemoveWeakScrollObserver(nsIScrollObserver* aObserver) {
2248 nsWeakPtr obs = do_GetWeakReference(aObserver);
2249 return mScrollObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
2252 void nsDocShell::NotifyAsyncPanZoomStarted() {
2253 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2254 while (iter.HasMore()) {
2255 nsWeakPtr ref = iter.GetNext();
2256 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2257 if (obs) {
2258 obs->AsyncPanZoomStarted();
2259 } else {
2260 iter.Remove();
2265 void nsDocShell::NotifyAsyncPanZoomStopped() {
2266 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2267 while (iter.HasMore()) {
2268 nsWeakPtr ref = iter.GetNext();
2269 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2270 if (obs) {
2271 obs->AsyncPanZoomStopped();
2272 } else {
2273 iter.Remove();
2278 NS_IMETHODIMP
2279 nsDocShell::NotifyScrollObservers() {
2280 nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mScrollObservers);
2281 while (iter.HasMore()) {
2282 nsWeakPtr ref = iter.GetNext();
2283 nsCOMPtr<nsIScrollObserver> obs = do_QueryReferent(ref);
2284 if (obs) {
2285 obs->ScrollPositionChanged();
2286 } else {
2287 iter.Remove();
2290 return NS_OK;
2293 //*****************************************************************************
2294 // nsDocShell::nsIDocShellTreeItem
2295 //*****************************************************************************
2297 NS_IMETHODIMP
2298 nsDocShell::GetName(nsAString& aName) {
2299 aName = mBrowsingContext->Name();
2300 return NS_OK;
2303 NS_IMETHODIMP
2304 nsDocShell::SetName(const nsAString& aName) {
2305 return mBrowsingContext->SetName(aName);
2308 NS_IMETHODIMP
2309 nsDocShell::NameEquals(const nsAString& aName, bool* aResult) {
2310 NS_ENSURE_ARG_POINTER(aResult);
2311 *aResult = mBrowsingContext->NameEquals(aName);
2312 return NS_OK;
2315 NS_IMETHODIMP
2316 nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent) {
2317 mBrowsingContext->GetCustomUserAgent(aCustomUserAgent);
2318 return NS_OK;
2321 NS_IMETHODIMP
2322 nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent) {
2323 if (mWillChangeProcess) {
2324 NS_WARNING("SetCustomUserAgent: Process is changing. Ignoring set");
2325 return NS_ERROR_FAILURE;
2328 return mBrowsingContext->SetCustomUserAgent(aCustomUserAgent);
2331 NS_IMETHODIMP
2332 nsDocShell::ClearCachedPlatform() {
2333 nsCOMPtr<nsPIDOMWindowInner> win =
2334 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2335 if (win) {
2336 Navigator* navigator = win->Navigator();
2337 if (navigator) {
2338 navigator->ClearPlatformCache();
2342 return NS_OK;
2345 NS_IMETHODIMP
2346 nsDocShell::ClearCachedUserAgent() {
2347 nsCOMPtr<nsPIDOMWindowInner> win =
2348 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2349 if (win) {
2350 Navigator* navigator = win->Navigator();
2351 if (navigator) {
2352 navigator->ClearUserAgentCache();
2356 return NS_OK;
2359 /* virtual */
2360 int32_t nsDocShell::ItemType() { return mItemType; }
2362 NS_IMETHODIMP
2363 nsDocShell::GetItemType(int32_t* aItemType) {
2364 NS_ENSURE_ARG_POINTER(aItemType);
2366 MOZ_DIAGNOSTIC_ASSERT(
2367 (mBrowsingContext->IsContent() ? typeContent : typeChrome) == mItemType);
2368 *aItemType = mItemType;
2369 return NS_OK;
2372 NS_IMETHODIMP
2373 nsDocShell::GetInProcessParent(nsIDocShellTreeItem** aParent) {
2374 if (!mParent) {
2375 *aParent = nullptr;
2376 } else {
2377 CallQueryInterface(mParent, aParent);
2379 // Note that in the case when the parent is not an nsIDocShellTreeItem we
2380 // don't want to throw; we just want to return null.
2381 return NS_OK;
2384 // With Fission, related nsDocShell objects may exist in a different process. In
2385 // that case, this method will return `nullptr`, despite a parent nsDocShell
2386 // object existing.
2388 // Prefer using `BrowsingContext::Parent()`, which will succeed even if the
2389 // parent entry is not in the current process, and handle the case where the
2390 // parent nsDocShell is inaccessible.
2391 already_AddRefed<nsDocShell> nsDocShell::GetInProcessParentDocshell() {
2392 nsCOMPtr<nsIDocShell> docshell = do_QueryInterface(GetAsSupports(mParent));
2393 return docshell.forget().downcast<nsDocShell>();
2396 void nsDocShell::MaybeCreateInitialClientSource(nsIPrincipal* aPrincipal) {
2397 MOZ_ASSERT(!mIsBeingDestroyed);
2399 // If there is an existing document then there is no need to create
2400 // a client for a future initial about:blank document.
2401 if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindow() &&
2402 mScriptGlobal->GetCurrentInnerWindow()->GetExtantDoc()) {
2403 MOZ_DIAGNOSTIC_ASSERT(
2404 mScriptGlobal->GetCurrentInnerWindow()->GetClientInfo().isSome());
2405 MOZ_DIAGNOSTIC_ASSERT(!mInitialClientSource);
2406 return;
2409 // Don't recreate the initial client source. We call this multiple times
2410 // when DoChannelLoad() is called before CreateAboutBlankDocumentViewer.
2411 if (mInitialClientSource) {
2412 return;
2415 // Don't pre-allocate the client when we are sandboxed. The inherited
2416 // principal does not take sandboxing into account.
2417 // TODO: Refactor sandboxing principal code out so we can use it here.
2418 if (!aPrincipal && mBrowsingContext->GetSandboxFlags()) {
2419 return;
2422 // We cannot get inherited foreign partitioned principal here. Instead, we
2423 // directly check which principal we want to inherit for the service worker.
2424 nsIPrincipal* principal =
2425 aPrincipal
2426 ? aPrincipal
2427 : GetInheritedPrincipal(
2428 false, StoragePrincipalHelper::
2429 ShouldUsePartitionPrincipalForServiceWorker(this));
2431 // Sometimes there is no principal available when we are called from
2432 // CreateAboutBlankDocumentViewer. For example, sometimes the principal
2433 // is only extracted from the load context after the document is created
2434 // in Document::ResetToURI(). Ideally we would do something similar
2435 // here, but for now lets just avoid the issue by not preallocating the
2436 // client.
2437 if (!principal) {
2438 return;
2441 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
2442 if (!win) {
2443 return;
2446 mInitialClientSource = ClientManager::CreateSource(
2447 ClientType::Window, GetMainThreadSerialEventTarget(), principal);
2448 MOZ_DIAGNOSTIC_ASSERT(mInitialClientSource);
2450 // Mark the initial client as execution ready, but owned by the docshell.
2451 // If the client is actually used this will cause ClientSource to force
2452 // the creation of the initial about:blank by calling
2453 // nsDocShell::GetDocument().
2454 mInitialClientSource->DocShellExecutionReady(this);
2456 // Next, check to see if the parent is controlled.
2457 nsCOMPtr<nsIDocShell> parent = GetInProcessParentDocshell();
2458 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
2459 nsPIDOMWindowInner* parentInner =
2460 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
2461 if (!parentInner) {
2462 return;
2465 nsCOMPtr<nsIURI> uri;
2466 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
2468 // We're done if there is no parent controller or if this docshell
2469 // is not permitted to control for some reason.
2470 Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
2471 if (controller.isNothing() ||
2472 !ServiceWorkerAllowedToControlWindow(principal, uri)) {
2473 return;
2476 mInitialClientSource->InheritController(controller.ref());
2479 Maybe<ClientInfo> nsDocShell::GetInitialClientInfo() const {
2480 if (mInitialClientSource) {
2481 Maybe<ClientInfo> result;
2482 result.emplace(mInitialClientSource->Info());
2483 return result;
2486 nsPIDOMWindowInner* innerWindow =
2487 mScriptGlobal ? mScriptGlobal->GetCurrentInnerWindow() : nullptr;
2488 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
2490 if (!doc || !doc->IsInitialDocument()) {
2491 return Maybe<ClientInfo>();
2494 return innerWindow->GetClientInfo();
2497 nsresult nsDocShell::SetDocLoaderParent(nsDocLoader* aParent) {
2498 bool wasFrame = IsSubframe();
2500 nsresult rv = nsDocLoader::SetDocLoaderParent(aParent);
2501 NS_ENSURE_SUCCESS(rv, rv);
2503 nsCOMPtr<nsISupportsPriority> priorityGroup = do_QueryInterface(mLoadGroup);
2504 if (wasFrame != IsSubframe() && priorityGroup) {
2505 priorityGroup->AdjustPriority(wasFrame ? -1 : 1);
2508 // Curse ambiguous nsISupports inheritance!
2509 nsISupports* parent = GetAsSupports(aParent);
2511 // If parent is another docshell, we inherit all their flags for
2512 // allowing plugins, scripting etc.
2513 bool value;
2514 nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
2516 if (parentAsDocShell) {
2517 if (mAllowMetaRedirects &&
2518 NS_SUCCEEDED(parentAsDocShell->GetAllowMetaRedirects(&value))) {
2519 SetAllowMetaRedirects(value);
2521 if (mAllowSubframes &&
2522 NS_SUCCEEDED(parentAsDocShell->GetAllowSubframes(&value))) {
2523 SetAllowSubframes(value);
2525 if (mAllowImages &&
2526 NS_SUCCEEDED(parentAsDocShell->GetAllowImages(&value))) {
2527 SetAllowImages(value);
2529 SetAllowMedia(parentAsDocShell->GetAllowMedia() && mAllowMedia);
2530 if (mAllowWindowControl &&
2531 NS_SUCCEEDED(parentAsDocShell->GetAllowWindowControl(&value))) {
2532 SetAllowWindowControl(value);
2534 if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
2535 value = false;
2537 SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
2540 nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
2541 if (parentURIListener) {
2542 mContentListener->SetParentContentListener(parentURIListener);
2545 return NS_OK;
2548 void nsDocShell::MaybeRestoreWindowName() {
2549 if (!StaticPrefs::privacy_window_name_update_enabled()) {
2550 return;
2553 // We only restore window.name for the top-level content.
2554 if (!mBrowsingContext->IsTopContent()) {
2555 return;
2558 nsAutoString name;
2560 // Following implements https://html.spec.whatwg.org/#history-traversal:
2561 // Step 4.4. Check if the loading entry has a name.
2563 if (mLSHE) {
2564 mLSHE->GetName(name);
2567 if (mLoadingEntry) {
2568 name = mLoadingEntry->mInfo.GetName();
2571 if (name.IsEmpty()) {
2572 return;
2575 // Step 4.4.1. Set the name to the browsing context.
2576 Unused << mBrowsingContext->SetName(name);
2578 // Step 4.4.2. Clear the name of all entries that are contiguous and
2579 // same-origin with the loading entry.
2580 if (mLSHE) {
2581 nsSHistory::WalkContiguousEntries(
2582 mLSHE, [](nsISHEntry* aEntry) { aEntry->SetName(EmptyString()); });
2585 if (mLoadingEntry) {
2586 // Clear the name of the session entry in the child side. For parent side,
2587 // the clearing will be done when we commit the history to the parent.
2588 mLoadingEntry->mInfo.SetName(EmptyString());
2592 void nsDocShell::StoreWindowNameToSHEntries() {
2593 MOZ_ASSERT(mBrowsingContext->IsTopContent());
2595 nsAutoString name;
2596 mBrowsingContext->GetName(name);
2598 if (mOSHE) {
2599 nsSHistory::WalkContiguousEntries(
2600 mOSHE, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2603 if (mozilla::SessionHistoryInParent()) {
2604 if (XRE_IsParentProcess()) {
2605 SessionHistoryEntry* entry =
2606 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2607 if (entry) {
2608 nsSHistory::WalkContiguousEntries(
2609 entry, [&](nsISHEntry* aEntry) { aEntry->SetName(name); });
2611 } else {
2612 // Ask parent process to store the name in entries.
2613 mozilla::Unused
2614 << ContentChild::GetSingleton()
2615 ->SendSessionHistoryEntryStoreWindowNameInContiguousEntries(
2616 mBrowsingContext, name);
2621 NS_IMETHODIMP
2622 nsDocShell::GetInProcessSameTypeParent(nsIDocShellTreeItem** aParent) {
2623 if (BrowsingContext* parentBC = mBrowsingContext->GetParent()) {
2624 *aParent = do_AddRef(parentBC->GetDocShell()).take();
2626 return NS_OK;
2629 NS_IMETHODIMP
2630 nsDocShell::GetInProcessRootTreeItem(nsIDocShellTreeItem** aRootTreeItem) {
2631 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2633 RefPtr<nsDocShell> root = this;
2634 RefPtr<nsDocShell> parent = root->GetInProcessParentDocshell();
2635 while (parent) {
2636 root = parent;
2637 parent = root->GetInProcessParentDocshell();
2640 root.forget(aRootTreeItem);
2641 return NS_OK;
2644 NS_IMETHODIMP
2645 nsDocShell::GetInProcessSameTypeRootTreeItem(
2646 nsIDocShellTreeItem** aRootTreeItem) {
2647 NS_ENSURE_ARG_POINTER(aRootTreeItem);
2648 *aRootTreeItem = static_cast<nsIDocShellTreeItem*>(this);
2650 nsCOMPtr<nsIDocShellTreeItem> parent;
2651 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parent)),
2652 NS_ERROR_FAILURE);
2653 while (parent) {
2654 *aRootTreeItem = parent;
2655 NS_ENSURE_SUCCESS(
2656 (*aRootTreeItem)->GetInProcessSameTypeParent(getter_AddRefs(parent)),
2657 NS_ERROR_FAILURE);
2659 NS_ADDREF(*aRootTreeItem);
2660 return NS_OK;
2663 NS_IMETHODIMP
2664 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner) {
2665 NS_ENSURE_ARG_POINTER(aTreeOwner);
2667 *aTreeOwner = mTreeOwner;
2668 NS_IF_ADDREF(*aTreeOwner);
2669 return NS_OK;
2672 NS_IMETHODIMP
2673 nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner) {
2674 if (mIsBeingDestroyed && aTreeOwner) {
2675 return NS_ERROR_FAILURE;
2678 // Don't automatically set the progress based on the tree owner for frames
2679 if (!IsSubframe()) {
2680 nsCOMPtr<nsIWebProgress> webProgress =
2681 do_QueryInterface(GetAsSupports(this));
2683 if (webProgress) {
2684 nsCOMPtr<nsIWebProgressListener> oldListener =
2685 do_QueryInterface(mTreeOwner);
2686 nsCOMPtr<nsIWebProgressListener> newListener =
2687 do_QueryInterface(aTreeOwner);
2689 if (oldListener) {
2690 webProgress->RemoveProgressListener(oldListener);
2693 if (newListener) {
2694 webProgress->AddProgressListener(newListener,
2695 nsIWebProgress::NOTIFY_ALL);
2700 mTreeOwner = aTreeOwner; // Weak reference per API
2702 for (auto* childDocLoader : mChildList.ForwardRange()) {
2703 nsCOMPtr<nsIDocShellTreeItem> child = do_QueryObject(childDocLoader);
2704 NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
2706 if (child->ItemType() == mItemType) {
2707 child->SetTreeOwner(aTreeOwner);
2711 // If we're in the content process and have had a TreeOwner set on us, extract
2712 // our BrowserChild actor. If we've already had our BrowserChild set, assert
2713 // that it hasn't changed.
2714 if (mTreeOwner && XRE_IsContentProcess()) {
2715 nsCOMPtr<nsIBrowserChild> newBrowserChild = do_GetInterface(mTreeOwner);
2716 MOZ_ASSERT(newBrowserChild,
2717 "No BrowserChild actor for tree owner in Content!");
2719 if (mBrowserChild) {
2720 nsCOMPtr<nsIBrowserChild> oldBrowserChild =
2721 do_QueryReferent(mBrowserChild);
2722 MOZ_RELEASE_ASSERT(
2723 oldBrowserChild == newBrowserChild,
2724 "Cannot change BrowserChild during nsDocShell lifetime!");
2725 } else {
2726 mBrowserChild = do_GetWeakReference(newBrowserChild);
2730 return NS_OK;
2733 NS_IMETHODIMP
2734 nsDocShell::GetHistoryID(nsID& aID) {
2735 aID = mBrowsingContext->GetHistoryID();
2736 return NS_OK;
2739 const nsID& nsDocShell::HistoryID() { return mBrowsingContext->GetHistoryID(); }
2741 NS_IMETHODIMP
2742 nsDocShell::GetIsInUnload(bool* aIsInUnload) {
2743 *aIsInUnload = mFiredUnloadEvent;
2744 return NS_OK;
2747 NS_IMETHODIMP
2748 nsDocShell::GetInProcessChildCount(int32_t* aChildCount) {
2749 NS_ENSURE_ARG_POINTER(aChildCount);
2750 *aChildCount = mChildList.Length();
2751 return NS_OK;
2754 NS_IMETHODIMP
2755 nsDocShell::AddChild(nsIDocShellTreeItem* aChild) {
2756 NS_ENSURE_ARG_POINTER(aChild);
2758 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2759 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2761 // Make sure we're not creating a loop in the docshell tree
2762 nsDocLoader* ancestor = this;
2763 do {
2764 if (childAsDocLoader == ancestor) {
2765 return NS_ERROR_ILLEGAL_VALUE;
2767 ancestor = ancestor->GetParent();
2768 } while (ancestor);
2770 // Make sure to remove the child from its current parent.
2771 nsDocLoader* childsParent = childAsDocLoader->GetParent();
2772 if (childsParent) {
2773 nsresult rv = childsParent->RemoveChildLoader(childAsDocLoader);
2774 NS_ENSURE_SUCCESS(rv, rv);
2777 // Make sure to clear the treeowner in case this child is a different type
2778 // from us.
2779 aChild->SetTreeOwner(nullptr);
2781 nsresult res = AddChildLoader(childAsDocLoader);
2782 NS_ENSURE_SUCCESS(res, res);
2783 NS_ASSERTION(!mChildList.IsEmpty(),
2784 "child list must not be empty after a successful add");
2786 /* Set the child's global history if the parent has one */
2787 if (mBrowsingContext->GetUseGlobalHistory()) {
2788 // childDocShell->SetUseGlobalHistory(true);
2789 // this should be set through BC inherit
2790 MOZ_ASSERT(aChild->GetBrowsingContext()->GetUseGlobalHistory());
2793 if (aChild->ItemType() != mItemType) {
2794 return NS_OK;
2797 aChild->SetTreeOwner(mTreeOwner);
2799 nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
2800 if (!childAsDocShell) {
2801 return NS_OK;
2804 // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
2806 // Now take this document's charset and set the child's parentCharset field
2807 // to it. We'll later use that field, in the loading process, for the
2808 // charset choosing algorithm.
2809 // If we fail, at any point, we just return NS_OK.
2810 // This code has some performance impact. But this will be reduced when
2811 // the current charset will finally be stored as an Atom, avoiding the
2812 // alias resolution extra look-up.
2814 // we are NOT going to propagate the charset is this Chrome's docshell
2815 if (mItemType == nsIDocShellTreeItem::typeChrome) {
2816 return NS_OK;
2819 // get the parent's current charset
2820 if (!mDocumentViewer) {
2821 return NS_OK;
2823 Document* doc = mDocumentViewer->GetDocument();
2824 if (!doc) {
2825 return NS_OK;
2828 const Encoding* parentCS = doc->GetDocumentCharacterSet();
2829 int32_t charsetSource = doc->GetDocumentCharacterSetSource();
2830 // set the child's parentCharset
2831 childAsDocShell->SetParentCharset(parentCS, charsetSource,
2832 doc->NodePrincipal());
2834 // printf("### 1 >>> Adding child. Parent CS = %s. ItemType = %d.\n",
2835 // NS_LossyConvertUTF16toASCII(parentCS).get(), mItemType);
2837 return NS_OK;
2840 NS_IMETHODIMP
2841 nsDocShell::RemoveChild(nsIDocShellTreeItem* aChild) {
2842 NS_ENSURE_ARG_POINTER(aChild);
2844 RefPtr<nsDocLoader> childAsDocLoader = GetAsDocLoader(aChild);
2845 NS_ENSURE_TRUE(childAsDocLoader, NS_ERROR_UNEXPECTED);
2847 nsresult rv = RemoveChildLoader(childAsDocLoader);
2848 NS_ENSURE_SUCCESS(rv, rv);
2850 aChild->SetTreeOwner(nullptr);
2852 return nsDocLoader::AddDocLoaderAsChildOfRoot(childAsDocLoader);
2855 NS_IMETHODIMP
2856 nsDocShell::GetInProcessChildAt(int32_t aIndex, nsIDocShellTreeItem** aChild) {
2857 NS_ENSURE_ARG_POINTER(aChild);
2859 RefPtr<nsDocShell> child = GetInProcessChildAt(aIndex);
2860 NS_ENSURE_TRUE(child, NS_ERROR_UNEXPECTED);
2862 child.forget(aChild);
2864 return NS_OK;
2867 nsDocShell* nsDocShell::GetInProcessChildAt(int32_t aIndex) {
2868 #ifdef DEBUG
2869 if (aIndex < 0) {
2870 NS_WARNING("Negative index passed to GetChildAt");
2871 } else if (static_cast<uint32_t>(aIndex) >= mChildList.Length()) {
2872 NS_WARNING("Too large an index passed to GetChildAt");
2874 #endif
2876 nsIDocumentLoader* child = ChildAt(aIndex);
2878 // child may be nullptr here.
2879 return static_cast<nsDocShell*>(child);
2882 nsresult nsDocShell::AddChildSHEntry(nsISHEntry* aCloneRef,
2883 nsISHEntry* aNewEntry,
2884 int32_t aChildOffset, uint32_t aLoadType,
2885 bool aCloneChildren) {
2886 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2887 nsresult rv = NS_OK;
2889 if (mLSHE && aLoadType != LOAD_PUSHSTATE) {
2890 /* You get here if you are currently building a
2891 * hierarchy ie.,you just visited a frameset page
2893 if (NS_FAILED(mLSHE->ReplaceChild(aNewEntry))) {
2894 rv = mLSHE->AddChild(aNewEntry, aChildOffset);
2896 } else if (!aCloneRef) {
2897 /* This is an initial load in some subframe. Just append it if we can */
2898 if (mOSHE) {
2899 rv = mOSHE->AddChild(aNewEntry, aChildOffset, UseRemoteSubframes());
2901 } else {
2902 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
2903 if (shistory) {
2904 rv = shistory->LegacySHistory()->AddChildSHEntryHelper(
2905 aCloneRef, aNewEntry, mBrowsingContext->Top(), aCloneChildren);
2908 return rv;
2911 nsresult nsDocShell::AddChildSHEntryToParent(nsISHEntry* aNewEntry,
2912 int32_t aChildOffset,
2913 bool aCloneChildren) {
2914 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
2915 /* You will get here when you are in a subframe and
2916 * a new url has been loaded on you.
2917 * The mOSHE in this subframe will be the previous url's
2918 * mOSHE. This mOSHE will be used as the identification
2919 * for this subframe in the CloneAndReplace function.
2922 // In this case, we will end up calling AddEntry, which increases the
2923 // current index by 1
2924 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
2925 if (rootSH) {
2926 mPreviousEntryIndex = rootSH->Index();
2929 nsresult rv;
2930 // XXX(farre): this is not Fission safe, expect errors. This never
2931 // get's executed once session history in the parent is enabled.
2932 nsCOMPtr<nsIDocShell> parent = do_QueryInterface(GetAsSupports(mParent), &rv);
2933 NS_WARNING_ASSERTION(
2934 parent || !UseRemoteSubframes(),
2935 "Failed to add child session history entry! This will be resolved once "
2936 "session history in the parent is enabled.");
2937 if (parent) {
2938 rv = nsDocShell::Cast(parent)->AddChildSHEntry(
2939 mOSHE, aNewEntry, aChildOffset, mLoadType, aCloneChildren);
2942 if (rootSH) {
2943 mLoadedEntryIndex = rootSH->Index();
2945 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
2946 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
2947 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
2948 mLoadedEntryIndex));
2952 return rv;
2955 NS_IMETHODIMP
2956 nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE) {
2957 *aOSHE = false;
2958 *aEntry = nullptr;
2959 if (mLSHE) {
2960 NS_ADDREF(*aEntry = mLSHE);
2961 } else if (mOSHE) {
2962 NS_ADDREF(*aEntry = mOSHE);
2963 *aOSHE = true;
2965 return NS_OK;
2968 NS_IMETHODIMP nsDocShell::SynchronizeLayoutHistoryState() {
2969 if (mActiveEntry && mActiveEntry->GetLayoutHistoryState() &&
2970 mBrowsingContext) {
2971 if (XRE_IsContentProcess()) {
2972 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
2973 if (contentChild) {
2974 contentChild->SendSynchronizeLayoutHistoryState(
2975 mBrowsingContext, mActiveEntry->GetLayoutHistoryState());
2977 } else {
2978 SessionHistoryEntry* entry =
2979 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
2980 if (entry) {
2981 entry->SetLayoutHistoryState(mActiveEntry->GetLayoutHistoryState());
2984 if (mLoadingEntry &&
2985 mLoadingEntry->mInfo.SharedId() == mActiveEntry->SharedId()) {
2986 mLoadingEntry->mInfo.SetLayoutHistoryState(
2987 mActiveEntry->GetLayoutHistoryState());
2991 return NS_OK;
2994 void nsDocShell::SetLoadGroupDefaultLoadFlags(nsLoadFlags aLoadFlags) {
2995 if (mLoadGroup) {
2996 mLoadGroup->SetDefaultLoadFlags(aLoadFlags);
2997 } else {
2998 NS_WARNING(
2999 "nsDocShell::SetLoadGroupDefaultLoadFlags has no loadGroup to "
3000 "propagate the mode to");
3004 nsIScriptGlobalObject* nsDocShell::GetScriptGlobalObject() {
3005 NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), nullptr);
3006 return mScriptGlobal;
3009 Document* nsDocShell::GetDocument() {
3010 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), nullptr);
3011 return mDocumentViewer->GetDocument();
3014 Document* nsDocShell::GetExtantDocument() {
3015 return mDocumentViewer ? mDocumentViewer->GetDocument() : nullptr;
3018 nsPIDOMWindowOuter* nsDocShell::GetWindow() {
3019 if (NS_FAILED(EnsureScriptEnvironment())) {
3020 return nullptr;
3022 return mScriptGlobal;
3025 NS_IMETHODIMP
3026 nsDocShell::GetDomWindow(mozIDOMWindowProxy** aWindow) {
3027 NS_ENSURE_ARG_POINTER(aWindow);
3029 nsresult rv = EnsureScriptEnvironment();
3030 NS_ENSURE_SUCCESS(rv, rv);
3032 RefPtr<nsGlobalWindowOuter> window = mScriptGlobal;
3033 window.forget(aWindow);
3034 return NS_OK;
3037 NS_IMETHODIMP
3038 nsDocShell::GetMessageManager(ContentFrameMessageManager** aMessageManager) {
3039 RefPtr<ContentFrameMessageManager> mm;
3040 if (RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this)) {
3041 mm = browserChild->GetMessageManager();
3042 } else if (nsPIDOMWindowOuter* win = GetWindow()) {
3043 mm = win->GetMessageManager();
3045 mm.forget(aMessageManager);
3046 return NS_OK;
3049 NS_IMETHODIMP
3050 nsDocShell::GetIsNavigating(bool* aOut) {
3051 *aOut = mIsNavigating;
3052 return NS_OK;
3055 void nsDocShell::ClearFrameHistory(nsISHEntry* aEntry) {
3056 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
3057 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3058 if (!rootSH || !aEntry) {
3059 return;
3062 rootSH->LegacySHistory()->RemoveFrameEntries(aEntry);
3065 //-------------------------------------
3066 //-- Helper Method for Print discovery
3067 //-------------------------------------
3068 bool nsDocShell::NavigationBlockedByPrinting(bool aDisplayErrorDialog) {
3069 if (!mBrowsingContext->Top()->GetIsPrinting()) {
3070 return false;
3072 if (aDisplayErrorDialog) {
3073 DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nullptr, nullptr, nullptr);
3075 return true;
3078 bool nsDocShell::IsNavigationAllowed(bool aDisplayPrintErrorDialog,
3079 bool aCheckIfUnloadFired) {
3080 bool isAllowed = !NavigationBlockedByPrinting(aDisplayPrintErrorDialog) &&
3081 (!aCheckIfUnloadFired || !mFiredUnloadEvent);
3082 if (!isAllowed) {
3083 return false;
3085 if (!mDocumentViewer) {
3086 return true;
3088 bool firingBeforeUnload;
3089 mDocumentViewer->GetBeforeUnloadFiring(&firingBeforeUnload);
3090 return !firingBeforeUnload;
3093 //*****************************************************************************
3094 // nsDocShell::nsIWebNavigation
3095 //*****************************************************************************
3097 NS_IMETHODIMP
3098 nsDocShell::GetCanGoBack(bool* aCanGoBack) {
3099 *aCanGoBack = false;
3100 if (!IsNavigationAllowed(false)) {
3101 return NS_OK; // JS may not handle returning of an error code
3103 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3104 if (rootSH) {
3105 *aCanGoBack = rootSH->CanGo(-1);
3106 MOZ_LOG(gSHLog, LogLevel::Verbose,
3107 ("nsDocShell %p CanGoBack()->%d", this, *aCanGoBack));
3109 return NS_OK;
3111 return NS_ERROR_FAILURE;
3114 NS_IMETHODIMP
3115 nsDocShell::GetCanGoForward(bool* aCanGoForward) {
3116 *aCanGoForward = false;
3117 if (!IsNavigationAllowed(false)) {
3118 return NS_OK; // JS may not handle returning of an error code
3120 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3121 if (rootSH) {
3122 *aCanGoForward = rootSH->CanGo(1);
3123 MOZ_LOG(gSHLog, LogLevel::Verbose,
3124 ("nsDocShell %p CanGoForward()->%d", this, *aCanGoForward));
3125 return NS_OK;
3127 return NS_ERROR_FAILURE;
3130 NS_IMETHODIMP
3131 nsDocShell::GoBack(bool aRequireUserInteraction, bool aUserActivation) {
3132 if (!IsNavigationAllowed()) {
3133 return NS_OK; // JS may not handle returning of an error code
3136 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3137 mIsNavigating = true;
3139 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3140 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3141 ErrorResult rv;
3142 rootSH->Go(-1, aRequireUserInteraction, aUserActivation, rv);
3143 return rv.StealNSResult();
3146 NS_IMETHODIMP
3147 nsDocShell::GoForward(bool aRequireUserInteraction, bool aUserActivation) {
3148 if (!IsNavigationAllowed()) {
3149 return NS_OK; // JS may not handle returning of an error code
3152 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3153 mIsNavigating = true;
3155 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3156 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3157 ErrorResult rv;
3158 rootSH->Go(1, aRequireUserInteraction, aUserActivation, rv);
3159 return rv.StealNSResult();
3162 // XXX(nika): We may want to stop exposing this API in the child process? Going
3163 // to a specific index from multiple different processes could definitely race.
3164 NS_IMETHODIMP
3165 nsDocShell::GotoIndex(int32_t aIndex, bool aUserActivation) {
3166 if (!IsNavigationAllowed()) {
3167 return NS_OK; // JS may not handle returning of an error code
3170 auto cleanupIsNavigating = MakeScopeExit([&]() { mIsNavigating = false; });
3171 mIsNavigating = true;
3173 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3174 NS_ENSURE_TRUE(rootSH, NS_ERROR_FAILURE);
3176 ErrorResult rv;
3177 rootSH->GotoIndex(aIndex, aIndex - rootSH->Index(), false, aUserActivation,
3178 rv);
3179 return rv.StealNSResult();
3182 nsresult nsDocShell::LoadURI(nsIURI* aURI,
3183 const LoadURIOptions& aLoadURIOptions) {
3184 if (!IsNavigationAllowed()) {
3185 return NS_OK; // JS may not handle returning of an error code
3187 RefPtr<nsDocShellLoadState> loadState;
3188 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3189 mBrowsingContext, aURI, aLoadURIOptions, getter_AddRefs(loadState));
3190 MOZ_ASSERT(rv != NS_ERROR_MALFORMED_URI);
3191 if (NS_FAILED(rv) || !loadState) {
3192 return NS_ERROR_FAILURE;
3195 return LoadURI(loadState, true);
3198 NS_IMETHODIMP
3199 nsDocShell::LoadURIFromScript(nsIURI* aURI,
3200 JS::Handle<JS::Value> aLoadURIOptions,
3201 JSContext* aCx) {
3202 // generate dictionary for aLoadURIOptions and forward call
3203 LoadURIOptions loadURIOptions;
3204 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3205 return NS_ERROR_INVALID_ARG;
3207 return LoadURI(aURI, loadURIOptions);
3210 nsresult nsDocShell::FixupAndLoadURIString(
3211 const nsAString& aURIString, const LoadURIOptions& aLoadURIOptions) {
3212 if (!IsNavigationAllowed()) {
3213 return NS_OK; // JS may not handle returning of an error code
3216 RefPtr<nsDocShellLoadState> loadState;
3217 nsresult rv = nsDocShellLoadState::CreateFromLoadURIOptions(
3218 mBrowsingContext, aURIString, aLoadURIOptions, getter_AddRefs(loadState));
3220 uint32_t loadFlags = aLoadURIOptions.mLoadFlags;
3221 if (NS_ERROR_MALFORMED_URI == rv) {
3222 MOZ_LOG(gSHLog, LogLevel::Debug,
3223 ("Creating an active entry on nsDocShell %p to %s (because "
3224 "we're showing an error page)",
3225 this, NS_ConvertUTF16toUTF8(aURIString).get()));
3227 // We need to store a session history entry. We don't have a valid URI, so
3228 // we use about:blank instead.
3229 nsCOMPtr<nsIURI> uri;
3230 MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), "about:blank"_ns));
3231 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
3232 if (aLoadURIOptions.mTriggeringPrincipal) {
3233 triggeringPrincipal = aLoadURIOptions.mTriggeringPrincipal;
3234 } else {
3235 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
3237 if (mozilla::SessionHistoryInParent()) {
3238 mActiveEntry = MakeUnique<SessionHistoryInfo>(
3239 uri, triggeringPrincipal, nullptr, nullptr, nullptr,
3240 nsLiteralCString("text/html"));
3241 mBrowsingContext->SetActiveSessionHistoryEntry(
3242 Nothing(), mActiveEntry.get(), MAKE_LOAD_TYPE(LOAD_NORMAL, loadFlags),
3243 /* aUpdatedCacheKey = */ 0);
3245 if (DisplayLoadError(rv, nullptr, PromiseFlatString(aURIString).get(),
3246 nullptr) &&
3247 (loadFlags & LOAD_FLAGS_ERROR_LOAD_CHANGES_RV) != 0) {
3248 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
3252 if (NS_FAILED(rv) || !loadState) {
3253 return NS_ERROR_FAILURE;
3256 return LoadURI(loadState, true);
3259 NS_IMETHODIMP
3260 nsDocShell::FixupAndLoadURIStringFromScript(
3261 const nsAString& aURIString, JS::Handle<JS::Value> aLoadURIOptions,
3262 JSContext* aCx) {
3263 // generate dictionary for aLoadURIOptions and forward call
3264 LoadURIOptions loadURIOptions;
3265 if (!loadURIOptions.Init(aCx, aLoadURIOptions)) {
3266 return NS_ERROR_INVALID_ARG;
3268 return FixupAndLoadURIString(aURIString, loadURIOptions);
3271 void nsDocShell::UnblockEmbedderLoadEventForFailure(bool aFireFrameErrorEvent) {
3272 // If we're not in a content frame, or are at a BrowsingContext tree boundary,
3273 // such as the content-chrome boundary, don't fire the error event.
3274 if (mBrowsingContext->IsTopContent() || mBrowsingContext->IsChrome()) {
3275 return;
3278 // If embedder is same-process, then unblocking the load event is already
3279 // handled by nsDocLoader. Fire the error event on our embedder element if
3280 // requested.
3282 // XXX: Bug 1440212 is looking into potentially changing this behaviour to act
3283 // more like the remote case when in-process.
3284 RefPtr<Element> element = mBrowsingContext->GetEmbedderElement();
3285 if (element) {
3286 if (aFireFrameErrorEvent) {
3287 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(element)) {
3288 if (RefPtr<nsFrameLoader> fl = flo->GetFrameLoader()) {
3289 fl->FireErrorEvent();
3293 return;
3296 // If we have a cross-process parent document, we must notify it that we no
3297 // longer block its load event. This is necessary for OOP sub-documents
3298 // because error documents do not result in a call to
3299 // SendMaybeFireEmbedderLoadEvents via any of the normal call paths.
3300 // (Obviously, we must do this before any of the returns below.)
3301 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(this);
3302 if (browserChild &&
3303 !mBrowsingContext->GetParentWindowContext()->IsInProcess()) {
3304 mozilla::Unused << browserChild->SendMaybeFireEmbedderLoadEvents(
3305 aFireFrameErrorEvent ? EmbedderElementEventType::ErrorEvent
3306 : EmbedderElementEventType::NoEvent);
3310 NS_IMETHODIMP
3311 nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI,
3312 const char16_t* aURL, nsIChannel* aFailedChannel,
3313 bool* aDisplayedErrorPage) {
3314 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
3315 ("DOCSHELL %p DisplayLoadError %s\n", this,
3316 aURI ? aURI->GetSpecOrDefault().get() : ""));
3318 *aDisplayedErrorPage = false;
3319 // Get prompt and string bundle services
3320 nsCOMPtr<nsIPrompt> prompter;
3321 nsCOMPtr<nsIStringBundle> stringBundle;
3322 GetPromptAndStringBundle(getter_AddRefs(prompter),
3323 getter_AddRefs(stringBundle));
3325 NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);
3326 NS_ENSURE_TRUE(prompter, NS_ERROR_FAILURE);
3328 const char* error = nullptr;
3329 // The key used to select the appropriate error message from the properties
3330 // file.
3331 const char* errorDescriptionID = nullptr;
3332 AutoTArray<nsString, 3> formatStrs;
3333 bool addHostPort = false;
3334 bool isBadStsCertError = false;
3335 nsresult rv = NS_OK;
3336 nsAutoString messageStr;
3337 nsAutoCString cssClass;
3338 nsAutoCString errorPage;
3340 errorPage.AssignLiteral("neterror");
3342 // Turn the error code into a human readable error message.
3343 if (NS_ERROR_UNKNOWN_PROTOCOL == aError) {
3344 NS_ENSURE_ARG_POINTER(aURI);
3346 // Extract the schemes into a comma delimited list.
3347 nsAutoCString scheme;
3348 aURI->GetScheme(scheme);
3349 CopyASCIItoUTF16(scheme, *formatStrs.AppendElement());
3350 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(aURI);
3351 while (nestedURI) {
3352 nsCOMPtr<nsIURI> tempURI;
3353 nsresult rv2;
3354 rv2 = nestedURI->GetInnerURI(getter_AddRefs(tempURI));
3355 if (NS_SUCCEEDED(rv2) && tempURI) {
3356 tempURI->GetScheme(scheme);
3357 formatStrs[0].AppendLiteral(", ");
3358 AppendASCIItoUTF16(scheme, formatStrs[0]);
3360 nestedURI = do_QueryInterface(tempURI);
3362 error = "unknownProtocolFound";
3363 } else if (NS_ERROR_FILE_NOT_FOUND == aError) {
3364 NS_ENSURE_ARG_POINTER(aURI);
3365 error = "fileNotFound";
3366 } else if (NS_ERROR_FILE_ACCESS_DENIED == aError) {
3367 NS_ENSURE_ARG_POINTER(aURI);
3368 error = "fileAccessDenied";
3369 } else if (NS_ERROR_UNKNOWN_HOST == aError) {
3370 NS_ENSURE_ARG_POINTER(aURI);
3371 // Get the host
3372 nsAutoCString host;
3373 nsCOMPtr<nsIURI> innermostURI = NS_GetInnermostURI(aURI);
3374 innermostURI->GetHost(host);
3375 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3376 errorDescriptionID = "dnsNotFound2";
3377 error = "dnsNotFound";
3378 } else if (NS_ERROR_CONNECTION_REFUSED == aError ||
3379 NS_ERROR_PROXY_BAD_GATEWAY == aError) {
3380 NS_ENSURE_ARG_POINTER(aURI);
3381 addHostPort = true;
3382 error = "connectionFailure";
3383 } else if (NS_ERROR_NET_INTERRUPT == aError) {
3384 NS_ENSURE_ARG_POINTER(aURI);
3385 addHostPort = true;
3386 error = "netInterrupt";
3387 } else if (NS_ERROR_NET_TIMEOUT == aError ||
3388 NS_ERROR_PROXY_GATEWAY_TIMEOUT == aError ||
3389 NS_ERROR_NET_TIMEOUT_EXTERNAL == aError) {
3390 NS_ENSURE_ARG_POINTER(aURI);
3391 // Get the host
3392 nsAutoCString host;
3393 aURI->GetHost(host);
3394 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3395 error = "netTimeout";
3396 } else if (NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION == aError ||
3397 NS_ERROR_CSP_FORM_ACTION_VIOLATION == aError) {
3398 // CSP error
3399 cssClass.AssignLiteral("neterror");
3400 error = "cspBlocked";
3401 } else if (NS_ERROR_XFO_VIOLATION == aError) {
3402 // XFO error
3403 cssClass.AssignLiteral("neterror");
3404 error = "xfoBlocked";
3405 } else if (NS_ERROR_GET_MODULE(aError) == NS_ERROR_MODULE_SECURITY) {
3406 nsCOMPtr<nsINSSErrorsService> nsserr =
3407 do_GetService(NS_NSS_ERRORS_SERVICE_CONTRACTID);
3409 uint32_t errorClass;
3410 if (!nsserr || NS_FAILED(nsserr->GetErrorClass(aError, &errorClass))) {
3411 errorClass = nsINSSErrorsService::ERROR_CLASS_SSL_PROTOCOL;
3414 nsCOMPtr<nsITransportSecurityInfo> tsi;
3415 if (aFailedChannel) {
3416 aFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
3418 if (tsi) {
3419 uint32_t securityState;
3420 tsi->GetSecurityState(&securityState);
3421 if (securityState & nsIWebProgressListener::STATE_USES_SSL_3) {
3422 error = "sslv3Used";
3423 addHostPort = true;
3424 } else if (securityState &
3425 nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
3426 error = "weakCryptoUsed";
3427 addHostPort = true;
3429 } else {
3430 // No channel, let's obtain the generic error message
3431 if (nsserr) {
3432 nsserr->GetErrorMessage(aError, messageStr);
3435 // We don't have a message string here anymore but DisplayLoadError
3436 // requires a non-empty messageStr.
3437 messageStr.Truncate();
3438 messageStr.AssignLiteral(u" ");
3439 if (errorClass == nsINSSErrorsService::ERROR_CLASS_BAD_CERT) {
3440 error = "nssBadCert";
3442 // If this is an HTTP Strict Transport Security host or a pinned host
3443 // and the certificate is bad, don't allow overrides (RFC 6797 section
3444 // 12.1).
3445 bool isStsHost = false;
3446 bool isPinnedHost = false;
3447 OriginAttributes attrsForHSTS;
3448 if (aFailedChannel) {
3449 StoragePrincipalHelper::GetOriginAttributesForHSTS(aFailedChannel,
3450 attrsForHSTS);
3451 } else {
3452 attrsForHSTS = GetOriginAttributes();
3455 if (XRE_IsParentProcess()) {
3456 nsCOMPtr<nsISiteSecurityService> sss =
3457 do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
3458 NS_ENSURE_SUCCESS(rv, rv);
3459 rv = sss->IsSecureURI(aURI, attrsForHSTS, &isStsHost);
3460 NS_ENSURE_SUCCESS(rv, rv);
3461 } else {
3462 mozilla::dom::ContentChild* cc =
3463 mozilla::dom::ContentChild::GetSingleton();
3464 cc->SendIsSecureURI(aURI, attrsForHSTS, &isStsHost);
3466 nsCOMPtr<nsIPublicKeyPinningService> pkps =
3467 do_GetService(NS_PKPSERVICE_CONTRACTID, &rv);
3468 NS_ENSURE_SUCCESS(rv, rv);
3469 rv = pkps->HostHasPins(aURI, &isPinnedHost);
3471 if (Preferences::GetBool("browser.xul.error_pages.expert_bad_cert",
3472 false)) {
3473 cssClass.AssignLiteral("expertBadCert");
3476 // HSTS/pinning takes precedence over the expert bad cert pref. We
3477 // never want to show the "Add Exception" button for these sites.
3478 // In the future we should differentiate between an HSTS host and a
3479 // pinned host and display a more informative message to the user.
3480 if (isStsHost || isPinnedHost) {
3481 isBadStsCertError = true;
3482 cssClass.AssignLiteral("badStsCert");
3485 errorPage.Assign("certerror");
3486 } else {
3487 error = "nssFailure2";
3489 } else if (NS_ERROR_PHISHING_URI == aError ||
3490 NS_ERROR_MALWARE_URI == aError ||
3491 NS_ERROR_UNWANTED_URI == aError ||
3492 NS_ERROR_HARMFUL_URI == aError) {
3493 nsAutoCString host;
3494 aURI->GetHost(host);
3495 CopyUTF8toUTF16(host, *formatStrs.AppendElement());
3497 // Malware and phishing detectors may want to use an alternate error
3498 // page, but if the pref's not set, we'll fall back on the standard page
3499 nsAutoCString alternateErrorPage;
3500 nsresult rv = Preferences::GetCString("urlclassifier.alternate_error_page",
3501 alternateErrorPage);
3502 if (NS_SUCCEEDED(rv)) {
3503 errorPage.Assign(alternateErrorPage);
3506 if (NS_ERROR_PHISHING_URI == aError) {
3507 error = "deceptiveBlocked";
3508 } else if (NS_ERROR_MALWARE_URI == aError) {
3509 error = "malwareBlocked";
3510 } else if (NS_ERROR_UNWANTED_URI == aError) {
3511 error = "unwantedBlocked";
3512 } else if (NS_ERROR_HARMFUL_URI == aError) {
3513 error = "harmfulBlocked";
3516 cssClass.AssignLiteral("blacklist");
3517 } else if (NS_ERROR_CONTENT_CRASHED == aError) {
3518 errorPage.AssignLiteral("tabcrashed");
3519 error = "tabcrashed";
3521 RefPtr<EventTarget> handler = mChromeEventHandler;
3522 if (handler) {
3523 nsCOMPtr<Element> element = do_QueryInterface(handler);
3524 element->GetAttribute(u"crashedPageTitle"_ns, messageStr);
3527 // DisplayLoadError requires a non-empty messageStr to proceed and call
3528 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3529 // space which will be trimmed and thus treated as empty by the front-end.
3530 if (messageStr.IsEmpty()) {
3531 messageStr.AssignLiteral(u" ");
3533 } else if (NS_ERROR_FRAME_CRASHED == aError) {
3534 errorPage.AssignLiteral("framecrashed");
3535 error = "framecrashed";
3536 messageStr.AssignLiteral(u" ");
3537 } else if (NS_ERROR_BUILDID_MISMATCH == aError) {
3538 errorPage.AssignLiteral("restartrequired");
3539 error = "restartrequired";
3541 // DisplayLoadError requires a non-empty messageStr to proceed and call
3542 // LoadErrorPage. If the page doesn't have a title, we will use a blank
3543 // space which will be trimmed and thus treated as empty by the front-end.
3544 if (messageStr.IsEmpty()) {
3545 messageStr.AssignLiteral(u" ");
3547 } else {
3548 // Errors requiring simple formatting
3549 switch (aError) {
3550 case NS_ERROR_MALFORMED_URI:
3551 // URI is malformed
3552 error = "malformedURI";
3553 errorDescriptionID = "malformedURI2";
3554 break;
3555 case NS_ERROR_REDIRECT_LOOP:
3556 // Doc failed to load because the server generated too many redirects
3557 error = "redirectLoop";
3558 break;
3559 case NS_ERROR_UNKNOWN_SOCKET_TYPE:
3560 // Doc failed to load because PSM is not installed
3561 error = "unknownSocketType";
3562 break;
3563 case NS_ERROR_NET_RESET:
3564 // Doc failed to load because the server kept reseting the connection
3565 // before we could read any data from it
3566 error = "netReset";
3567 break;
3568 case NS_ERROR_DOCUMENT_NOT_CACHED:
3569 // Doc failed to load because the cache does not contain a copy of
3570 // the document.
3571 error = "notCached";
3572 break;
3573 case NS_ERROR_OFFLINE:
3574 // Doc failed to load because we are offline.
3575 error = "netOffline";
3576 break;
3577 case NS_ERROR_DOCUMENT_IS_PRINTMODE:
3578 // Doc navigation attempted while Printing or Print Preview
3579 error = "isprinting";
3580 break;
3581 case NS_ERROR_PORT_ACCESS_NOT_ALLOWED:
3582 // Port blocked for security reasons
3583 addHostPort = true;
3584 error = "deniedPortAccess";
3585 break;
3586 case NS_ERROR_UNKNOWN_PROXY_HOST:
3587 // Proxy hostname could not be resolved.
3588 error = "proxyResolveFailure";
3589 break;
3590 case NS_ERROR_PROXY_CONNECTION_REFUSED:
3591 case NS_ERROR_PROXY_FORBIDDEN:
3592 case NS_ERROR_PROXY_NOT_IMPLEMENTED:
3593 case NS_ERROR_PROXY_AUTHENTICATION_FAILED:
3594 case NS_ERROR_PROXY_TOO_MANY_REQUESTS:
3595 // Proxy connection was refused.
3596 error = "proxyConnectFailure";
3597 break;
3598 case NS_ERROR_INVALID_CONTENT_ENCODING:
3599 // Bad Content Encoding.
3600 error = "contentEncodingError";
3601 break;
3602 case NS_ERROR_UNSAFE_CONTENT_TYPE:
3603 // Channel refused to load from an unrecognized content type.
3604 error = "unsafeContentType";
3605 break;
3606 case NS_ERROR_CORRUPTED_CONTENT:
3607 // Broken Content Detected. e.g. Content-MD5 check failure.
3608 error = "corruptedContentErrorv2";
3609 break;
3610 case NS_ERROR_INTERCEPTION_FAILED:
3611 // ServiceWorker intercepted request, but something went wrong.
3612 error = "corruptedContentErrorv2";
3613 break;
3614 case NS_ERROR_NET_INADEQUATE_SECURITY:
3615 // Server negotiated bad TLS for HTTP/2.
3616 error = "inadequateSecurityError";
3617 addHostPort = true;
3618 break;
3619 case NS_ERROR_BLOCKED_BY_POLICY:
3620 case NS_ERROR_DOM_COOP_FAILED:
3621 case NS_ERROR_DOM_COEP_FAILED:
3622 // Page blocked by policy
3623 error = "blockedByPolicy";
3624 break;
3625 case NS_ERROR_NET_HTTP2_SENT_GOAWAY:
3626 case NS_ERROR_NET_HTTP3_PROTOCOL_ERROR:
3627 // HTTP/2 or HTTP/3 stack detected a protocol error
3628 error = "networkProtocolError";
3629 break;
3631 default:
3632 break;
3636 nsresult delegateErrorCode = aError;
3637 // If the HTTPS-Only Mode upgraded this request and the upgrade might have
3638 // caused this error, we replace the error-page with about:httpsonlyerror
3639 if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(aFailedChannel, aError)) {
3640 errorPage.AssignLiteral("httpsonlyerror");
3641 delegateErrorCode = NS_ERROR_HTTPS_ONLY;
3642 } else if (isBadStsCertError) {
3643 delegateErrorCode = NS_ERROR_BAD_HSTS_CERT;
3646 if (nsCOMPtr<nsILoadURIDelegate> loadURIDelegate = GetLoadURIDelegate()) {
3647 nsCOMPtr<nsIURI> errorPageURI;
3648 rv = loadURIDelegate->HandleLoadError(
3649 aURI, delegateErrorCode, NS_ERROR_GET_MODULE(delegateErrorCode),
3650 getter_AddRefs(errorPageURI));
3651 // If the docshell is going away there's no point in showing an error page.
3652 if (NS_FAILED(rv) || mIsBeingDestroyed) {
3653 *aDisplayedErrorPage = false;
3654 return NS_OK;
3657 if (errorPageURI) {
3658 *aDisplayedErrorPage =
3659 NS_SUCCEEDED(LoadErrorPage(errorPageURI, aURI, aFailedChannel));
3660 return NS_OK;
3664 // Test if the error should be displayed
3665 if (!error) {
3666 return NS_OK;
3669 if (!errorDescriptionID) {
3670 errorDescriptionID = error;
3673 Telemetry::AccumulateCategoricalKeyed(
3674 IsSubframe() ? "frame"_ns : "top"_ns,
3675 mozilla::dom::LoadErrorToTelemetryLabel(aError));
3677 // Test if the error needs to be formatted
3678 if (!messageStr.IsEmpty()) {
3679 // already obtained message
3680 } else {
3681 if (addHostPort) {
3682 // Build up the host:port string.
3683 nsAutoCString hostport;
3684 if (aURI) {
3685 aURI->GetHostPort(hostport);
3686 } else {
3687 hostport.Assign('?');
3689 CopyUTF8toUTF16(hostport, *formatStrs.AppendElement());
3692 nsAutoCString spec;
3693 rv = NS_ERROR_NOT_AVAILABLE;
3694 auto& nextFormatStr = *formatStrs.AppendElement();
3695 if (aURI) {
3696 // displaying "file://" is aesthetically unpleasing and could even be
3697 // confusing to the user
3698 if (SchemeIsFile(aURI)) {
3699 aURI->GetPathQueryRef(spec);
3700 } else {
3701 aURI->GetSpec(spec);
3704 nsCOMPtr<nsITextToSubURI> textToSubURI(
3705 do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv));
3706 if (NS_SUCCEEDED(rv)) {
3707 rv = textToSubURI->UnEscapeURIForUI(spec, nextFormatStr);
3709 } else {
3710 spec.Assign('?');
3712 if (NS_FAILED(rv)) {
3713 CopyUTF8toUTF16(spec, nextFormatStr);
3715 rv = NS_OK;
3717 nsAutoString str;
3718 rv =
3719 stringBundle->FormatStringFromName(errorDescriptionID, formatStrs, str);
3720 NS_ENSURE_SUCCESS(rv, rv);
3721 messageStr.Assign(str);
3724 // Display the error as a page or an alert prompt
3725 NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
3727 if ((NS_ERROR_NET_INTERRUPT == aError || NS_ERROR_NET_RESET == aError) &&
3728 SchemeIsHTTPS(aURI)) {
3729 // Maybe TLS intolerant. Treat this as an SSL error.
3730 error = "nssFailure2";
3733 if (mBrowsingContext->GetUseErrorPages()) {
3734 // Display an error page
3735 nsresult loadedPage =
3736 LoadErrorPage(aURI, aURL, errorPage.get(), error, messageStr.get(),
3737 cssClass.get(), aFailedChannel);
3738 *aDisplayedErrorPage = NS_SUCCEEDED(loadedPage);
3739 } else {
3740 // The prompter reqires that our private window has a document (or it
3741 // asserts). Satisfy that assertion now since GetDoc will force
3742 // creation of one if it hasn't already been created.
3743 if (mScriptGlobal) {
3744 Unused << mScriptGlobal->GetDoc();
3747 // Display a message box
3748 prompter->Alert(nullptr, messageStr.get());
3751 return NS_OK;
3754 #define PREF_SAFEBROWSING_ALLOWOVERRIDE "browser.safebrowsing.allowOverride"
3756 nsresult nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL,
3757 const char* aErrorPage,
3758 const char* aErrorType,
3759 const char16_t* aDescription,
3760 const char* aCSSClass,
3761 nsIChannel* aFailedChannel) {
3762 if (mIsBeingDestroyed) {
3763 return NS_ERROR_NOT_AVAILABLE;
3766 #if defined(DEBUG)
3767 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
3768 nsAutoCString chanName;
3769 if (aFailedChannel) {
3770 aFailedChannel->GetName(chanName);
3771 } else {
3772 chanName.AssignLiteral("<no channel>");
3775 MOZ_LOG(gDocShellLog, LogLevel::Debug,
3776 ("nsDocShell[%p]::LoadErrorPage(\"%s\", \"%s\", {...}, [%s])\n",
3777 this, aURI ? aURI->GetSpecOrDefault().get() : "",
3778 NS_ConvertUTF16toUTF8(aURL).get(), chanName.get()));
3780 #endif
3782 nsAutoCString url;
3783 if (aURI) {
3784 nsresult rv = aURI->GetSpec(url);
3785 NS_ENSURE_SUCCESS(rv, rv);
3786 } else if (aURL) {
3787 CopyUTF16toUTF8(MakeStringSpan(aURL), url);
3788 } else {
3789 return NS_ERROR_INVALID_POINTER;
3792 // Create a URL to pass all the error information through to the page.
3794 #undef SAFE_ESCAPE
3795 #define SAFE_ESCAPE(output, input, params) \
3796 if (NS_WARN_IF(!NS_Escape(input, output, params))) { \
3797 return NS_ERROR_OUT_OF_MEMORY; \
3800 nsCString escapedUrl, escapedError, escapedDescription, escapedCSSClass;
3801 SAFE_ESCAPE(escapedUrl, url, url_Path);
3802 SAFE_ESCAPE(escapedError, nsDependentCString(aErrorType), url_Path);
3803 SAFE_ESCAPE(escapedDescription, NS_ConvertUTF16toUTF8(aDescription),
3804 url_Path);
3805 if (aCSSClass) {
3806 nsCString cssClass(aCSSClass);
3807 SAFE_ESCAPE(escapedCSSClass, cssClass, url_Path);
3809 nsCString errorPageUrl("about:");
3810 errorPageUrl.AppendASCII(aErrorPage);
3811 errorPageUrl.AppendLiteral("?e=");
3813 errorPageUrl.AppendASCII(escapedError.get());
3814 errorPageUrl.AppendLiteral("&u=");
3815 errorPageUrl.AppendASCII(escapedUrl.get());
3816 if ((strcmp(aErrorPage, "blocked") == 0) &&
3817 Preferences::GetBool(PREF_SAFEBROWSING_ALLOWOVERRIDE, true)) {
3818 errorPageUrl.AppendLiteral("&o=1");
3820 if (!escapedCSSClass.IsEmpty()) {
3821 errorPageUrl.AppendLiteral("&s=");
3822 errorPageUrl.AppendASCII(escapedCSSClass.get());
3824 errorPageUrl.AppendLiteral("&c=UTF-8");
3826 nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
3827 int32_t cpsState;
3828 if (cps && NS_SUCCEEDED(cps->GetState(&cpsState)) &&
3829 cpsState == nsICaptivePortalService::LOCKED_PORTAL) {
3830 errorPageUrl.AppendLiteral("&captive=true");
3833 errorPageUrl.AppendLiteral("&d=");
3834 errorPageUrl.AppendASCII(escapedDescription.get());
3836 nsCOMPtr<nsIURI> errorPageURI;
3837 nsresult rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
3838 NS_ENSURE_SUCCESS(rv, rv);
3840 return LoadErrorPage(errorPageURI, aURI, aFailedChannel);
3843 nsresult nsDocShell::LoadErrorPage(nsIURI* aErrorURI, nsIURI* aFailedURI,
3844 nsIChannel* aFailedChannel) {
3845 mFailedChannel = aFailedChannel;
3846 mFailedURI = aFailedURI;
3847 mFailedLoadType = mLoadType;
3849 if (mLSHE) {
3850 // Abandon mLSHE's BFCache entry and create a new one. This way, if
3851 // we go back or forward to another SHEntry with the same doc
3852 // identifier, the error page won't persist.
3853 mLSHE->AbandonBFCacheEntry();
3856 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aErrorURI);
3857 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
3858 if (mBrowsingContext) {
3859 loadState->SetTriggeringSandboxFlags(mBrowsingContext->GetSandboxFlags());
3860 loadState->SetTriggeringWindowId(
3861 mBrowsingContext->GetCurrentInnerWindowId());
3862 nsPIDOMWindowInner* innerWin = mScriptGlobal->GetCurrentInnerWindow();
3863 if (innerWin) {
3864 loadState->SetTriggeringStorageAccess(innerWin->UsingStorageAccess());
3867 loadState->SetLoadType(LOAD_ERROR_PAGE);
3868 loadState->SetFirstParty(true);
3869 loadState->SetSourceBrowsingContext(mBrowsingContext);
3870 if (mozilla::SessionHistoryInParent() && mLoadingEntry) {
3871 // We keep the loading entry for the load that failed here. If the user
3872 // reloads we want to try to reload the original load, not the error page.
3873 loadState->SetLoadingSessionHistoryInfo(
3874 MakeUnique<LoadingSessionHistoryInfo>(*mLoadingEntry));
3876 return InternalLoad(loadState);
3879 NS_IMETHODIMP
3880 nsDocShell::Reload(uint32_t aReloadFlags) {
3881 if (!IsNavigationAllowed()) {
3882 return NS_OK; // JS may not handle returning of an error code
3885 NS_ASSERTION(((aReloadFlags & INTERNAL_LOAD_FLAGS_LOADURI_SETUP_FLAGS) == 0),
3886 "Reload command not updated to use load flags!");
3887 NS_ASSERTION((aReloadFlags & EXTRA_LOAD_FLAGS) == 0,
3888 "Don't pass these flags to Reload");
3890 uint32_t loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
3891 NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
3893 // Send notifications to the HistoryListener if any, about the impending
3894 // reload
3895 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
3896 if (mozilla::SessionHistoryInParent()) {
3897 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p Reload", this));
3898 bool forceReload = IsForceReloadType(loadType);
3899 if (!XRE_IsParentProcess()) {
3900 ++mPendingReloadCount;
3901 RefPtr<nsDocShell> docShell(this);
3902 nsCOMPtr<nsIDocumentViewer> viewer(mDocumentViewer);
3903 NS_ENSURE_STATE(viewer);
3905 bool okToUnload = true;
3906 MOZ_TRY(viewer->PermitUnload(&okToUnload));
3907 if (!okToUnload) {
3908 return NS_OK;
3911 RefPtr<Document> doc(GetDocument());
3912 RefPtr<BrowsingContext> browsingContext(mBrowsingContext);
3913 nsCOMPtr<nsIURI> currentURI(mCurrentURI);
3914 nsCOMPtr<nsIReferrerInfo> referrerInfo(mReferrerInfo);
3915 RefPtr<StopDetector> stopDetector = new StopDetector();
3916 nsCOMPtr<nsILoadGroup> loadGroup;
3917 GetLoadGroup(getter_AddRefs(loadGroup));
3918 if (loadGroup) {
3919 // loadGroup may be null in theory. In that case stopDetector just
3920 // doesn't do anything.
3921 loadGroup->AddRequest(stopDetector, nullptr);
3924 ContentChild::GetSingleton()->SendNotifyOnHistoryReload(
3925 mBrowsingContext, forceReload,
3926 [docShell, doc, loadType, browsingContext, currentURI, referrerInfo,
3927 loadGroup, stopDetector](
3928 std::tuple<bool, Maybe<NotNull<RefPtr<nsDocShellLoadState>>>,
3929 Maybe<bool>>&& aResult) {
3930 auto scopeExit = MakeScopeExit([loadGroup, stopDetector]() {
3931 if (loadGroup) {
3932 loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
3936 // Decrease mPendingReloadCount before any other early returns!
3937 if (--(docShell->mPendingReloadCount) > 0) {
3938 return;
3941 if (stopDetector->Canceled()) {
3942 return;
3944 bool canReload;
3945 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
3946 Maybe<bool> reloadingActiveEntry;
3948 std::tie(canReload, loadState, reloadingActiveEntry) = aResult;
3950 if (!canReload) {
3951 return;
3954 if (loadState.isSome()) {
3955 MOZ_LOG(
3956 gSHLog, LogLevel::Debug,
3957 ("nsDocShell %p Reload - LoadHistoryEntry", docShell.get()));
3958 loadState.ref()->SetNotifiedBeforeUnloadListeners(true);
3959 docShell->LoadHistoryEntry(loadState.ref(), loadType,
3960 reloadingActiveEntry.ref());
3961 } else {
3962 MOZ_LOG(gSHLog, LogLevel::Debug,
3963 ("nsDocShell %p ReloadDocument", docShell.get()));
3964 ReloadDocument(docShell, doc, loadType, browsingContext,
3965 currentURI, referrerInfo,
3966 /* aNotifiedBeforeUnloadListeners */ true);
3969 [](mozilla::ipc::ResponseRejectReason) {});
3970 } else {
3971 // Parent process
3972 bool canReload = false;
3973 Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
3974 Maybe<bool> reloadingActiveEntry;
3975 if (!mBrowsingContext->IsDiscarded()) {
3976 mBrowsingContext->Canonical()->NotifyOnHistoryReload(
3977 forceReload, canReload, loadState, reloadingActiveEntry);
3979 if (canReload) {
3980 if (loadState.isSome()) {
3981 MOZ_LOG(gSHLog, LogLevel::Debug,
3982 ("nsDocShell %p Reload - LoadHistoryEntry", this));
3983 LoadHistoryEntry(loadState.ref(), loadType,
3984 reloadingActiveEntry.ref());
3985 } else {
3986 MOZ_LOG(gSHLog, LogLevel::Debug,
3987 ("nsDocShell %p ReloadDocument", this));
3988 RefPtr<Document> doc = GetDocument();
3989 RefPtr<BrowsingContext> bc = mBrowsingContext;
3990 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
3991 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
3992 ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
3996 return NS_OK;
3999 bool canReload = true;
4000 if (rootSH) {
4001 rootSH->LegacySHistory()->NotifyOnHistoryReload(&canReload);
4004 if (!canReload) {
4005 return NS_OK;
4008 /* If you change this part of code, make sure bug 45297 does not re-occur */
4009 if (mOSHE) {
4010 nsCOMPtr<nsISHEntry> oshe = mOSHE;
4011 return LoadHistoryEntry(
4012 oshe, loadType,
4013 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4016 if (mLSHE) { // In case a reload happened before the current load is done
4017 nsCOMPtr<nsISHEntry> lshe = mLSHE;
4018 return LoadHistoryEntry(
4019 lshe, loadType,
4020 aReloadFlags & nsIWebNavigation::LOAD_FLAGS_USER_ACTIVATION);
4023 RefPtr<Document> doc = GetDocument();
4024 RefPtr<BrowsingContext> bc = mBrowsingContext;
4025 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
4026 nsCOMPtr<nsIReferrerInfo> referrerInfo = mReferrerInfo;
4027 return ReloadDocument(this, doc, loadType, bc, currentURI, referrerInfo);
4030 /* static */
4031 nsresult nsDocShell::ReloadDocument(nsDocShell* aDocShell, Document* aDocument,
4032 uint32_t aLoadType,
4033 BrowsingContext* aBrowsingContext,
4034 nsIURI* aCurrentURI,
4035 nsIReferrerInfo* aReferrerInfo,
4036 bool aNotifiedBeforeUnloadListeners) {
4037 if (!aDocument) {
4038 return NS_OK;
4041 // Do not inherit owner from document
4042 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
4043 nsAutoString srcdoc;
4044 nsIURI* baseURI = nullptr;
4045 nsCOMPtr<nsIURI> originalURI;
4046 nsCOMPtr<nsIURI> resultPrincipalURI;
4047 bool loadReplace = false;
4049 nsIPrincipal* triggeringPrincipal = aDocument->NodePrincipal();
4050 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
4051 uint32_t triggeringSandboxFlags = aDocument->GetSandboxFlags();
4052 uint64_t triggeringWindowId = aDocument->InnerWindowID();
4053 bool triggeringStorageAccess = aDocument->UsingStorageAccess();
4055 nsAutoString contentTypeHint;
4056 aDocument->GetContentType(contentTypeHint);
4058 if (aDocument->IsSrcdocDocument()) {
4059 aDocument->GetSrcdocData(srcdoc);
4060 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
4061 baseURI = aDocument->GetBaseURI();
4062 } else {
4063 srcdoc = VoidString();
4065 nsCOMPtr<nsIChannel> chan = aDocument->GetChannel();
4066 if (chan) {
4067 uint32_t loadFlags;
4068 chan->GetLoadFlags(&loadFlags);
4069 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
4070 nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
4071 if (httpChan) {
4072 httpChan->GetOriginalURI(getter_AddRefs(originalURI));
4075 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
4076 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
4079 if (!triggeringPrincipal) {
4080 MOZ_ASSERT(false, "Reload needs a valid triggeringPrincipal");
4081 return NS_ERROR_FAILURE;
4084 // Stack variables to ensure changes to the member variables don't affect to
4085 // the call.
4086 nsCOMPtr<nsIURI> currentURI = aCurrentURI;
4088 // Reload always rewrites result principal URI.
4089 Maybe<nsCOMPtr<nsIURI>> emplacedResultPrincipalURI;
4090 emplacedResultPrincipalURI.emplace(std::move(resultPrincipalURI));
4092 RefPtr<WindowContext> context = aBrowsingContext->GetCurrentWindowContext();
4093 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(currentURI);
4094 loadState->SetReferrerInfo(aReferrerInfo);
4095 loadState->SetOriginalURI(originalURI);
4096 loadState->SetMaybeResultPrincipalURI(emplacedResultPrincipalURI);
4097 loadState->SetLoadReplace(loadReplace);
4098 loadState->SetTriggeringPrincipal(triggeringPrincipal);
4099 loadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
4100 loadState->SetTriggeringWindowId(triggeringWindowId);
4101 loadState->SetTriggeringStorageAccess(triggeringStorageAccess);
4102 loadState->SetPrincipalToInherit(triggeringPrincipal);
4103 loadState->SetCsp(csp);
4104 loadState->SetInternalLoadFlags(flags);
4105 loadState->SetTypeHint(NS_ConvertUTF16toUTF8(contentTypeHint));
4106 loadState->SetLoadType(aLoadType);
4107 loadState->SetFirstParty(true);
4108 loadState->SetSrcdocData(srcdoc);
4109 loadState->SetSourceBrowsingContext(aBrowsingContext);
4110 loadState->SetBaseURI(baseURI);
4111 loadState->SetHasValidUserGestureActivation(
4112 context && context->HasValidTransientUserGestureActivation());
4114 loadState->SetTextDirectiveUserActivation(
4115 aDocument->ConsumeTextDirectiveUserActivation() ||
4116 loadState->HasValidUserGestureActivation());
4118 loadState->SetNotifiedBeforeUnloadListeners(aNotifiedBeforeUnloadListeners);
4119 return aDocShell->InternalLoad(loadState);
4122 NS_IMETHODIMP
4123 nsDocShell::Stop(uint32_t aStopFlags) {
4124 // Revoke any pending event related to content viewer restoration
4125 mRestorePresentationEvent.Revoke();
4127 if (mLoadType == LOAD_ERROR_PAGE) {
4128 if (mLSHE) {
4129 // Since error page loads never unset mLSHE, do so now
4130 SetHistoryEntryAndUpdateBC(Some(nullptr), Some<nsISHEntry*>(mLSHE));
4132 mActiveEntryIsLoadingFromSessionHistory = false;
4134 mFailedChannel = nullptr;
4135 mFailedURI = nullptr;
4138 if (nsIWebNavigation::STOP_CONTENT & aStopFlags) {
4139 // Stop the document loading and animations
4140 if (mDocumentViewer) {
4141 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4142 viewer->Stop();
4144 } else if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4145 // Stop the document loading only
4146 if (mDocumentViewer) {
4147 RefPtr<Document> doc = mDocumentViewer->GetDocument();
4148 if (doc) {
4149 doc->StopDocumentLoad();
4154 if (nsIWebNavigation::STOP_NETWORK & aStopFlags) {
4155 // Suspend any timers that were set for this loader. We'll clear
4156 // them out for good in CreateDocumentViewer.
4157 if (mRefreshURIList) {
4158 SuspendRefreshURIs();
4159 mSavedRefreshURIList.swap(mRefreshURIList);
4160 mRefreshURIList = nullptr;
4163 // XXXbz We could also pass |this| to nsIURILoader::Stop. That will
4164 // just call Stop() on us as an nsIDocumentLoader... We need fewer
4165 // redundant apis!
4166 Stop();
4168 // Clear out mChannelToDisconnectOnPageHide. This page won't go in the
4169 // BFCache now, and the Stop above will have removed the DocumentChannel
4170 // from the loadgroup.
4171 mChannelToDisconnectOnPageHide = 0;
4174 for (auto* child : mChildList.ForwardRange()) {
4175 nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryObject(child));
4176 if (shellAsNav) {
4177 shellAsNav->Stop(aStopFlags);
4181 return NS_OK;
4184 NS_IMETHODIMP
4185 nsDocShell::GetDocument(Document** aDocument) {
4186 NS_ENSURE_ARG_POINTER(aDocument);
4187 NS_ENSURE_SUCCESS(EnsureDocumentViewer(), NS_ERROR_FAILURE);
4189 RefPtr<Document> doc = mDocumentViewer->GetDocument();
4190 if (!doc) {
4191 return NS_ERROR_NOT_AVAILABLE;
4194 doc.forget(aDocument);
4195 return NS_OK;
4198 NS_IMETHODIMP
4199 nsDocShell::GetCurrentURI(nsIURI** aURI) {
4200 NS_ENSURE_ARG_POINTER(aURI);
4202 nsCOMPtr<nsIURI> uri = mCurrentURI;
4203 uri.forget(aURI);
4204 return NS_OK;
4207 NS_IMETHODIMP
4208 nsDocShell::GetSessionHistoryXPCOM(nsISupports** aSessionHistory) {
4209 NS_ENSURE_ARG_POINTER(aSessionHistory);
4210 RefPtr<ChildSHistory> shistory = GetSessionHistory();
4211 shistory.forget(aSessionHistory);
4212 return NS_OK;
4215 //*****************************************************************************
4216 // nsDocShell::nsIWebPageDescriptor
4217 //*****************************************************************************
4219 NS_IMETHODIMP
4220 nsDocShell::LoadPageAsViewSource(nsIDocShell* aOtherDocShell,
4221 const nsAString& aURI) {
4222 if (!aOtherDocShell) {
4223 return NS_ERROR_INVALID_POINTER;
4225 nsCOMPtr<nsIURI> newURI;
4226 nsresult rv = NS_NewURI(getter_AddRefs(newURI), aURI);
4227 if (NS_FAILED(rv)) {
4228 return rv;
4231 RefPtr<nsDocShellLoadState> loadState;
4232 uint32_t cacheKey;
4233 auto* otherDocShell = nsDocShell::Cast(aOtherDocShell);
4234 if (mozilla::SessionHistoryInParent()) {
4235 loadState = new nsDocShellLoadState(newURI);
4236 if (!otherDocShell->FillLoadStateFromCurrentEntry(*loadState)) {
4237 return NS_ERROR_INVALID_POINTER;
4239 cacheKey = otherDocShell->GetCacheKeyFromCurrentEntry().valueOr(0);
4240 } else {
4241 nsCOMPtr<nsISHEntry> entry;
4242 bool isOriginalSHE;
4243 otherDocShell->GetCurrentSHEntry(getter_AddRefs(entry), &isOriginalSHE);
4244 if (!entry) {
4245 return NS_ERROR_INVALID_POINTER;
4247 rv = entry->CreateLoadInfo(getter_AddRefs(loadState));
4248 NS_ENSURE_SUCCESS(rv, rv);
4249 entry->GetCacheKey(&cacheKey);
4250 loadState->SetURI(newURI);
4251 loadState->SetSHEntry(nullptr);
4254 // We're doing a load of the page, via an API that
4255 // is only exposed to system code. The triggering principal for this load
4256 // should be the system principal.
4257 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
4258 loadState->SetOriginalURI(nullptr);
4259 loadState->SetResultPrincipalURI(nullptr);
4261 return InternalLoad(loadState, Some(cacheKey));
4264 NS_IMETHODIMP
4265 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor) {
4266 MOZ_ASSERT(aPageDescriptor, "Null out param?");
4268 *aPageDescriptor = nullptr;
4270 nsISHEntry* src = mOSHE ? mOSHE : mLSHE;
4271 if (src) {
4272 nsCOMPtr<nsISHEntry> dest;
4274 nsresult rv = src->Clone(getter_AddRefs(dest));
4275 if (NS_FAILED(rv)) {
4276 return rv;
4279 // null out inappropriate cloned attributes...
4280 dest->SetParent(nullptr);
4281 dest->SetIsSubFrame(false);
4283 return CallQueryInterface(dest, aPageDescriptor);
4286 return NS_ERROR_NOT_AVAILABLE;
4289 already_AddRefed<nsIInputStream> nsDocShell::GetPostDataFromCurrentEntry()
4290 const {
4291 nsCOMPtr<nsIInputStream> postData;
4292 if (mozilla::SessionHistoryInParent()) {
4293 if (mActiveEntry) {
4294 postData = mActiveEntry->GetPostData();
4295 } else if (mLoadingEntry) {
4296 postData = mLoadingEntry->mInfo.GetPostData();
4298 } else {
4299 if (mOSHE) {
4300 postData = mOSHE->GetPostData();
4301 } else if (mLSHE) {
4302 postData = mLSHE->GetPostData();
4306 return postData.forget();
4309 Maybe<uint32_t> nsDocShell::GetCacheKeyFromCurrentEntry() const {
4310 if (mozilla::SessionHistoryInParent()) {
4311 if (mActiveEntry) {
4312 return Some(mActiveEntry->GetCacheKey());
4315 if (mLoadingEntry) {
4316 return Some(mLoadingEntry->mInfo.GetCacheKey());
4318 } else {
4319 if (mOSHE) {
4320 return Some(mOSHE->GetCacheKey());
4323 if (mLSHE) {
4324 return Some(mLSHE->GetCacheKey());
4328 return Nothing();
4331 bool nsDocShell::FillLoadStateFromCurrentEntry(
4332 nsDocShellLoadState& aLoadState) {
4333 if (mLoadingEntry) {
4334 mLoadingEntry->mInfo.FillLoadInfo(aLoadState);
4335 return true;
4337 if (mActiveEntry) {
4338 mActiveEntry->FillLoadInfo(aLoadState);
4339 return true;
4341 return false;
4344 //*****************************************************************************
4345 // nsDocShell::nsIBaseWindow
4346 //*****************************************************************************
4348 NS_IMETHODIMP
4349 nsDocShell::InitWindow(nativeWindow aParentNativeWindow,
4350 nsIWidget* aParentWidget, int32_t aX, int32_t aY,
4351 int32_t aWidth, int32_t aHeight) {
4352 SetParentWidget(aParentWidget);
4353 SetPositionAndSize(aX, aY, aWidth, aHeight, 0);
4354 NS_ENSURE_TRUE(Initialize(), NS_ERROR_FAILURE);
4356 return NS_OK;
4359 NS_IMETHODIMP
4360 nsDocShell::Destroy() {
4361 // XXX: We allow this function to be called just once. If you are going to
4362 // reset new variables in this function, please make sure the variables will
4363 // never be re-initialized. Adding assertions to check |mIsBeingDestroyed|
4364 // in the setter functions for the variables would be enough.
4365 if (mIsBeingDestroyed) {
4366 return NS_ERROR_DOCSHELL_DYING;
4369 NS_ASSERTION(mItemType == typeContent || mItemType == typeChrome,
4370 "Unexpected item type in docshell");
4372 nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
4373 if (serv) {
4374 const char* msg = mItemType == typeContent
4375 ? NS_WEBNAVIGATION_DESTROY
4376 : NS_CHROME_WEBNAVIGATION_DESTROY;
4377 serv->NotifyObservers(GetAsSupports(this), msg, nullptr);
4380 mIsBeingDestroyed = true;
4382 // Brak the cycle with the initial client, if present.
4383 mInitialClientSource.reset();
4385 // Make sure to blow away our mLoadingURI just in case. No loads
4386 // from inside this pagehide.
4387 mLoadingURI = nullptr;
4389 // Fire unload event before we blow anything away.
4390 (void)FirePageHideNotification(true);
4392 // Clear pointers to any detached nsEditorData that's lying
4393 // around in shistory entries. Breaks cycle. See bug 430921.
4394 if (mOSHE) {
4395 mOSHE->SetEditorData(nullptr);
4397 if (mLSHE) {
4398 mLSHE->SetEditorData(nullptr);
4401 // Note: mContentListener can be null if Init() failed and we're being
4402 // called from the destructor.
4403 if (mContentListener) {
4404 mContentListener->DropDocShellReference();
4405 mContentListener->SetParentContentListener(nullptr);
4406 // Note that we do NOT set mContentListener to null here; that
4407 // way if someone tries to do a load in us after this point
4408 // the nsDSURIContentListener will block it. All of which
4409 // means that we should do this before calling Stop(), of
4410 // course.
4413 // Stop any URLs that are currently being loaded...
4414 Stop(nsIWebNavigation::STOP_ALL);
4416 mEditorData = nullptr;
4418 // Save the state of the current document, before destroying the window.
4419 // This is needed to capture the state of a frameset when the new document
4420 // causes the frameset to be destroyed...
4421 PersistLayoutHistoryState();
4423 // Remove this docshell from its parent's child list
4424 nsCOMPtr<nsIDocShellTreeItem> docShellParentAsItem =
4425 do_QueryInterface(GetAsSupports(mParent));
4426 if (docShellParentAsItem) {
4427 docShellParentAsItem->RemoveChild(this);
4430 if (mDocumentViewer) {
4431 mDocumentViewer->Close(nullptr);
4432 mDocumentViewer->Destroy();
4433 mDocumentViewer = nullptr;
4436 nsDocLoader::Destroy();
4438 mParentWidget = nullptr;
4439 SetCurrentURIInternal(nullptr);
4441 if (mScriptGlobal) {
4442 mScriptGlobal->DetachFromDocShell(!mWillChangeProcess);
4443 mScriptGlobal = nullptr;
4446 if (GetSessionHistory()) {
4447 // We want to destroy these content viewers now rather than
4448 // letting their destruction wait for the session history
4449 // entries to get garbage collected. (Bug 488394)
4450 GetSessionHistory()->EvictLocalDocumentViewers();
4453 if (mWillChangeProcess && !mBrowsingContext->IsDiscarded()) {
4454 mBrowsingContext->PrepareForProcessChange();
4457 SetTreeOwner(nullptr);
4459 mBrowserChild = nullptr;
4461 mChromeEventHandler = nullptr;
4463 // Cancel any timers that were set for this docshell; this is needed
4464 // to break the cycle between us and the timers.
4465 CancelRefreshURITimers();
4467 return NS_OK;
4470 double nsDocShell::GetWidgetCSSToDeviceScale() {
4471 if (mParentWidget) {
4472 return mParentWidget->GetDefaultScale().scale;
4474 if (nsCOMPtr<nsIBaseWindow> ownerWindow = do_QueryInterface(mTreeOwner)) {
4475 return ownerWindow->GetWidgetCSSToDeviceScale();
4477 return 1.0;
4480 NS_IMETHODIMP
4481 nsDocShell::GetDevicePixelsPerDesktopPixel(double* aScale) {
4482 if (mParentWidget) {
4483 *aScale = mParentWidget->GetDesktopToDeviceScale().scale;
4484 return NS_OK;
4487 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4488 if (ownerWindow) {
4489 return ownerWindow->GetDevicePixelsPerDesktopPixel(aScale);
4492 *aScale = 1.0;
4493 return NS_OK;
4496 NS_IMETHODIMP
4497 nsDocShell::SetPosition(int32_t aX, int32_t aY) {
4498 mBounds.MoveTo(aX, aY);
4500 if (mDocumentViewer) {
4501 NS_ENSURE_SUCCESS(mDocumentViewer->Move(aX, aY), NS_ERROR_FAILURE);
4504 return NS_OK;
4507 NS_IMETHODIMP
4508 nsDocShell::SetPositionDesktopPix(int32_t aX, int32_t aY) {
4509 nsCOMPtr<nsIBaseWindow> ownerWindow(do_QueryInterface(mTreeOwner));
4510 if (ownerWindow) {
4511 return ownerWindow->SetPositionDesktopPix(aX, aY);
4514 double scale = 1.0;
4515 GetDevicePixelsPerDesktopPixel(&scale);
4516 return SetPosition(NSToIntRound(aX * scale), NSToIntRound(aY * scale));
4519 NS_IMETHODIMP
4520 nsDocShell::GetPosition(int32_t* aX, int32_t* aY) {
4521 return GetPositionAndSize(aX, aY, nullptr, nullptr);
4524 NS_IMETHODIMP
4525 nsDocShell::SetSize(int32_t aWidth, int32_t aHeight, bool aRepaint) {
4526 int32_t x = 0, y = 0;
4527 GetPosition(&x, &y);
4528 return SetPositionAndSize(x, y, aWidth, aHeight,
4529 aRepaint ? nsIBaseWindow::eRepaint : 0);
4532 NS_IMETHODIMP
4533 nsDocShell::GetSize(int32_t* aWidth, int32_t* aHeight) {
4534 return GetPositionAndSize(nullptr, nullptr, aWidth, aHeight);
4537 NS_IMETHODIMP
4538 nsDocShell::SetPositionAndSize(int32_t aX, int32_t aY, int32_t aWidth,
4539 int32_t aHeight, uint32_t aFlags) {
4540 mBounds.SetRect(aX, aY, aWidth, aHeight);
4542 // Hold strong ref, since SetBounds can make us null out mDocumentViewer
4543 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4544 if (viewer) {
4545 uint32_t cvflags = (aFlags & nsIBaseWindow::eDelayResize)
4546 ? nsIDocumentViewer::eDelayResize
4547 : 0;
4548 // XXX Border figured in here or is that handled elsewhere?
4549 nsresult rv = viewer->SetBoundsWithFlags(mBounds, cvflags);
4550 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
4553 return NS_OK;
4556 NS_IMETHODIMP
4557 nsDocShell::GetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4558 int32_t* aHeight) {
4559 if (mParentWidget) {
4560 // ensure size is up-to-date if window has changed resolution
4561 LayoutDeviceIntRect r = mParentWidget->GetClientBounds();
4562 SetPositionAndSize(mBounds.X(), mBounds.Y(), r.Width(), r.Height(), 0);
4565 // We should really consider just getting this information from
4566 // our window instead of duplicating the storage and code...
4567 if (aWidth || aHeight) {
4568 // Caller wants to know our size; make sure to give them up to
4569 // date information.
4570 RefPtr<Document> doc(do_GetInterface(GetAsSupports(mParent)));
4571 if (doc) {
4572 doc->FlushPendingNotifications(FlushType::Layout);
4576 DoGetPositionAndSize(aX, aY, aWidth, aHeight);
4577 return NS_OK;
4580 void nsDocShell::DoGetPositionAndSize(int32_t* aX, int32_t* aY, int32_t* aWidth,
4581 int32_t* aHeight) {
4582 if (aX) {
4583 *aX = mBounds.X();
4585 if (aY) {
4586 *aY = mBounds.Y();
4588 if (aWidth) {
4589 *aWidth = mBounds.Width();
4591 if (aHeight) {
4592 *aHeight = mBounds.Height();
4596 NS_IMETHODIMP
4597 nsDocShell::SetDimensions(DimensionRequest&& aRequest) {
4598 return NS_ERROR_NOT_IMPLEMENTED;
4601 NS_IMETHODIMP
4602 nsDocShell::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
4603 int32_t* aY, int32_t* aCX, int32_t* aCY) {
4604 return NS_ERROR_NOT_IMPLEMENTED;
4607 NS_IMETHODIMP
4608 nsDocShell::Repaint(bool aForce) {
4609 PresShell* presShell = GetPresShell();
4610 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4612 RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
4613 NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
4615 viewManager->InvalidateAllViews();
4616 return NS_OK;
4619 NS_IMETHODIMP
4620 nsDocShell::GetParentWidget(nsIWidget** aParentWidget) {
4621 NS_ENSURE_ARG_POINTER(aParentWidget);
4623 *aParentWidget = mParentWidget;
4624 NS_IF_ADDREF(*aParentWidget);
4626 return NS_OK;
4629 NS_IMETHODIMP
4630 nsDocShell::SetParentWidget(nsIWidget* aParentWidget) {
4631 MOZ_ASSERT(!mIsBeingDestroyed);
4632 mParentWidget = aParentWidget;
4634 return NS_OK;
4637 NS_IMETHODIMP
4638 nsDocShell::GetParentNativeWindow(nativeWindow* aParentNativeWindow) {
4639 NS_ENSURE_ARG_POINTER(aParentNativeWindow);
4641 if (mParentWidget) {
4642 *aParentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
4643 } else {
4644 *aParentNativeWindow = nullptr;
4647 return NS_OK;
4650 NS_IMETHODIMP
4651 nsDocShell::SetParentNativeWindow(nativeWindow aParentNativeWindow) {
4652 return NS_ERROR_NOT_IMPLEMENTED;
4655 NS_IMETHODIMP
4656 nsDocShell::GetNativeHandle(nsAString& aNativeHandle) {
4657 // the nativeHandle should be accessed from nsIAppWindow
4658 return NS_ERROR_NOT_IMPLEMENTED;
4661 NS_IMETHODIMP
4662 nsDocShell::GetVisibility(bool* aVisibility) {
4663 NS_ENSURE_ARG_POINTER(aVisibility);
4665 *aVisibility = false;
4667 if (!mDocumentViewer) {
4668 return NS_OK;
4671 PresShell* presShell = GetPresShell();
4672 if (!presShell) {
4673 return NS_OK;
4676 // get the view manager
4677 nsViewManager* vm = presShell->GetViewManager();
4678 NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
4680 // get the root view
4681 nsView* view = vm->GetRootView(); // views are not ref counted
4682 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
4684 // if our root view is hidden, we are not visible
4685 if (view->GetVisibility() == ViewVisibility::Hide) {
4686 return NS_OK;
4689 // otherwise, we must walk up the document and view trees checking
4690 // for a hidden view, unless we're an off screen browser, which
4691 // would make this test meaningless.
4693 RefPtr<nsDocShell> docShell = this;
4694 RefPtr<nsDocShell> parentItem = docShell->GetInProcessParentDocshell();
4695 while (parentItem) {
4696 // Null-check for crash in bug 267804
4697 if (!parentItem->GetPresShell()) {
4698 MOZ_ASSERT_UNREACHABLE("parent docshell has null pres shell");
4699 return NS_OK;
4702 vm = docShell->GetPresShell()->GetViewManager();
4703 if (vm) {
4704 view = vm->GetRootView();
4707 if (view) {
4708 view = view->GetParent(); // anonymous inner view
4709 if (view) {
4710 view = view->GetParent(); // subdocumentframe's view
4714 nsIFrame* frame = view ? view->GetFrame() : nullptr;
4715 if (frame && !frame->IsVisibleConsideringAncestors(
4716 nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
4717 return NS_OK;
4720 docShell = parentItem;
4721 parentItem = docShell->GetInProcessParentDocshell();
4724 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4725 if (!treeOwnerAsWin) {
4726 *aVisibility = true;
4727 return NS_OK;
4730 // Check with the tree owner as well to give embedders a chance to
4731 // expose visibility as well.
4732 nsresult rv = treeOwnerAsWin->GetVisibility(aVisibility);
4733 if (rv == NS_ERROR_NOT_IMPLEMENTED) {
4734 // The tree owner had no opinion on our visibility.
4735 *aVisibility = true;
4736 return NS_OK;
4738 return rv;
4741 void nsDocShell::ActivenessMaybeChanged() {
4742 const bool isActive = mBrowsingContext->IsActive();
4743 if (RefPtr<PresShell> presShell = GetPresShell()) {
4744 presShell->ActivenessMaybeChanged();
4747 // Tell the window about it
4748 if (mScriptGlobal) {
4749 mScriptGlobal->SetIsBackground(!isActive);
4750 if (RefPtr<Document> doc = mScriptGlobal->GetExtantDoc()) {
4751 // Update orientation when the top-level browsing context becomes active.
4752 if (isActive && mBrowsingContext->IsTop()) {
4753 // We only care about the top-level browsing context.
4754 auto orientation = mBrowsingContext->GetOrientationLock();
4755 ScreenOrientation::UpdateActiveOrientationLock(orientation);
4758 doc->PostVisibilityUpdateEvent();
4762 // Tell the nsDOMNavigationTiming about it
4763 RefPtr<nsDOMNavigationTiming> timing = mTiming;
4764 if (!timing && mDocumentViewer) {
4765 if (Document* doc = mDocumentViewer->GetDocument()) {
4766 timing = doc->GetNavigationTiming();
4769 if (timing) {
4770 timing->NotifyDocShellStateChanged(
4771 isActive ? nsDOMNavigationTiming::DocShellState::eActive
4772 : nsDOMNavigationTiming::DocShellState::eInactive);
4775 // Restart or stop meta refresh timers if necessary
4776 if (mDisableMetaRefreshWhenInactive) {
4777 if (isActive) {
4778 ResumeRefreshURIs();
4779 } else {
4780 SuspendRefreshURIs();
4784 if (InputTaskManager::CanSuspendInputEvent()) {
4785 mBrowsingContext->Group()->UpdateInputTaskManagerIfNeeded(isActive);
4789 NS_IMETHODIMP
4790 nsDocShell::SetDefaultLoadFlags(uint32_t aDefaultLoadFlags) {
4791 if (!mWillChangeProcess) {
4792 // Intentionally ignoring handling discarded browsing contexts.
4793 Unused << mBrowsingContext->SetDefaultLoadFlags(aDefaultLoadFlags);
4794 } else {
4795 // Bug 1623565: DevTools tries to clean up defaultLoadFlags on
4796 // shutdown. Sorry DevTools, your DocShell is in another process.
4797 NS_WARNING("nsDocShell::SetDefaultLoadFlags called on Zombie DocShell");
4799 return NS_OK;
4802 NS_IMETHODIMP
4803 nsDocShell::GetDefaultLoadFlags(uint32_t* aDefaultLoadFlags) {
4804 *aDefaultLoadFlags = mBrowsingContext->GetDefaultLoadFlags();
4805 return NS_OK;
4808 NS_IMETHODIMP
4809 nsDocShell::GetFailedChannel(nsIChannel** aFailedChannel) {
4810 NS_ENSURE_ARG_POINTER(aFailedChannel);
4811 Document* doc = GetDocument();
4812 if (!doc) {
4813 *aFailedChannel = nullptr;
4814 return NS_OK;
4816 NS_IF_ADDREF(*aFailedChannel = doc->GetFailedChannel());
4817 return NS_OK;
4820 NS_IMETHODIMP
4821 nsDocShell::SetVisibility(bool aVisibility) {
4822 // Show()/Hide() may change mDocumentViewer.
4823 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
4824 if (!viewer) {
4825 return NS_OK;
4827 if (aVisibility) {
4828 viewer->Show();
4829 } else {
4830 viewer->Hide();
4833 return NS_OK;
4836 NS_IMETHODIMP
4837 nsDocShell::GetEnabled(bool* aEnabled) {
4838 NS_ENSURE_ARG_POINTER(aEnabled);
4839 *aEnabled = true;
4840 return NS_ERROR_NOT_IMPLEMENTED;
4843 NS_IMETHODIMP
4844 nsDocShell::SetEnabled(bool aEnabled) { return NS_ERROR_NOT_IMPLEMENTED; }
4846 NS_IMETHODIMP
4847 nsDocShell::GetMainWidget(nsIWidget** aMainWidget) {
4848 // We don't create our own widget, so simply return the parent one.
4849 return GetParentWidget(aMainWidget);
4852 NS_IMETHODIMP
4853 nsDocShell::GetTitle(nsAString& aTitle) {
4854 aTitle = mTitle;
4855 return NS_OK;
4858 NS_IMETHODIMP
4859 nsDocShell::SetTitle(const nsAString& aTitle) {
4860 // Avoid unnecessary updates of the title if the URI and the title haven't
4861 // changed.
4862 if (mTitleValidForCurrentURI && mTitle == aTitle) {
4863 return NS_OK;
4866 // Store local title
4867 mTitle = aTitle;
4868 mTitleValidForCurrentURI = true;
4870 // When title is set on the top object it should then be passed to the
4871 // tree owner.
4872 if (mBrowsingContext->IsTop()) {
4873 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
4874 if (treeOwnerAsWin) {
4875 treeOwnerAsWin->SetTitle(aTitle);
4879 if (mCurrentURI && mLoadType != LOAD_ERROR_PAGE) {
4880 UpdateGlobalHistoryTitle(mCurrentURI);
4883 // Update SessionHistory with the document's title.
4884 if (mLoadType != LOAD_BYPASS_HISTORY && mLoadType != LOAD_ERROR_PAGE) {
4885 SetTitleOnHistoryEntry(true);
4888 return NS_OK;
4891 void nsDocShell::SetTitleOnHistoryEntry(bool aUpdateEntryInSessionHistory) {
4892 if (mOSHE) {
4893 mOSHE->SetTitle(mTitle);
4896 if (mActiveEntry && mBrowsingContext) {
4897 mActiveEntry->SetTitle(mTitle);
4898 if (aUpdateEntryInSessionHistory) {
4899 if (XRE_IsParentProcess()) {
4900 SessionHistoryEntry* entry =
4901 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
4902 if (entry) {
4903 entry->SetTitle(mTitle);
4905 } else {
4906 mozilla::Unused
4907 << ContentChild::GetSingleton()->SendSessionHistoryEntryTitle(
4908 mBrowsingContext, mTitle);
4914 nsPoint nsDocShell::GetCurScrollPos() {
4915 nsPoint scrollPos;
4916 if (ScrollContainerFrame* sf = GetRootScrollContainerFrame()) {
4917 scrollPos = sf->GetVisualViewportOffset();
4919 return scrollPos;
4922 nsresult nsDocShell::SetCurScrollPosEx(int32_t aCurHorizontalPos,
4923 int32_t aCurVerticalPos) {
4924 ScrollContainerFrame* sf = GetRootScrollContainerFrame();
4925 NS_ENSURE_TRUE(sf, NS_ERROR_FAILURE);
4927 ScrollMode scrollMode =
4928 sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
4930 nsPoint targetPos(aCurHorizontalPos, aCurVerticalPos);
4931 sf->ScrollTo(targetPos, scrollMode);
4933 // Set the visual viewport offset as well.
4935 RefPtr<PresShell> presShell = GetPresShell();
4936 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
4938 nsPresContext* presContext = presShell->GetPresContext();
4939 NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
4941 // Only the root content document can have a distinct visual viewport offset.
4942 if (!presContext->IsRootContentDocumentCrossProcess()) {
4943 return NS_OK;
4946 // Not on a platform with a distinct visual viewport - don't bother setting
4947 // the visual viewport offset.
4948 if (!presShell->IsVisualViewportSizeSet()) {
4949 return NS_OK;
4952 presShell->ScrollToVisual(targetPos, layers::FrameMetrics::eMainThread,
4953 scrollMode);
4955 return NS_OK;
4958 void nsDocShell::SetScrollbarPreference(mozilla::ScrollbarPreference aPref) {
4959 if (mScrollbarPref == aPref) {
4960 return;
4962 mScrollbarPref = aPref;
4963 auto* ps = GetPresShell();
4964 if (!ps) {
4965 return;
4967 nsIFrame* rootScrollContainerFrame = ps->GetRootScrollContainerFrame();
4968 if (!rootScrollContainerFrame) {
4969 return;
4971 ps->FrameNeedsReflow(rootScrollContainerFrame,
4972 IntrinsicDirty::FrameAncestorsAndDescendants,
4973 NS_FRAME_IS_DIRTY);
4976 //*****************************************************************************
4977 // nsDocShell::nsIRefreshURI
4978 //*****************************************************************************
4980 NS_IMETHODIMP
4981 nsDocShell::RefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
4982 uint32_t aDelay) {
4983 MOZ_ASSERT(!mIsBeingDestroyed);
4985 NS_ENSURE_ARG(aURI);
4987 /* Check if Meta refresh/redirects are permitted. Some
4988 * embedded applications may not want to do this.
4989 * Must do this before sending out NOTIFY_REFRESH events
4990 * because listeners may have side effects (e.g. displaying a
4991 * button to manually trigger the refresh later).
4993 bool allowRedirects = true;
4994 GetAllowMetaRedirects(&allowRedirects);
4995 if (!allowRedirects) {
4996 return NS_OK;
4999 // If any web progress listeners are listening for NOTIFY_REFRESH events,
5000 // give them a chance to block this refresh.
5001 bool sameURI;
5002 nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
5003 if (NS_FAILED(rv)) {
5004 sameURI = false;
5006 if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
5007 return NS_OK;
5010 nsCOMPtr<nsITimerCallback> refreshTimer =
5011 new nsRefreshTimer(this, aURI, aPrincipal, aDelay);
5013 BusyFlags busyFlags = GetBusyFlags();
5015 if (!mRefreshURIList) {
5016 mRefreshURIList = nsArray::Create();
5019 if (busyFlags & BUSY_FLAGS_BUSY ||
5020 (!mBrowsingContext->IsActive() && mDisableMetaRefreshWhenInactive)) {
5021 // We don't want to create the timer right now. Instead queue up the
5022 // request and trigger the timer in EndPageLoad() or whenever we become
5023 // active.
5024 mRefreshURIList->AppendElement(refreshTimer);
5025 } else {
5026 // There is no page loading going on right now. Create the
5027 // timer and fire it right away.
5028 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5029 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
5031 nsCOMPtr<nsITimer> timer;
5032 MOZ_TRY_VAR(timer, NS_NewTimerWithCallback(refreshTimer, aDelay,
5033 nsITimer::TYPE_ONE_SHOT));
5035 mRefreshURIList->AppendElement(timer); // owning timer ref
5037 return NS_OK;
5040 nsresult nsDocShell::ForceRefreshURIFromTimer(nsIURI* aURI,
5041 nsIPrincipal* aPrincipal,
5042 uint32_t aDelay,
5043 nsITimer* aTimer) {
5044 MOZ_ASSERT(aTimer, "Must have a timer here");
5046 // Remove aTimer from mRefreshURIList if needed
5047 if (mRefreshURIList) {
5048 uint32_t n = 0;
5049 mRefreshURIList->GetLength(&n);
5051 for (uint32_t i = 0; i < n; ++i) {
5052 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5053 if (timer == aTimer) {
5054 mRefreshURIList->RemoveElementAt(i);
5055 break;
5060 return ForceRefreshURI(aURI, aPrincipal, aDelay);
5063 NS_IMETHODIMP
5064 nsDocShell::ForceRefreshURI(nsIURI* aURI, nsIPrincipal* aPrincipal,
5065 uint32_t aDelay) {
5066 NS_ENSURE_ARG(aURI);
5068 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
5069 loadState->SetOriginalURI(mCurrentURI);
5070 loadState->SetResultPrincipalURI(aURI);
5071 loadState->SetResultPrincipalURIIsSome(true);
5072 loadState->SetKeepResultPrincipalURIIfSet(true);
5073 loadState->SetIsMetaRefresh(true);
5075 // Set the triggering pricipal to aPrincipal if available, or current
5076 // document's principal otherwise.
5077 nsCOMPtr<nsIPrincipal> principal = aPrincipal;
5078 RefPtr<Document> doc = GetDocument();
5079 if (!principal) {
5080 if (!doc) {
5081 return NS_ERROR_FAILURE;
5083 principal = doc->NodePrincipal();
5085 loadState->SetTriggeringPrincipal(principal);
5086 if (doc) {
5087 loadState->SetCsp(doc->GetCsp());
5088 loadState->SetHasValidUserGestureActivation(
5089 doc->HasValidTransientUserGestureActivation());
5091 loadState->SetTextDirectiveUserActivation(
5092 doc->ConsumeTextDirectiveUserActivation() ||
5093 loadState->HasValidUserGestureActivation());
5094 loadState->SetTriggeringSandboxFlags(doc->GetSandboxFlags());
5095 loadState->SetTriggeringWindowId(doc->InnerWindowID());
5096 loadState->SetTriggeringStorageAccess(doc->UsingStorageAccess());
5099 loadState->SetPrincipalIsExplicit(true);
5101 /* Check if this META refresh causes a redirection
5102 * to another site.
5104 bool equalUri = false;
5105 nsresult rv = aURI->Equals(mCurrentURI, &equalUri);
5107 nsCOMPtr<nsIReferrerInfo> referrerInfo;
5108 if (NS_SUCCEEDED(rv) && !equalUri && aDelay <= REFRESH_REDIRECT_TIMER) {
5109 /* It is a META refresh based redirection within the threshold time
5110 * we have in mind (15000 ms as defined by REFRESH_REDIRECT_TIMER).
5111 * Pass a REPLACE flag to LoadURI().
5113 loadState->SetLoadType(LOAD_REFRESH_REPLACE);
5115 /* For redirects we mimic HTTP, which passes the
5116 * original referrer.
5117 * We will pass in referrer but will not send to server
5119 if (mReferrerInfo) {
5120 referrerInfo = static_cast<ReferrerInfo*>(mReferrerInfo.get())
5121 ->CloneWithNewSendReferrer(false);
5123 } else {
5124 loadState->SetLoadType(LOAD_REFRESH);
5125 /* We do need to pass in a referrer, but we don't want it to
5126 * be sent to the server.
5127 * For most refreshes the current URI is an appropriate
5128 * internal referrer.
5130 referrerInfo = new ReferrerInfo(mCurrentURI, ReferrerPolicy::_empty, false);
5133 loadState->SetReferrerInfo(referrerInfo);
5134 loadState->SetLoadFlags(
5135 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL);
5136 loadState->SetFirstParty(true);
5139 * LoadURI(...) will cancel all refresh timers... This causes the
5140 * Timer and its refreshData instance to be released...
5142 LoadURI(loadState, false);
5144 return NS_OK;
5147 static const char16_t* SkipASCIIWhitespace(const char16_t* aStart,
5148 const char16_t* aEnd) {
5149 const char16_t* iter = aStart;
5150 while (iter != aEnd && mozilla::IsAsciiWhitespace(*iter)) {
5151 ++iter;
5153 return iter;
5156 static std::tuple<const char16_t*, const char16_t*> ExtractURLString(
5157 const char16_t* aPosition, const char16_t* aEnd) {
5158 MOZ_ASSERT(aPosition != aEnd);
5160 // 1. Let urlString be the substring of input from the code point at
5161 // position to the end of the string.
5162 const char16_t* urlStart = aPosition;
5163 const char16_t* urlEnd = aEnd;
5165 // 2. If the code point in input pointed to by position is U+0055 (U) or
5166 // U+0075 (u), then advance position to the next code point.
5167 // Otherwise, jump to the step labeled skip quotes.
5168 if (*aPosition == 'U' || *aPosition == 'u') {
5169 ++aPosition;
5171 // 3. If the code point in input pointed to by position is U+0052 (R) or
5172 // U+0072 (r), then advance position to the next code point.
5173 // Otherwise, jump to the step labeled parse.
5174 if (aPosition == aEnd || (*aPosition != 'R' && *aPosition != 'r')) {
5175 return std::make_tuple(urlStart, urlEnd);
5178 ++aPosition;
5180 // 4. If the code point in input pointed to by position is U+004C (L) or
5181 // U+006C (l), then advance position to the next code point.
5182 // Otherwise, jump to the step labeled parse.
5183 if (aPosition == aEnd || (*aPosition != 'L' && *aPosition != 'l')) {
5184 return std::make_tuple(urlStart, urlEnd);
5187 ++aPosition;
5189 // 5. Skip ASCII whitespace within input given position.
5190 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5192 // 6. If the code point in input pointed to by position is U+003D (=),
5193 // then advance position to the next code point. Otherwise, jump to
5194 // the step labeled parse.
5195 if (aPosition == aEnd || *aPosition != '=') {
5196 return std::make_tuple(urlStart, urlEnd);
5199 ++aPosition;
5201 // 7. Skip ASCII whitespace within input given position.
5202 aPosition = SkipASCIIWhitespace(aPosition, aEnd);
5205 // 8. Skip quotes: If the code point in input pointed to by position is
5206 // U+0027 (') or U+0022 ("), then let quote be that code point, and
5207 // advance position to the next code point. Otherwise, let quote be
5208 // the empty string.
5209 Maybe<char> quote;
5210 if (aPosition != aEnd && (*aPosition == '\'' || *aPosition == '"')) {
5211 quote.emplace(*aPosition);
5212 ++aPosition;
5215 // 9. Set urlString to the substring of input from the code point at
5216 // position to the end of the string.
5217 urlStart = aPosition;
5218 urlEnd = aEnd;
5220 // 10. If quote is not the empty string, and there is a code point in
5221 // urlString equal to quote, then truncate urlString at that code
5222 // point, so that it and all subsequent code points are removed.
5223 const char16_t* quotePos;
5224 if (quote.isSome() &&
5225 (quotePos = nsCharTraits<char16_t>::find(
5226 urlStart, std::distance(urlStart, aEnd), quote.value()))) {
5227 urlEnd = quotePos;
5230 return std::make_tuple(urlStart, urlEnd);
5233 void nsDocShell::SetupRefreshURIFromHeader(Document* aDocument,
5234 const nsAString& aHeader) {
5235 if (mIsBeingDestroyed) {
5236 return;
5239 const char16_t* position = aHeader.BeginReading();
5240 const char16_t* end = aHeader.EndReading();
5242 // See
5243 // https://html.spec.whatwg.org/#pragma-directives:shared-declarative-refresh-steps.
5245 // 3. Skip ASCII whitespace
5246 position = SkipASCIIWhitespace(position, end);
5248 // 4. Let time be 0.
5249 CheckedInt<uint32_t> milliSeconds;
5251 // 5. Collect a sequence of code points that are ASCII digits
5252 const char16_t* digitsStart = position;
5253 while (position != end && mozilla::IsAsciiDigit(*position)) {
5254 ++position;
5257 if (position == digitsStart) {
5258 // 6. If timeString is the empty string, then:
5259 // 1. If the code point in input pointed to by position is not U+002E
5260 // (.), then return.
5261 if (position == end || *position != '.') {
5262 return;
5264 } else {
5265 // 7. Otherwise, set time to the result of parsing timeString using the
5266 // rules for parsing non-negative integers.
5267 nsContentUtils::ParseHTMLIntegerResultFlags result;
5268 uint32_t seconds =
5269 nsContentUtils::ParseHTMLInteger(digitsStart, position, &result);
5270 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_Negative));
5271 if (result & nsContentUtils::eParseHTMLInteger_Error) {
5272 // The spec assumes no errors here (since we only pass ASCII digits in),
5273 // but we can still overflow, so this block should deal with that (and
5274 // only that).
5275 MOZ_ASSERT(!(result & nsContentUtils::eParseHTMLInteger_ErrorOverflow));
5276 return;
5278 MOZ_ASSERT(
5279 !(result & nsContentUtils::eParseHTMLInteger_DidNotConsumeAllInput));
5281 milliSeconds = seconds;
5282 milliSeconds *= 1000;
5283 if (!milliSeconds.isValid()) {
5284 return;
5288 // 8. Collect a sequence of code points that are ASCII digits and U+002E FULL
5289 // STOP characters (.) from input given position. Ignore any collected
5290 // characters.
5291 while (position != end &&
5292 (mozilla::IsAsciiDigit(*position) || *position == '.')) {
5293 ++position;
5296 // 9. Let urlRecord be document's URL.
5297 nsCOMPtr<nsIURI> urlRecord(aDocument->GetDocumentURI());
5299 // 10. If position is not past the end of input
5300 if (position != end) {
5301 // 1. If the code point in input pointed to by position is not U+003B (;),
5302 // U+002C (,), or ASCII whitespace, then return.
5303 if (*position != ';' && *position != ',' &&
5304 !mozilla::IsAsciiWhitespace(*position)) {
5305 return;
5308 // 2. Skip ASCII whitespace within input given position.
5309 position = SkipASCIIWhitespace(position, end);
5311 // 3. If the code point in input pointed to by position is U+003B (;) or
5312 // U+002C (,), then advance position to the next code point.
5313 if (position != end && (*position == ';' || *position == ',')) {
5314 ++position;
5316 // 4. Skip ASCII whitespace within input given position.
5317 position = SkipASCIIWhitespace(position, end);
5320 // 11. If position is not past the end of input, then:
5321 if (position != end) {
5322 const char16_t* urlStart;
5323 const char16_t* urlEnd;
5325 // 1-10. See ExtractURLString.
5326 std::tie(urlStart, urlEnd) = ExtractURLString(position, end);
5328 // 11. Parse: Parse urlString relative to document. If that fails, return.
5329 // Otherwise, set urlRecord to the resulting URL record.
5330 nsresult rv =
5331 NS_NewURI(getter_AddRefs(urlRecord),
5332 Substring(urlStart, std::distance(urlStart, urlEnd)),
5333 /* charset = */ nullptr, aDocument->GetDocBaseURI());
5334 NS_ENSURE_SUCCESS_VOID(rv);
5338 nsIPrincipal* principal = aDocument->NodePrincipal();
5339 nsCOMPtr<nsIScriptSecurityManager> securityManager =
5340 nsContentUtils::GetSecurityManager();
5341 nsresult rv = securityManager->CheckLoadURIWithPrincipal(
5342 principal, urlRecord,
5343 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT,
5344 aDocument->InnerWindowID());
5345 NS_ENSURE_SUCCESS_VOID(rv);
5347 bool isjs = true;
5348 rv = NS_URIChainHasFlags(
5349 urlRecord, nsIProtocolHandler::URI_OPENING_EXECUTES_SCRIPT, &isjs);
5350 NS_ENSURE_SUCCESS_VOID(rv);
5352 if (isjs) {
5353 return;
5356 RefreshURI(urlRecord, principal, milliSeconds.value());
5359 static void DoCancelRefreshURITimers(nsIMutableArray* aTimerList) {
5360 if (!aTimerList) {
5361 return;
5364 uint32_t n = 0;
5365 aTimerList->GetLength(&n);
5367 while (n) {
5368 nsCOMPtr<nsITimer> timer(do_QueryElementAt(aTimerList, --n));
5370 aTimerList->RemoveElementAt(n); // bye bye owning timer ref
5372 if (timer) {
5373 timer->Cancel();
5378 NS_IMETHODIMP
5379 nsDocShell::CancelRefreshURITimers() {
5380 DoCancelRefreshURITimers(mRefreshURIList);
5381 DoCancelRefreshURITimers(mSavedRefreshURIList);
5382 DoCancelRefreshURITimers(mBFCachedRefreshURIList);
5383 mRefreshURIList = nullptr;
5384 mSavedRefreshURIList = nullptr;
5385 mBFCachedRefreshURIList = nullptr;
5387 return NS_OK;
5390 NS_IMETHODIMP
5391 nsDocShell::GetRefreshPending(bool* aResult) {
5392 if (!mRefreshURIList) {
5393 *aResult = false;
5394 return NS_OK;
5397 uint32_t count;
5398 nsresult rv = mRefreshURIList->GetLength(&count);
5399 if (NS_SUCCEEDED(rv)) {
5400 *aResult = (count != 0);
5402 return rv;
5405 void nsDocShell::RefreshURIToQueue() {
5406 if (mRefreshURIList) {
5407 uint32_t n = 0;
5408 mRefreshURIList->GetLength(&n);
5410 for (uint32_t i = 0; i < n; ++i) {
5411 nsCOMPtr<nsITimer> timer = do_QueryElementAt(mRefreshURIList, i);
5412 if (!timer) {
5413 continue; // this must be a nsRefreshURI already
5416 // Replace this timer object with a nsRefreshTimer object.
5417 nsCOMPtr<nsITimerCallback> callback;
5418 timer->GetCallback(getter_AddRefs(callback));
5420 timer->Cancel();
5422 mRefreshURIList->ReplaceElementAt(callback, i);
5427 NS_IMETHODIMP
5428 nsDocShell::SuspendRefreshURIs() {
5429 RefreshURIToQueue();
5431 // Suspend refresh URIs for our child shells as well.
5432 for (auto* child : mChildList.ForwardRange()) {
5433 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5434 if (shell) {
5435 shell->SuspendRefreshURIs();
5439 return NS_OK;
5442 NS_IMETHODIMP
5443 nsDocShell::ResumeRefreshURIs() {
5444 RefreshURIFromQueue();
5446 // Resume refresh URIs for our child shells as well.
5447 for (auto* child : mChildList.ForwardRange()) {
5448 nsCOMPtr<nsIDocShell> shell = do_QueryObject(child);
5449 if (shell) {
5450 shell->ResumeRefreshURIs();
5454 return NS_OK;
5457 nsresult nsDocShell::RefreshURIFromQueue() {
5458 if (!mRefreshURIList) {
5459 return NS_OK;
5461 uint32_t n = 0;
5462 mRefreshURIList->GetLength(&n);
5464 while (n) {
5465 nsCOMPtr<nsITimerCallback> refreshInfo =
5466 do_QueryElementAt(mRefreshURIList, --n);
5468 if (refreshInfo) {
5469 // This is the nsRefreshTimer object, waiting to be
5470 // setup in a timer object and fired.
5471 // Create the timer and trigger it.
5472 uint32_t delay = static_cast<nsRefreshTimer*>(
5473 static_cast<nsITimerCallback*>(refreshInfo))
5474 ->GetDelay();
5475 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
5476 if (win) {
5477 nsCOMPtr<nsITimer> timer;
5478 NS_NewTimerWithCallback(getter_AddRefs(timer), refreshInfo, delay,
5479 nsITimer::TYPE_ONE_SHOT);
5481 if (timer) {
5482 // Replace the nsRefreshTimer element in the queue with
5483 // its corresponding timer object, so that in case another
5484 // load comes through before the timer can go off, the timer will
5485 // get cancelled in CancelRefreshURITimer()
5486 mRefreshURIList->ReplaceElementAt(timer, n);
5492 return NS_OK;
5495 static bool IsFollowupPartOfMultipart(nsIRequest* aRequest) {
5496 nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(aRequest);
5497 bool firstPart = false;
5498 return multiPartChannel &&
5499 NS_SUCCEEDED(multiPartChannel->GetIsFirstPart(&firstPart)) &&
5500 !firstPart;
5503 nsresult nsDocShell::Embed(nsIDocumentViewer* aDocumentViewer,
5504 WindowGlobalChild* aWindowActor,
5505 bool aIsTransientAboutBlank, bool aPersist,
5506 nsIRequest* aRequest, nsIURI* aPreviousURI) {
5507 // Save the LayoutHistoryState of the previous document, before
5508 // setting up new document
5509 PersistLayoutHistoryState();
5511 nsresult rv = SetupNewViewer(aDocumentViewer, aWindowActor);
5512 NS_ENSURE_SUCCESS(rv, rv);
5514 // XXX What if SetupNewViewer fails?
5515 if (mozilla::SessionHistoryInParent() ? !!mLoadingEntry : !!mLSHE) {
5516 // Set history.state
5517 SetDocCurrentStateObj(mLSHE,
5518 mLoadingEntry ? &mLoadingEntry->mInfo : nullptr);
5521 if (mLSHE) {
5522 // Restore the editing state, if it's stored in session history.
5523 if (mLSHE->HasDetachedEditor()) {
5524 ReattachEditorToWindow(mLSHE);
5527 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
5530 if (!aIsTransientAboutBlank && mozilla::SessionHistoryInParent() &&
5531 !IsFollowupPartOfMultipart(aRequest)) {
5532 bool expired = false;
5533 uint32_t cacheKey = 0;
5534 nsCOMPtr<nsICacheInfoChannel> cacheChannel = do_QueryInterface(aRequest);
5535 if (cacheChannel) {
5536 // Check if the page has expired from cache
5537 uint32_t expTime = 0;
5538 cacheChannel->GetCacheTokenExpirationTime(&expTime);
5539 uint32_t now = PRTimeToSeconds(PR_Now());
5540 if (expTime <= now) {
5541 expired = true;
5544 // The checks for updating cache key are similar to the old session
5545 // history in OnNewURI. Try to update the cache key if
5546 // - we should update session history and aren't doing a session
5547 // history load.
5548 // - we're doing a forced reload.
5549 if (((!mLoadingEntry || !mLoadingEntry->mLoadIsFromSessionHistory) &&
5550 mBrowsingContext->ShouldUpdateSessionHistory(mLoadType)) ||
5551 IsForceReloadType(mLoadType)) {
5552 cacheChannel->GetCacheKey(&cacheKey);
5556 MOZ_LOG(gSHLog, LogLevel::Debug, ("document %p Embed", this));
5557 MoveLoadingToActiveEntry(aPersist, expired, cacheKey, aPreviousURI);
5560 bool updateHistory = true;
5562 // Determine if this type of load should update history
5563 switch (mLoadType) {
5564 case LOAD_NORMAL_REPLACE:
5565 case LOAD_REFRESH_REPLACE:
5566 case LOAD_STOP_CONTENT_AND_REPLACE:
5567 case LOAD_RELOAD_BYPASS_CACHE:
5568 case LOAD_RELOAD_BYPASS_PROXY:
5569 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
5570 case LOAD_REPLACE_BYPASS_CACHE:
5571 updateHistory = false;
5572 break;
5573 default:
5574 break;
5577 if (!updateHistory) {
5578 SetLayoutHistoryState(nullptr);
5581 return NS_OK;
5584 //*****************************************************************************
5585 // nsDocShell::nsIWebProgressListener
5586 //*****************************************************************************
5588 NS_IMETHODIMP
5589 nsDocShell::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5590 int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
5591 int32_t aCurTotalProgress,
5592 int32_t aMaxTotalProgress) {
5593 return NS_OK;
5596 NS_IMETHODIMP
5597 nsDocShell::OnStateChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5598 uint32_t aStateFlags, nsresult aStatus) {
5599 if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
5600 // Save timing statistics.
5601 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5602 nsCOMPtr<nsIURI> uri;
5603 channel->GetURI(getter_AddRefs(uri));
5604 nsAutoCString aURI;
5605 uri->GetAsciiSpec(aURI);
5607 if (this == aProgress) {
5608 mozilla::Unused << MaybeInitTiming();
5609 mTiming->NotifyFetchStart(uri,
5610 ConvertLoadTypeToNavigationType(mLoadType));
5611 // If we are starting a DocumentChannel, we need to pass the timing
5612 // statistics so that should a process switch occur, the starting type can
5613 // be passed to the new DocShell running in the other content process.
5614 if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aRequest)) {
5615 docChannel->SetNavigationTiming(mTiming);
5619 // Page has begun to load
5620 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_BEFORE_PAGE_LOAD);
5622 if ((aStateFlags & STATE_RESTORING) == 0) {
5623 if (SessionStorePlatformCollection()) {
5624 if (IsForceReloadType(mLoadType)) {
5625 if (WindowContext* windowContext =
5626 mBrowsingContext->GetCurrentWindowContext()) {
5627 SessionStoreChild::From(windowContext->GetWindowGlobalChild())
5628 ->ResetSessionStore(mBrowsingContext,
5629 mBrowsingContext->GetSessionStoreEpoch());
5634 } else if ((~aStateFlags & (STATE_TRANSFERRING | STATE_IS_DOCUMENT)) == 0) {
5635 // Page is loading
5636 mBusyFlags = (BusyFlags)(BUSY_FLAGS_BUSY | BUSY_FLAGS_PAGE_LOADING);
5637 } else if ((aStateFlags & STATE_STOP) && (aStateFlags & STATE_IS_NETWORK)) {
5638 // Page has finished loading
5639 mBusyFlags = BUSY_FLAGS_NONE;
5642 if ((~aStateFlags & (STATE_IS_DOCUMENT | STATE_STOP)) == 0) {
5643 nsCOMPtr<nsIWebProgress> webProgress =
5644 do_QueryInterface(GetAsSupports(this));
5645 // Is the document stop notification for this document?
5646 if (aProgress == webProgress.get()) {
5647 nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
5648 EndPageLoad(aProgress, channel, aStatus);
5651 // note that redirect state changes will go through here as well, but it
5652 // is better to handle those in OnRedirectStateChange where more
5653 // information is available.
5654 return NS_OK;
5657 NS_IMETHODIMP
5658 nsDocShell::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
5659 nsIURI* aURI, uint32_t aFlags) {
5660 // Since we've now changed Documents, notify the BrowsingContext that we've
5661 // changed. Ideally we'd just let the BrowsingContext do this when it
5662 // changes the current window global, but that happens before this and we
5663 // have a lot of tests that depend on the specific ordering of messages.
5664 bool isTopLevel = false;
5665 if (XRE_IsParentProcess() &&
5666 !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT) &&
5667 NS_SUCCEEDED(aProgress->GetIsTopLevel(&isTopLevel)) && isTopLevel) {
5668 GetBrowsingContext()->Canonical()->UpdateSecurityState();
5670 return NS_OK;
5673 void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
5674 nsIChannel* aNewChannel,
5675 uint32_t aRedirectFlags,
5676 uint32_t aStateFlags) {
5677 NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
5678 "Calling OnRedirectStateChange when there is no redirect");
5680 if (!(aStateFlags & STATE_IS_DOCUMENT)) {
5681 return; // not a toplevel document
5684 nsCOMPtr<nsIURI> oldURI, newURI;
5685 aOldChannel->GetURI(getter_AddRefs(oldURI));
5686 aNewChannel->GetURI(getter_AddRefs(newURI));
5687 if (!oldURI || !newURI) {
5688 return;
5691 // DocumentChannel adds redirect chain to global history in the parent
5692 // process. The redirect chain can't be queried from the content process, so
5693 // there's no need to update global history here.
5694 RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
5695 if (!docChannel) {
5696 // Below a URI visit is saved (see AddURIVisit method doc).
5697 // The visit chain looks something like:
5698 // ...
5699 // Site N - 1
5700 // => Site N
5701 // (redirect to =>) Site N + 1 (we are here!)
5703 // Get N - 1 and transition type
5704 nsCOMPtr<nsIURI> previousURI;
5705 uint32_t previousFlags = 0;
5706 ExtractLastVisit(aOldChannel, getter_AddRefs(previousURI), &previousFlags);
5708 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL ||
5709 net::ChannelIsPost(aOldChannel)) {
5710 // 1. Internal redirects are ignored because they are specific to the
5711 // channel implementation.
5712 // 2. POSTs are not saved by global history.
5714 // Regardless, we need to propagate the previous visit to the new
5715 // channel.
5716 SaveLastVisit(aNewChannel, previousURI, previousFlags);
5717 } else {
5718 // Get the HTTP response code, if available.
5719 uint32_t responseStatus = 0;
5720 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aOldChannel);
5721 if (httpChannel) {
5722 Unused << httpChannel->GetResponseStatus(&responseStatus);
5725 // Add visit N -1 => N
5726 AddURIVisit(oldURI, previousURI, previousFlags, responseStatus);
5728 // Since N + 1 could be the final destination, we will not save N => N + 1
5729 // here. OnNewURI will do that, so we will cache it.
5730 SaveLastVisit(aNewChannel, oldURI, aRedirectFlags);
5734 if (!(aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) &&
5735 mLoadType & (LOAD_CMD_RELOAD | LOAD_CMD_HISTORY)) {
5736 mLoadType = LOAD_NORMAL_REPLACE;
5737 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
5741 NS_IMETHODIMP
5742 nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5743 nsresult aStatus, const char16_t* aMessage) {
5744 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5745 return NS_OK;
5748 NS_IMETHODIMP
5749 nsDocShell::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
5750 uint32_t aState) {
5751 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5752 return NS_OK;
5755 NS_IMETHODIMP
5756 nsDocShell::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
5757 nsIRequest* aRequest, uint32_t aEvent) {
5758 MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
5759 return NS_OK;
5762 already_AddRefed<nsIURIFixupInfo> nsDocShell::KeywordToURI(
5763 const nsACString& aKeyword, bool aIsPrivateContext) {
5764 nsCOMPtr<nsIURIFixupInfo> info;
5765 if (!XRE_IsContentProcess()) {
5766 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
5767 if (uriFixup) {
5768 uriFixup->KeywordToURI(aKeyword, aIsPrivateContext, getter_AddRefs(info));
5771 return info.forget();
5774 /* static */
5775 already_AddRefed<nsIURI> nsDocShell::MaybeFixBadCertDomainErrorURI(
5776 nsIChannel* aChannel, nsIURI* aUrl) {
5777 if (!aChannel) {
5778 return nullptr;
5781 nsresult rv = NS_OK;
5782 nsAutoCString host;
5783 rv = aUrl->GetAsciiHost(host);
5784 if (NS_WARN_IF(NS_FAILED(rv))) {
5785 return nullptr;
5788 // Return if fixup enable pref is turned off.
5789 if (!mozilla::StaticPrefs::security_bad_cert_domain_error_url_fix_enabled()) {
5790 return nullptr;
5793 // Return if scheme is not HTTPS.
5794 if (!SchemeIsHTTPS(aUrl)) {
5795 return nullptr;
5798 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
5799 if (!info) {
5800 return nullptr;
5803 // Skip doing the fixup if our channel was redirected, because we
5804 // shouldn't be guessing things about the post-redirect URI.
5805 if (!info->RedirectChain().IsEmpty()) {
5806 return nullptr;
5809 int32_t port = 0;
5810 rv = aUrl->GetPort(&port);
5811 if (NS_WARN_IF(NS_FAILED(rv))) {
5812 return nullptr;
5815 // Don't fix up hosts with ports.
5816 if (port != -1) {
5817 return nullptr;
5820 // Don't fix up localhost url.
5821 if (host == "localhost") {
5822 return nullptr;
5825 // Don't fix up hostnames with IP address.
5826 if (net_IsValidIPv4Addr(host) || net_IsValidIPv6Addr(host)) {
5827 return nullptr;
5830 nsAutoCString userPass;
5831 rv = aUrl->GetUserPass(userPass);
5832 if (NS_WARN_IF(NS_FAILED(rv))) {
5833 return nullptr;
5836 // Security - URLs with user / password info should NOT be modified.
5837 if (!userPass.IsEmpty()) {
5838 return nullptr;
5841 nsCOMPtr<nsITransportSecurityInfo> tsi;
5842 rv = aChannel->GetSecurityInfo(getter_AddRefs(tsi));
5843 if (NS_WARN_IF(NS_FAILED(rv))) {
5844 return nullptr;
5847 if (NS_WARN_IF(!tsi)) {
5848 return nullptr;
5851 nsCOMPtr<nsIX509Cert> cert;
5852 rv = tsi->GetServerCert(getter_AddRefs(cert));
5853 if (NS_WARN_IF(NS_FAILED(rv) || !cert)) {
5854 return nullptr;
5857 nsTArray<uint8_t> certBytes;
5858 rv = cert->GetRawDER(certBytes);
5859 if (NS_FAILED(rv)) {
5860 return nullptr;
5863 mozilla::pkix::Input serverCertInput;
5864 mozilla::pkix::Result result =
5865 serverCertInput.Init(certBytes.Elements(), certBytes.Length());
5866 if (result != mozilla::pkix::Success) {
5867 return nullptr;
5870 constexpr auto wwwPrefix = "www."_ns;
5871 nsAutoCString newHost;
5872 if (StringBeginsWith(host, wwwPrefix)) {
5873 // Try www.example.com -> example.com
5874 newHost.Assign(Substring(host, wwwPrefix.Length()));
5875 } else {
5876 // Try example.com -> www.example.com
5877 newHost.Assign(wwwPrefix);
5878 newHost.Append(host);
5881 mozilla::pkix::Input newHostInput;
5882 result = newHostInput.Init(
5883 BitwiseCast<const uint8_t*, const char*>(newHost.BeginReading()),
5884 newHost.Length());
5885 if (result != mozilla::pkix::Success) {
5886 return nullptr;
5889 // Because certificate verification returned Result::ERROR_BAD_CERT_DOMAIN /
5890 // SSL_ERROR_BAD_CERT_DOMAIN, a chain was built and we know whether or not
5891 // the root was a built-in.
5892 bool rootIsBuiltIn;
5893 if (NS_FAILED(tsi->GetIsBuiltCertChainRootBuiltInRoot(&rootIsBuiltIn))) {
5894 return nullptr;
5896 mozilla::psm::SkipInvalidSANsForNonBuiltInRootsPolicy nameMatchingPolicy(
5897 rootIsBuiltIn);
5899 // Check if the certificate is valid for the new hostname.
5900 result = mozilla::pkix::CheckCertHostname(serverCertInput, newHostInput,
5901 nameMatchingPolicy);
5902 if (result != mozilla::pkix::Success) {
5903 return nullptr;
5906 nsCOMPtr<nsIURI> newURI;
5907 Unused << NS_MutateURI(aUrl).SetHost(newHost).Finalize(
5908 getter_AddRefs(newURI));
5910 return newURI.forget();
5913 /* static */
5914 already_AddRefed<nsIURI> nsDocShell::AttemptURIFixup(
5915 nsIChannel* aChannel, nsresult aStatus,
5916 const mozilla::Maybe<nsCString>& aOriginalURIString, uint32_t aLoadType,
5917 bool aIsTopFrame, bool aAllowKeywordFixup, bool aUsePrivateBrowsing,
5918 bool aNotifyKeywordSearchLoading, nsIInputStream** aNewPostData,
5919 bool* outWasSchemelessInput) {
5920 if (aStatus != NS_ERROR_UNKNOWN_HOST && aStatus != NS_ERROR_NET_RESET &&
5921 aStatus != NS_ERROR_CONNECTION_REFUSED &&
5922 aStatus !=
5923 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
5924 return nullptr;
5927 if (!(aLoadType == LOAD_NORMAL && aIsTopFrame) && !aAllowKeywordFixup) {
5928 return nullptr;
5931 nsCOMPtr<nsIURI> url;
5932 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
5933 if (NS_FAILED(rv)) {
5934 return nullptr;
5938 // Try and make an alternative URI from the old one
5940 nsCOMPtr<nsIURI> newURI;
5941 nsCOMPtr<nsIInputStream> newPostData;
5943 nsAutoCString oldSpec;
5944 url->GetSpec(oldSpec);
5947 // First try keyword fixup
5949 nsAutoString keywordProviderName, keywordAsSent;
5950 if (aStatus == NS_ERROR_UNKNOWN_HOST && aAllowKeywordFixup) {
5951 // we should only perform a keyword search under the following
5952 // conditions:
5953 // (0) Pref keyword.enabled is true
5954 // (1) the url scheme is http (or https)
5955 // (2) the url does not have a protocol scheme
5956 // If we don't enforce such a policy, then we end up doing
5957 // keyword searchs on urls we don't intend like imap, file,
5958 // mailbox, etc. This could lead to a security problem where we
5959 // send data to the keyword server that we shouldn't be.
5960 // Someone needs to clean up keywords in general so we can
5961 // determine on a per url basis if we want keywords
5962 // enabled...this is just a bandaid...
5963 nsAutoCString scheme;
5964 Unused << url->GetScheme(scheme);
5965 if (Preferences::GetBool("keyword.enabled", false) &&
5966 StringBeginsWith(scheme, "http"_ns)) {
5967 bool attemptFixup = false;
5968 nsAutoCString host;
5969 Unused << url->GetHost(host);
5970 if (host.FindChar('.') == kNotFound) {
5971 attemptFixup = true;
5972 } else {
5973 // For domains with dots, we check the public suffix validity.
5974 nsCOMPtr<nsIEffectiveTLDService> tldService =
5975 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
5976 if (tldService) {
5977 nsAutoCString suffix;
5978 attemptFixup =
5979 NS_SUCCEEDED(tldService->GetKnownPublicSuffix(url, suffix)) &&
5980 suffix.IsEmpty();
5983 if (attemptFixup) {
5984 nsCOMPtr<nsIURIFixupInfo> info;
5985 // only send non-qualified hosts to the keyword server
5986 if (aOriginalURIString && !aOriginalURIString->IsEmpty()) {
5987 info = KeywordToURI(*aOriginalURIString, aUsePrivateBrowsing);
5988 } else {
5990 // If this string was passed through nsStandardURL by
5991 // chance, then it may have been converted from UTF-8 to
5992 // ACE, which would result in a completely bogus keyword
5993 // query. Here we try to recover the original Unicode
5994 // value, but this is not 100% correct since the value may
5995 // have been normalized per the IDN normalization rules.
5997 // Since we don't have access to the exact original string
5998 // that was entered by the user, this will just have to do.
6000 // XXX: Since we are not trying to use the result as an
6001 // actual domain name, label-wise Punycode decode would
6002 // likely be more appropriate than the full ToUnicode
6003 // operation.
6004 bool isACE;
6005 nsAutoCString utf8Host;
6006 nsCOMPtr<nsIIDNService> idnSrv =
6007 do_GetService(NS_IDNSERVICE_CONTRACTID);
6008 if (idnSrv && NS_SUCCEEDED(idnSrv->IsACE(host, &isACE)) && isACE &&
6009 NS_SUCCEEDED(idnSrv->ConvertACEtoUTF8(host, utf8Host))) {
6010 info = KeywordToURI(utf8Host, aUsePrivateBrowsing);
6012 } else {
6013 info = KeywordToURI(host, aUsePrivateBrowsing);
6016 if (info) {
6017 info->GetPreferredURI(getter_AddRefs(newURI));
6018 info->GetWasSchemelessInput(outWasSchemelessInput);
6019 if (newURI) {
6020 info->GetKeywordAsSent(keywordAsSent);
6021 info->GetKeywordProviderName(keywordProviderName);
6022 info->GetPostData(getter_AddRefs(newPostData));
6030 // Now try change the address, e.g. turn http://foo into
6031 // http://www.foo.com, and if that doesn't work try https with
6032 // https://foo and https://www.foo.com.
6034 if (aStatus == NS_ERROR_UNKNOWN_HOST || aStatus == NS_ERROR_NET_RESET) {
6035 // Skip fixup for anything except a normal document load
6036 // operation on the topframe.
6037 bool doCreateAlternate = aLoadType == LOAD_NORMAL && aIsTopFrame;
6039 if (doCreateAlternate) {
6040 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6041 nsIPrincipal* principal = loadInfo->TriggeringPrincipal();
6042 // Only do this if our channel was loaded directly by the user from the
6043 // URL bar or similar (system principal) and not redirected, because we
6044 // shouldn't be guessing things about links from other sites, or a
6045 // post-redirect URI.
6046 doCreateAlternate = principal && principal->IsSystemPrincipal() &&
6047 loadInfo->RedirectChain().IsEmpty();
6049 // Test if keyword lookup produced a new URI or not
6050 if (doCreateAlternate && newURI) {
6051 bool sameURI = false;
6052 url->Equals(newURI, &sameURI);
6053 if (!sameURI) {
6054 // Keyword lookup made a new URI so no need to try
6055 // an alternate one.
6056 doCreateAlternate = false;
6059 if (doCreateAlternate) {
6060 newURI = nullptr;
6061 newPostData = nullptr;
6062 keywordProviderName.Truncate();
6063 keywordAsSent.Truncate();
6064 nsCOMPtr<nsIURIFixup> uriFixup = components::URIFixup::Service();
6065 if (uriFixup) {
6066 nsCOMPtr<nsIURIFixupInfo> fixupInfo;
6067 uriFixup->GetFixupURIInfo(oldSpec, nsIURIFixup::FIXUP_FLAG_NONE,
6068 getter_AddRefs(fixupInfo));
6069 if (fixupInfo) {
6070 fixupInfo->GetPreferredURI(getter_AddRefs(newURI));
6074 } else if (aStatus == NS_ERROR_CONNECTION_REFUSED &&
6075 Preferences::GetBool("browser.fixup.fallback-to-https", false)) {
6076 // Try HTTPS, since http didn't work
6077 if (SchemeIsHTTP(url)) {
6078 int32_t port = 0;
6079 url->GetPort(&port);
6081 // Fall back to HTTPS only if port is default
6082 if (port == -1) {
6083 newURI = nullptr;
6084 newPostData = nullptr;
6085 Unused << NS_MutateURI(url)
6086 .SetScheme("https"_ns)
6087 .Finalize(getter_AddRefs(newURI));
6092 // If we have a SSL_ERROR_BAD_CERT_DOMAIN error, try adding or removing
6093 // "www." to/from the beginning of the domain name to see if we can avoid
6094 // showing the cert error page. For example, https://example.com ->
6095 // https://www.example.com or https://www.example.com -> https://example.com.
6096 if (aStatus ==
6097 mozilla::psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERT_DOMAIN)) {
6098 newPostData = nullptr;
6099 newURI = MaybeFixBadCertDomainErrorURI(aChannel, url);
6102 // Did we make a new URI that is different to the old one? If so
6103 // load it.
6105 if (newURI) {
6106 // Make sure the new URI is different from the old one,
6107 // otherwise there's little point trying to load it again.
6108 bool sameURI = false;
6109 url->Equals(newURI, &sameURI);
6110 if (!sameURI) {
6111 if (aNewPostData) {
6112 newPostData.forget(aNewPostData);
6114 if (aNotifyKeywordSearchLoading) {
6115 // This notification is meant for Firefox Health Report so it
6116 // can increment counts from the search engine
6117 MaybeNotifyKeywordSearchLoading(keywordProviderName, keywordAsSent);
6119 return newURI.forget();
6123 return nullptr;
6126 nsresult nsDocShell::FilterStatusForErrorPage(
6127 nsresult aStatus, nsIChannel* aChannel, uint32_t aLoadType,
6128 bool aIsTopFrame, bool aUseErrorPages, bool aIsInitialDocument,
6129 bool* aSkippedUnknownProtocolNavigation) {
6130 // Errors to be shown only on top-level frames
6131 if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
6132 aStatus == NS_ERROR_CONNECTION_REFUSED ||
6133 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
6134 aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
6135 aStatus == NS_ERROR_PROXY_FORBIDDEN ||
6136 aStatus == NS_ERROR_PROXY_NOT_IMPLEMENTED ||
6137 aStatus == NS_ERROR_PROXY_AUTHENTICATION_FAILED ||
6138 aStatus == NS_ERROR_PROXY_TOO_MANY_REQUESTS ||
6139 aStatus == NS_ERROR_MALFORMED_URI ||
6140 aStatus == NS_ERROR_BLOCKED_BY_POLICY ||
6141 aStatus == NS_ERROR_DOM_COOP_FAILED ||
6142 aStatus == NS_ERROR_DOM_COEP_FAILED) &&
6143 (aIsTopFrame || aUseErrorPages)) {
6144 return aStatus;
6147 if (aStatus == NS_ERROR_NET_TIMEOUT ||
6148 aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL ||
6149 aStatus == NS_ERROR_PROXY_GATEWAY_TIMEOUT ||
6150 aStatus == NS_ERROR_REDIRECT_LOOP ||
6151 aStatus == NS_ERROR_UNKNOWN_SOCKET_TYPE ||
6152 aStatus == NS_ERROR_NET_INTERRUPT || aStatus == NS_ERROR_NET_RESET ||
6153 aStatus == NS_ERROR_PROXY_BAD_GATEWAY || aStatus == NS_ERROR_OFFLINE ||
6154 aStatus == NS_ERROR_MALWARE_URI || aStatus == NS_ERROR_PHISHING_URI ||
6155 aStatus == NS_ERROR_UNWANTED_URI || aStatus == NS_ERROR_HARMFUL_URI ||
6156 aStatus == NS_ERROR_UNSAFE_CONTENT_TYPE ||
6157 aStatus == NS_ERROR_INTERCEPTION_FAILED ||
6158 aStatus == NS_ERROR_NET_INADEQUATE_SECURITY ||
6159 aStatus == NS_ERROR_NET_HTTP2_SENT_GOAWAY ||
6160 aStatus == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR ||
6161 aStatus == NS_ERROR_DOM_BAD_URI || aStatus == NS_ERROR_FILE_NOT_FOUND ||
6162 aStatus == NS_ERROR_FILE_ACCESS_DENIED ||
6163 aStatus == NS_ERROR_CORRUPTED_CONTENT ||
6164 aStatus == NS_ERROR_INVALID_CONTENT_ENCODING ||
6165 NS_ERROR_GET_MODULE(aStatus) == NS_ERROR_MODULE_SECURITY) {
6166 // Errors to be shown for any frame
6167 return aStatus;
6170 if (aStatus == NS_ERROR_UNKNOWN_PROTOCOL) {
6171 // For unknown protocols we only display an error if the load is triggered
6172 // by the browser itself, or we're replacing the initial document (and
6173 // nothing else). Showing the error for page-triggered navigations causes
6174 // annoying behavior for users, see bug 1528305.
6176 // We could, maybe, try to detect if this is in response to some user
6177 // interaction (like clicking a link, or something else) and maybe show
6178 // the error page in that case. But this allows for ctrl+clicking and such
6179 // to see the error page.
6180 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
6181 if (!info->TriggeringPrincipal()->IsSystemPrincipal() &&
6182 !aIsInitialDocument) {
6183 if (aSkippedUnknownProtocolNavigation) {
6184 *aSkippedUnknownProtocolNavigation = true;
6186 return NS_OK;
6188 return aStatus;
6191 if (aStatus == NS_ERROR_DOCUMENT_NOT_CACHED) {
6192 // Non-caching channels will simply return NS_ERROR_OFFLINE.
6193 // Caching channels would have to look at their flags to work
6194 // out which error to return. Or we can fix up the error here.
6195 if (!(aLoadType & LOAD_CMD_HISTORY)) {
6196 return NS_ERROR_OFFLINE;
6198 return aStatus;
6201 return NS_OK;
6204 nsresult nsDocShell::EndPageLoad(nsIWebProgress* aProgress,
6205 nsIChannel* aChannel, nsresult aStatus) {
6206 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
6207 ("DOCSHELL %p EndPageLoad status: %" PRIx32 "\n", this,
6208 static_cast<uint32_t>(aStatus)));
6209 if (!aChannel) {
6210 return NS_ERROR_NULL_POINTER;
6213 // Make sure to discard the initial client if we never created the initial
6214 // about:blank document. Do this before possibly returning from the method
6215 // due to an error.
6216 mInitialClientSource.reset();
6218 nsCOMPtr<nsIConsoleReportCollector> reporter = do_QueryInterface(aChannel);
6219 if (reporter) {
6220 nsCOMPtr<nsILoadGroup> loadGroup;
6221 aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
6222 if (loadGroup) {
6223 reporter->FlushConsoleReports(loadGroup);
6224 } else {
6225 reporter->FlushConsoleReports(GetDocument());
6229 nsCOMPtr<nsIURI> url;
6230 nsresult rv = aChannel->GetURI(getter_AddRefs(url));
6231 if (NS_FAILED(rv)) {
6232 return rv;
6235 nsCOMPtr<nsITimedChannel> timingChannel = do_QueryInterface(aChannel);
6236 if (timingChannel) {
6237 TimeStamp channelCreationTime;
6238 rv = timingChannel->GetChannelCreation(&channelCreationTime);
6239 if (NS_SUCCEEDED(rv) && !channelCreationTime.IsNull()) {
6240 glean::performance_page::total_content_page_load.AccumulateRawDuration(
6241 TimeStamp::Now() - channelCreationTime);
6245 // Timing is picked up by the window, we don't need it anymore
6246 mTiming = nullptr;
6248 // clean up reload state for meta charset
6249 if (eCharsetReloadRequested == mCharsetReloadState) {
6250 mCharsetReloadState = eCharsetReloadStopOrigional;
6251 } else {
6252 mCharsetReloadState = eCharsetReloadInit;
6255 // Save a pointer to the currently-loading history entry.
6256 // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
6257 // entry further down in this method.
6258 nsCOMPtr<nsISHEntry> loadingSHE = mLSHE;
6259 mozilla::Unused << loadingSHE; // XXX: Not sure if we need this anymore
6262 // one of many safeguards that prevent death and destruction if
6263 // someone is so very very rude as to bring this window down
6264 // during this load handler.
6266 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6268 // Notify the DocumentViewer that the Document has finished loading. This
6269 // will cause any OnLoad(...) and PopState(...) handlers to fire.
6270 if (!mEODForCurrentDocument && mDocumentViewer) {
6271 mIsExecutingOnLoadHandler = true;
6272 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
6273 viewer->LoadComplete(aStatus);
6274 mIsExecutingOnLoadHandler = false;
6276 mEODForCurrentDocument = true;
6278 /* Check if the httpChannel has any cache-control related response headers,
6279 * like no-store, no-cache. If so, update SHEntry so that
6280 * when a user goes back/forward to this page, we appropriately do
6281 * form value restoration or load from server.
6283 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
6284 if (!httpChannel) {
6285 // HttpChannel could be hiding underneath a Multipart channel.
6286 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
6289 if (httpChannel) {
6290 // figure out if SH should be saving layout state.
6291 bool discardLayoutState = ShouldDiscardLayoutState(httpChannel);
6292 if (mLSHE && discardLayoutState && (mLoadType & LOAD_CMD_NORMAL) &&
6293 (mLoadType != LOAD_BYPASS_HISTORY) && (mLoadType != LOAD_ERROR_PAGE)) {
6294 mLSHE->SetSaveLayoutStateFlag(false);
6298 // Clear mLSHE after calling the onLoadHandlers. This way, if the
6299 // onLoadHandler tries to load something different in
6300 // itself or one of its children, we can deal with it appropriately.
6301 if (mLSHE) {
6302 mLSHE->SetLoadType(LOAD_HISTORY);
6304 // Clear the mLSHE reference to indicate document loading is done one
6305 // way or another.
6306 SetHistoryEntryAndUpdateBC(Some(nullptr), Nothing());
6308 mActiveEntryIsLoadingFromSessionHistory = false;
6310 // if there's a refresh header in the channel, this method
6311 // will set it up for us.
6312 if (mBrowsingContext->IsActive() || !mDisableMetaRefreshWhenInactive)
6313 RefreshURIFromQueue();
6315 // Test whether this is the top frame or a subframe
6316 bool isTopFrame = mBrowsingContext->IsTop();
6318 bool hadErrorStatus = false;
6319 // If status code indicates an error it means that DocumentChannel already
6320 // tried to fixup the uri and failed. Throw an error dialog box here.
6321 if (NS_FAILED(aStatus)) {
6322 // If we got CONTENT_BLOCKED from EndPageLoad, then we need to fire
6323 // the error event to our embedder, since tests are relying on this.
6324 // The error event is usually fired by the caller of InternalLoad, but
6325 // this particular error can happen asynchronously.
6326 // Bug 1629201 is filed for having much clearer decision making around
6327 // which cases need error events.
6328 bool fireFrameErrorEvent = (aStatus == NS_ERROR_CONTENT_BLOCKED_SHOW_ALT ||
6329 aStatus == NS_ERROR_CONTENT_BLOCKED);
6330 UnblockEmbedderLoadEventForFailure(fireFrameErrorEvent);
6332 bool isInitialDocument =
6333 !GetExtantDocument() || GetExtantDocument()->IsInitialDocument();
6334 bool skippedUnknownProtocolNavigation = false;
6335 aStatus = FilterStatusForErrorPage(aStatus, aChannel, mLoadType, isTopFrame,
6336 mBrowsingContext->GetUseErrorPages(),
6337 isInitialDocument,
6338 &skippedUnknownProtocolNavigation);
6339 hadErrorStatus = true;
6340 if (NS_FAILED(aStatus)) {
6341 if (!mIsBeingDestroyed) {
6342 DisplayLoadError(aStatus, url, nullptr, aChannel);
6344 } else if (skippedUnknownProtocolNavigation) {
6345 nsTArray<nsString> params;
6346 if (NS_FAILED(
6347 NS_GetSanitizedURIStringFromURI(url, *params.AppendElement()))) {
6348 params.LastElement().AssignLiteral(u"(unknown uri)");
6350 nsContentUtils::ReportToConsole(
6351 nsIScriptError::warningFlag, "DOM"_ns, GetExtantDocument(),
6352 nsContentUtils::eDOM_PROPERTIES, "UnknownProtocolNavigationPrevented",
6353 params);
6355 } else {
6356 // If we have a host
6357 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
6358 PredictorLearnRedirect(url, aChannel, loadInfo->GetOriginAttributes());
6361 if (hadErrorStatus) {
6362 // Don't send session store updates if the reason EndPageLoad was called is
6363 // because we are process switching. Sometimes the update takes too long and
6364 // incorrectly overrides session store data from the following load.
6365 return NS_OK;
6367 if (SessionStorePlatformCollection()) {
6368 if (WindowContext* windowContext =
6369 mBrowsingContext->GetCurrentWindowContext()) {
6370 using Change = SessionStoreChangeListener::Change;
6372 // We've finished loading the page and now we want to collect all the
6373 // session store state that the page is initialized with.
6374 SessionStoreChangeListener::CollectSessionStoreData(
6375 windowContext,
6376 EnumSet<Change>(Change::Input, Change::Scroll, Change::SessionHistory,
6377 Change::WireFrame));
6381 return NS_OK;
6384 //*****************************************************************************
6385 // nsDocShell: Content Viewer Management
6386 //*****************************************************************************
6388 nsresult nsDocShell::EnsureDocumentViewer() {
6389 if (mDocumentViewer) {
6390 return NS_OK;
6392 if (mIsBeingDestroyed) {
6393 return NS_ERROR_FAILURE;
6396 nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
6397 nsCOMPtr<nsIURI> baseURI;
6398 nsIPrincipal* principal = GetInheritedPrincipal(false);
6399 nsIPrincipal* partitionedPrincipal = GetInheritedPrincipal(false, true);
6401 nsCOMPtr<nsIDocShellTreeItem> parentItem;
6402 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
6403 if (parentItem) {
6404 if (nsCOMPtr<nsPIDOMWindowOuter> domWin = GetWindow()) {
6405 nsCOMPtr<Element> parentElement = domWin->GetFrameElementInternal();
6406 if (parentElement) {
6407 baseURI = parentElement->GetBaseURI();
6408 cspToInheritForAboutBlank = parentElement->GetCsp();
6413 nsresult rv = CreateAboutBlankDocumentViewer(
6414 principal, partitionedPrincipal, cspToInheritForAboutBlank, baseURI,
6415 /* aIsInitialDocument */ true);
6417 NS_ENSURE_STATE(mDocumentViewer);
6419 if (NS_SUCCEEDED(rv)) {
6420 RefPtr<Document> doc(GetDocument());
6421 MOZ_ASSERT(doc,
6422 "Should have doc if CreateAboutBlankDocumentViewer "
6423 "succeeded!");
6424 MOZ_ASSERT(doc->IsInitialDocument(), "Document should be initial document");
6426 // Documents created using EnsureDocumentViewer may be transient
6427 // placeholders created by framescripts before content has a
6428 // chance to load. In some cases, window.open(..., "noopener")
6429 // will create such a document and then synchronously tear it
6430 // down, firing a "pagehide" event. Doing so violates our
6431 // assertions about DocGroups. It's easier to silence the
6432 // assertion here than to avoid creating the extra document.
6433 doc->IgnoreDocGroupMismatches();
6436 return rv;
6439 nsresult nsDocShell::CreateAboutBlankDocumentViewer(
6440 nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal,
6441 nsIContentSecurityPolicy* aCSP, nsIURI* aBaseURI, bool aIsInitialDocument,
6442 const Maybe<nsILoadInfo::CrossOriginEmbedderPolicy>& aCOEP,
6443 bool aTryToSaveOldPresentation, bool aCheckPermitUnload,
6444 WindowGlobalChild* aActor) {
6445 RefPtr<Document> blankDoc;
6446 nsCOMPtr<nsIDocumentViewer> viewer;
6447 nsresult rv = NS_ERROR_FAILURE;
6449 PROFILER_MARKER_UNTYPED("CreateAboutBlankDocumentViewer", DOM,
6450 MarkerStack::Capture());
6452 MOZ_ASSERT_IF(aActor, aActor->DocumentPrincipal() == aPrincipal);
6454 /* mCreatingDocument should never be true at this point. However, it's
6455 a theoretical possibility. We want to know about it and make it stop,
6456 and this sounds like a job for an assertion. */
6457 NS_ASSERTION(!mCreatingDocument,
6458 "infinite(?) loop creating document averted");
6459 if (mCreatingDocument) {
6460 return NS_ERROR_FAILURE;
6463 if (!mBrowsingContext->AncestorsAreCurrent() ||
6464 mBrowsingContext->IsInBFCache()) {
6465 mBrowsingContext->RemoveRootFromBFCacheSync();
6466 return NS_ERROR_NOT_AVAILABLE;
6469 // mDocumentViewer->PermitUnload may release |this| docshell.
6470 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
6472 AutoRestore<bool> creatingDocument(mCreatingDocument);
6473 mCreatingDocument = true;
6475 if (aPrincipal && !aPrincipal->IsSystemPrincipal() &&
6476 mItemType != typeChrome) {
6477 MOZ_ASSERT(aPrincipal->OriginAttributesRef() ==
6478 mBrowsingContext->OriginAttributesRef());
6481 // Make sure timing is created. But first record whether we had it
6482 // already, so we don't clobber the timing for an in-progress load.
6483 bool hadTiming = mTiming;
6484 bool toBeReset = MaybeInitTiming();
6485 if (mDocumentViewer) {
6486 if (aCheckPermitUnload) {
6487 // We've got a content viewer already. Make sure the user
6488 // permits us to discard the current document and replace it
6489 // with about:blank. And also ensure we fire the unload events
6490 // in the current document.
6492 // Unload gets fired first for
6493 // document loaded from the session history.
6494 mTiming->NotifyBeforeUnload();
6496 bool okToUnload;
6497 rv = mDocumentViewer->PermitUnload(&okToUnload);
6499 if (NS_SUCCEEDED(rv) && !okToUnload) {
6500 // The user chose not to unload the page, interrupt the load.
6501 MaybeResetInitTiming(toBeReset);
6502 return NS_ERROR_FAILURE;
6504 if (mTiming) {
6505 mTiming->NotifyUnloadAccepted(mCurrentURI);
6509 mSavingOldViewer =
6510 aTryToSaveOldPresentation &&
6511 CanSavePresentation(LOAD_NORMAL, nullptr, nullptr,
6512 /* aReportBFCacheComboTelemetry */ true);
6514 // Make sure to blow away our mLoadingURI just in case. No loads
6515 // from inside this pagehide.
6516 mLoadingURI = nullptr;
6518 // Stop any in-progress loading, so that we don't accidentally trigger any
6519 // PageShow notifications from Embed() interrupting our loading below.
6520 Stop();
6522 // Notify the current document that it is about to be unloaded!!
6524 // It is important to fire the unload() notification *before* any state
6525 // is changed within the DocShell - otherwise, javascript will get the
6526 // wrong information :-(
6528 (void)FirePageHideNotification(!mSavingOldViewer);
6529 // pagehide notification might destroy this docshell.
6530 if (mIsBeingDestroyed) {
6531 return NS_ERROR_DOCSHELL_DYING;
6535 // Now make sure we don't think we're in the middle of firing unload after
6536 // this point. This will make us fire unload when the about:blank document
6537 // unloads... but that's ok, more or less. Would be nice if it fired load
6538 // too, of course.
6539 mFiredUnloadEvent = false;
6541 nsCOMPtr<nsIDocumentLoaderFactory> docFactory =
6542 nsContentUtils::FindInternalDocumentViewer("text/html"_ns);
6544 if (docFactory) {
6545 nsCOMPtr<nsIPrincipal> principal, partitionedPrincipal;
6546 const uint32_t sandboxFlags =
6547 mBrowsingContext->GetHasLoadedNonInitialDocument()
6548 ? mBrowsingContext->GetSandboxFlags()
6549 : mBrowsingContext->GetInitialSandboxFlags();
6550 // If we're sandboxed, then create a new null principal. We skip
6551 // this if we're being created from WindowGlobalChild, since in
6552 // that case we already have a null principal if required.
6553 // We can't compare againt the BrowsingContext sandbox flag, since
6554 // the value was taken when the load initiated and may have since
6555 // changed.
6556 if ((sandboxFlags & SANDBOXED_ORIGIN) && !aActor) {
6557 if (aPrincipal) {
6558 principal = NullPrincipal::CreateWithInheritedAttributes(aPrincipal);
6559 } else {
6560 principal = NullPrincipal::Create(GetOriginAttributes());
6562 partitionedPrincipal = principal;
6563 } else {
6564 principal = aPrincipal;
6565 partitionedPrincipal = aPartitionedPrincipal;
6568 // We cannot get the foreign partitioned prinicpal for the initial
6569 // about:blank page. So, we change to check if we need to use the
6570 // partitioned principal for the service worker here.
6571 MaybeCreateInitialClientSource(
6572 StoragePrincipalHelper::ShouldUsePartitionPrincipalForServiceWorker(
6573 this)
6574 ? partitionedPrincipal
6575 : principal);
6577 // generate (about:blank) document to load
6578 blankDoc = nsContentDLF::CreateBlankDocument(mLoadGroup, principal,
6579 partitionedPrincipal, this);
6580 if (blankDoc) {
6581 // Hack: manually set the CSP for the new document
6582 // Please create an actual copy of the CSP (do not share the same
6583 // reference) otherwise appending a new policy within the new
6584 // document will be incorrectly propagated to the opening doc.
6585 if (aCSP) {
6586 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
6587 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(aCSP));
6588 blankDoc->SetCsp(cspToInherit);
6591 blankDoc->SetIsInitialDocument(aIsInitialDocument);
6593 blankDoc->SetEmbedderPolicy(aCOEP);
6595 // Hack: set the base URI manually, since this document never
6596 // got Reset() with a channel.
6597 blankDoc->SetBaseURI(aBaseURI);
6599 // Copy our sandbox flags to the document. These are immutable
6600 // after being set here.
6601 blankDoc->SetSandboxFlags(sandboxFlags);
6603 // create a content viewer for us and the new document
6604 docFactory->CreateInstanceForDocument(
6605 NS_ISUPPORTS_CAST(nsIDocShell*, this), blankDoc, "view",
6606 getter_AddRefs(viewer));
6608 // hook 'em up
6609 if (viewer) {
6610 viewer->SetContainer(this);
6611 rv = Embed(viewer, aActor, true, false, nullptr, mCurrentURI);
6612 NS_ENSURE_SUCCESS(rv, rv);
6614 SetCurrentURI(blankDoc->GetDocumentURI(), nullptr,
6615 /* aFireLocationChange */ true,
6616 /* aIsInitialAboutBlank */ true,
6617 /* aLocationFlags */ 0);
6618 rv = mIsBeingDestroyed ? NS_ERROR_NOT_AVAILABLE : NS_OK;
6621 if (Element* embedderElement = blankDoc->GetEmbedderElement()) {
6622 blankDoc->InitFeaturePolicy(AsVariant(embedderElement));
6623 } else {
6624 blankDoc->InitFeaturePolicy(AsVariant(Nothing{}));
6629 // The transient about:blank viewer doesn't have a session history entry.
6630 SetHistoryEntryAndUpdateBC(Nothing(), Some(nullptr));
6632 // Clear out our mTiming like we would in EndPageLoad, if we didn't
6633 // have one before entering this function.
6634 if (!hadTiming) {
6635 mTiming = nullptr;
6636 mBlankTiming = true;
6639 return rv;
6642 NS_IMETHODIMP
6643 nsDocShell::CreateAboutBlankDocumentViewer(nsIPrincipal* aPrincipal,
6644 nsIPrincipal* aPartitionedPrincipal,
6645 nsIContentSecurityPolicy* aCSP) {
6646 return CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal, aCSP,
6647 nullptr,
6648 /* aIsInitialDocument */ false);
6651 nsresult nsDocShell::CreateDocumentViewerForActor(
6652 WindowGlobalChild* aWindowActor) {
6653 MOZ_ASSERT(aWindowActor);
6655 // FIXME: WindowGlobalChild should provide the PartitionedPrincipal.
6656 // FIXME: We may want to support non-initial documents here.
6657 nsresult rv = CreateAboutBlankDocumentViewer(
6658 aWindowActor->DocumentPrincipal(), aWindowActor->DocumentPrincipal(),
6659 /* aCsp */ nullptr,
6660 /* aBaseURI */ nullptr,
6661 /* aIsInitialDocument */ true,
6662 /* aCOEP */ Nothing(),
6663 /* aTryToSaveOldPresentation */ true,
6664 /* aCheckPermitUnload */ true, aWindowActor);
6665 #ifdef DEBUG
6666 if (NS_SUCCEEDED(rv)) {
6667 RefPtr<Document> doc(GetDocument());
6668 MOZ_ASSERT(
6669 doc,
6670 "Should have a document if CreateAboutBlankDocumentViewer succeeded");
6671 MOZ_ASSERT(doc->GetOwnerGlobal() == aWindowActor->GetWindowGlobal(),
6672 "New document should be in the same global as our actor");
6673 MOZ_ASSERT(doc->IsInitialDocument(),
6674 "New document should be an initial document");
6676 #endif
6678 return rv;
6681 bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
6682 nsIRequest* aNewRequest,
6683 Document* aNewDocument,
6684 bool aReportBFCacheComboTelemetry) {
6685 if (!mOSHE) {
6686 return false; // no entry to save into
6689 MOZ_ASSERT(!mozilla::SessionHistoryInParent(),
6690 "mOSHE cannot be non-null with SHIP");
6691 nsCOMPtr<nsIDocumentViewer> viewer = mOSHE->GetDocumentViewer();
6692 if (viewer) {
6693 NS_WARNING("mOSHE already has a content viewer!");
6694 return false;
6697 // Only save presentation for "normal" loads and link loads. Anything else
6698 // probably wants to refetch the page, so caching the old presentation
6699 // would be incorrect.
6700 if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
6701 aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
6702 aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&
6703 aLoadType != LOAD_ERROR_PAGE) {
6704 return false;
6707 // If the session history entry has the saveLayoutState flag set to false,
6708 // then we should not cache the presentation.
6709 if (!mOSHE->GetSaveLayoutStateFlag()) {
6710 return false;
6713 // If the document is not done loading, don't cache it.
6714 if (!mScriptGlobal || mScriptGlobal->IsLoading()) {
6715 MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
6716 ("Blocked due to document still loading"));
6717 return false;
6720 if (mScriptGlobal->WouldReuseInnerWindow(aNewDocument)) {
6721 return false;
6724 // Avoid doing the work of saving the presentation state in the case where
6725 // the content viewer cache is disabled.
6726 if (nsSHistory::GetMaxTotalViewers() == 0) {
6727 return false;
6730 // Don't cache the content viewer if we're in a subframe.
6731 if (mBrowsingContext->GetParent()) {
6732 return false; // this is a subframe load
6735 // If the document does not want its presentation cached, then don't.
6736 RefPtr<Document> doc = mScriptGlobal->GetExtantDoc();
6738 uint32_t bfCacheCombo = 0;
6739 bool canSavePresentation =
6740 doc->CanSavePresentation(aNewRequest, bfCacheCombo, true);
6741 MOZ_ASSERT_IF(canSavePresentation, bfCacheCombo == 0);
6742 if (canSavePresentation && doc->IsTopLevelContentDocument()) {
6743 auto* browsingContextGroup = mBrowsingContext->Group();
6744 nsTArray<RefPtr<BrowsingContext>>& topLevelContext =
6745 browsingContextGroup->Toplevels();
6747 for (const auto& browsingContext : topLevelContext) {
6748 if (browsingContext != mBrowsingContext) {
6749 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6750 canSavePresentation = false;
6752 bfCacheCombo |= BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG;
6753 break;
6758 if (aReportBFCacheComboTelemetry) {
6759 ReportBFCacheComboTelemetry(bfCacheCombo);
6761 return doc && canSavePresentation;
6764 /* static */
6765 void nsDocShell::ReportBFCacheComboTelemetry(uint32_t aCombo) {
6766 // There are 11 possible reasons to make a request fails to use BFCache
6767 // (see BFCacheStatus in dom/base/Document.h), and we'd like to record
6768 // the common combinations for reasons which make requests fail to use
6769 // BFCache. These combinations are generated based on some local browsings,
6770 // we need to adjust them when necessary.
6771 enum BFCacheStatusCombo : uint32_t {
6772 BFCACHE_SUCCESS,
6773 NOT_ONLY_TOPLEVEL = mozilla::dom::BFCacheStatus::NOT_ONLY_TOPLEVEL_IN_BCG,
6774 // If both unload and beforeunload listeners are presented, it'll be
6775 // recorded as unload
6776 UNLOAD = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER,
6777 UNLOAD_REQUEST = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6778 mozilla::dom::BFCacheStatus::REQUEST,
6779 REQUEST = mozilla::dom::BFCacheStatus::REQUEST,
6780 UNLOAD_REQUEST_PEER = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6781 mozilla::dom::BFCacheStatus::REQUEST |
6782 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6783 UNLOAD_REQUEST_PEER_MSE =
6784 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6785 mozilla::dom::BFCacheStatus::REQUEST |
6786 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION |
6787 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6788 UNLOAD_REQUEST_MSE = mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6789 mozilla::dom::BFCacheStatus::REQUEST |
6790 mozilla::dom::BFCacheStatus::CONTAINS_MSE_CONTENT,
6791 SUSPENDED_UNLOAD_REQUEST_PEER =
6792 mozilla::dom::BFCacheStatus::SUSPENDED |
6793 mozilla::dom::BFCacheStatus::UNLOAD_LISTENER |
6794 mozilla::dom::BFCacheStatus::REQUEST |
6795 mozilla::dom::BFCacheStatus::ACTIVE_PEER_CONNECTION,
6796 REMOTE_SUBFRAMES = mozilla::dom::BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES,
6797 BEFOREUNLOAD = mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER,
6800 // Beforeunload is recorded as a blocker only if it is the only one to block
6801 // bfcache.
6802 if (aCombo != mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER) {
6803 aCombo &= ~mozilla::dom::BFCacheStatus::BEFOREUNLOAD_LISTENER;
6805 switch (aCombo) {
6806 case BFCACHE_SUCCESS:
6807 Telemetry::AccumulateCategorical(
6808 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6809 break;
6810 case NOT_ONLY_TOPLEVEL:
6811 if (StaticPrefs::docshell_shistory_bfcache_require_no_opener()) {
6812 Telemetry::AccumulateCategorical(
6813 Telemetry::LABELS_BFCACHE_COMBO::Other);
6814 break;
6816 Telemetry::AccumulateCategorical(
6817 Telemetry::LABELS_BFCACHE_COMBO::BFCache_Success);
6818 Telemetry::AccumulateCategorical(
6819 Telemetry::LABELS_BFCACHE_COMBO::Success_Not_Toplevel);
6820 break;
6821 case UNLOAD:
6822 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Unload);
6823 break;
6824 case BEFOREUNLOAD:
6825 Telemetry::AccumulateCategorical(
6826 Telemetry::LABELS_BFCACHE_COMBO::Beforeunload);
6827 break;
6828 case UNLOAD_REQUEST:
6829 Telemetry::AccumulateCategorical(
6830 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req);
6831 break;
6832 case REQUEST:
6833 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Req);
6834 break;
6835 case UNLOAD_REQUEST_PEER:
6836 Telemetry::AccumulateCategorical(
6837 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer);
6838 break;
6839 case UNLOAD_REQUEST_PEER_MSE:
6840 Telemetry::AccumulateCategorical(
6841 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_Peer_MSE);
6842 break;
6843 case UNLOAD_REQUEST_MSE:
6844 Telemetry::AccumulateCategorical(
6845 Telemetry::LABELS_BFCACHE_COMBO::Unload_Req_MSE);
6846 break;
6847 case SUSPENDED_UNLOAD_REQUEST_PEER:
6848 Telemetry::AccumulateCategorical(
6849 Telemetry::LABELS_BFCACHE_COMBO::SPD_Unload_Req_Peer);
6850 break;
6851 case REMOTE_SUBFRAMES:
6852 Telemetry::AccumulateCategorical(
6853 Telemetry::LABELS_BFCACHE_COMBO::Remote_Subframes);
6854 break;
6855 default:
6856 Telemetry::AccumulateCategorical(Telemetry::LABELS_BFCACHE_COMBO::Other);
6857 break;
6861 void nsDocShell::ReattachEditorToWindow(nsISHEntry* aSHEntry) {
6862 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6863 MOZ_ASSERT(!mIsBeingDestroyed);
6865 NS_ASSERTION(!mEditorData,
6866 "Why reattach an editor when we already have one?");
6867 NS_ASSERTION(aSHEntry && aSHEntry->HasDetachedEditor(),
6868 "Reattaching when there's not a detached editor.");
6870 if (mEditorData || !aSHEntry) {
6871 return;
6874 mEditorData = WrapUnique(aSHEntry->ForgetEditorData());
6875 if (mEditorData) {
6876 #ifdef DEBUG
6877 nsresult rv =
6878 #endif
6879 mEditorData->ReattachToWindow(this);
6880 NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to reattach editing session");
6884 void nsDocShell::DetachEditorFromWindow() {
6885 if (!mEditorData || mEditorData->WaitingForLoad()) {
6886 // If there's nothing to detach, or if the editor data is actually set
6887 // up for the _new_ page that's coming in, don't detach.
6888 return;
6891 NS_ASSERTION(!mOSHE || !mOSHE->HasDetachedEditor(),
6892 "Detaching editor when it's already detached.");
6894 nsresult res = mEditorData->DetachFromWindow();
6895 NS_ASSERTION(NS_SUCCEEDED(res), "Failed to detach editor");
6897 if (NS_SUCCEEDED(res)) {
6898 // Make mOSHE hold the owning ref to the editor data.
6899 if (mOSHE) {
6900 MOZ_ASSERT(!mIsBeingDestroyed || !mOSHE->HasDetachedEditor(),
6901 "We should not set the editor data again once after we "
6902 "detached the editor data during destroying this docshell");
6903 mOSHE->SetEditorData(mEditorData.release());
6904 } else {
6905 mEditorData = nullptr;
6909 #ifdef DEBUG
6911 bool isEditable;
6912 GetEditable(&isEditable);
6913 NS_ASSERTION(!isEditable,
6914 "Window is still editable after detaching editor.");
6916 #endif // DEBUG
6919 nsresult nsDocShell::CaptureState() {
6920 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6922 if (!mOSHE || mOSHE == mLSHE) {
6923 // No entry to save into, or we're replacing the existing entry.
6924 return NS_ERROR_FAILURE;
6927 if (!mScriptGlobal) {
6928 return NS_ERROR_FAILURE;
6931 nsCOMPtr<nsISupports> windowState = mScriptGlobal->SaveWindowState();
6932 NS_ENSURE_TRUE(windowState, NS_ERROR_FAILURE);
6934 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
6935 nsAutoCString spec;
6936 nsCOMPtr<nsIURI> uri = mOSHE->GetURI();
6937 if (uri) {
6938 uri->GetSpec(spec);
6940 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
6941 ("Saving presentation into session history, URI: %s", spec.get()));
6944 mOSHE->SetWindowState(windowState);
6946 // Suspend refresh URIs and save off the timer queue
6947 mOSHE->SetRefreshURIList(mSavedRefreshURIList);
6949 // Capture the current content viewer bounds.
6950 if (mDocumentViewer) {
6951 nsIntRect bounds;
6952 mDocumentViewer->GetBounds(bounds);
6953 mOSHE->SetViewerBounds(bounds);
6956 // Capture the docshell hierarchy.
6957 mOSHE->ClearChildShells();
6959 uint32_t childCount = mChildList.Length();
6960 for (uint32_t i = 0; i < childCount; ++i) {
6961 nsCOMPtr<nsIDocShellTreeItem> childShell = do_QueryInterface(ChildAt(i));
6962 NS_ASSERTION(childShell, "null child shell");
6964 mOSHE->AddChildShell(childShell);
6967 return NS_OK;
6970 NS_IMETHODIMP
6971 nsDocShell::RestorePresentationEvent::Run() {
6972 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6974 if (mDocShell && NS_FAILED(mDocShell->RestoreFromHistory())) {
6975 NS_WARNING("RestoreFromHistory failed");
6977 return NS_OK;
6980 NS_IMETHODIMP
6981 nsDocShell::BeginRestore(nsIDocumentViewer* aDocumentViewer, bool aTop) {
6982 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
6984 nsresult rv;
6985 if (!aDocumentViewer) {
6986 rv = EnsureDocumentViewer();
6987 NS_ENSURE_SUCCESS(rv, rv);
6989 aDocumentViewer = mDocumentViewer;
6992 // Dispatch events for restoring the presentation. We try to simulate
6993 // the progress notifications loading the document would cause, so we add
6994 // the document's channel to the loadgroup to initiate stateChange
6995 // notifications.
6997 RefPtr<Document> doc = aDocumentViewer->GetDocument();
6998 if (doc) {
6999 nsIChannel* channel = doc->GetChannel();
7000 if (channel) {
7001 mEODForCurrentDocument = false;
7002 mIsRestoringDocument = true;
7003 mLoadGroup->AddRequest(channel, nullptr);
7004 mIsRestoringDocument = false;
7008 if (!aTop) {
7009 // This point corresponds to us having gotten OnStartRequest or
7010 // STATE_START, so do the same thing that CreateDocumentViewer does at
7011 // this point to ensure that unload/pagehide events for this document
7012 // will fire when it's unloaded again.
7013 mFiredUnloadEvent = false;
7015 // For non-top frames, there is no notion of making sure that the
7016 // previous document is in the domwindow when STATE_START notifications
7017 // happen. We can just call BeginRestore for all of the child shells
7018 // now.
7019 rv = BeginRestoreChildren();
7020 NS_ENSURE_SUCCESS(rv, rv);
7023 return NS_OK;
7026 nsresult nsDocShell::BeginRestoreChildren() {
7027 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7029 for (auto* childDocLoader : mChildList.ForwardRange()) {
7030 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7031 if (child) {
7032 nsresult rv = child->BeginRestore(nullptr, false);
7033 NS_ENSURE_SUCCESS(rv, rv);
7036 return NS_OK;
7039 NS_IMETHODIMP
7040 nsDocShell::FinishRestore() {
7041 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7043 // First we call finishRestore() on our children. In the simulated load,
7044 // all of the child frames finish loading before the main document.
7046 for (auto* childDocLoader : mChildList.ForwardRange()) {
7047 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7048 if (child) {
7049 child->FinishRestore();
7053 if (mOSHE && mOSHE->HasDetachedEditor()) {
7054 ReattachEditorToWindow(mOSHE);
7057 RefPtr<Document> doc = GetDocument();
7058 if (doc) {
7059 // Finally, we remove the request from the loadgroup. This will
7060 // cause onStateChange(STATE_STOP) to fire, which will fire the
7061 // pageshow event to the chrome.
7063 nsIChannel* channel = doc->GetChannel();
7064 if (channel) {
7065 mIsRestoringDocument = true;
7066 mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
7067 mIsRestoringDocument = false;
7071 return NS_OK;
7074 NS_IMETHODIMP
7075 nsDocShell::GetRestoringDocument(bool* aRestoring) {
7076 *aRestoring = mIsRestoringDocument;
7077 return NS_OK;
7080 nsresult nsDocShell::RestorePresentation(nsISHEntry* aSHEntry,
7081 bool* aRestoring) {
7082 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7083 MOZ_ASSERT(!mIsBeingDestroyed);
7085 NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY,
7086 "RestorePresentation should only be called for history loads");
7088 nsCOMPtr<nsIDocumentViewer> viewer = aSHEntry->GetDocumentViewer();
7090 nsAutoCString spec;
7091 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Debug))) {
7092 nsCOMPtr<nsIURI> uri = aSHEntry->GetURI();
7093 if (uri) {
7094 uri->GetSpec(spec);
7098 *aRestoring = false;
7100 if (!viewer) {
7101 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7102 ("no saved presentation for uri: %s", spec.get()));
7103 return NS_OK;
7106 // We need to make sure the content viewer's container is this docshell.
7107 // In subframe navigation, it's possible for the docshell that the
7108 // content viewer was originally loaded into to be replaced with a
7109 // different one. We don't currently support restoring the presentation
7110 // in that case.
7112 nsCOMPtr<nsIDocShell> container;
7113 viewer->GetContainer(getter_AddRefs(container));
7114 if (!::SameCOMIdentity(container, GetAsSupports(this))) {
7115 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7116 ("No valid container, clearing presentation"));
7117 aSHEntry->SetDocumentViewer(nullptr);
7118 return NS_ERROR_FAILURE;
7121 NS_ASSERTION(mDocumentViewer != viewer, "Restoring existing presentation");
7123 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7124 ("restoring presentation from session history: %s", spec.get()));
7126 SetHistoryEntryAndUpdateBC(Some(aSHEntry), Nothing());
7128 // Post an event that will remove the request after we've returned
7129 // to the event loop. This mimics the way it is called by nsIChannel
7130 // implementations.
7132 // Revoke any pending restore (just in case).
7133 NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
7134 "should only have one RestorePresentationEvent");
7135 mRestorePresentationEvent.Revoke();
7137 RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
7138 nsresult rv = Dispatch(do_AddRef(evt));
7139 if (NS_SUCCEEDED(rv)) {
7140 mRestorePresentationEvent = evt.get();
7141 // The rest of the restore processing will happen on our event
7142 // callback.
7143 *aRestoring = true;
7146 return rv;
7149 namespace {
7150 class MOZ_STACK_CLASS PresentationEventForgetter {
7151 public:
7152 explicit PresentationEventForgetter(
7153 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7154 aRestorePresentationEvent)
7155 : mRestorePresentationEvent(aRestorePresentationEvent),
7156 mEvent(aRestorePresentationEvent.get()) {}
7158 ~PresentationEventForgetter() { Forget(); }
7160 void Forget() {
7161 if (mRestorePresentationEvent.get() == mEvent) {
7162 mRestorePresentationEvent.Forget();
7163 mEvent = nullptr;
7167 private:
7168 nsRevocableEventPtr<nsDocShell::RestorePresentationEvent>&
7169 mRestorePresentationEvent;
7170 RefPtr<nsDocShell::RestorePresentationEvent> mEvent;
7173 } // namespace
7175 bool nsDocShell::SandboxFlagsImplyCookies(const uint32_t& aSandboxFlags) {
7176 return (aSandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0;
7179 nsresult nsDocShell::RestoreFromHistory() {
7180 MOZ_ASSERT(!mozilla::SessionHistoryInParent());
7181 MOZ_ASSERT(mRestorePresentationEvent.IsPending());
7182 PresentationEventForgetter forgetter(mRestorePresentationEvent);
7184 // This section of code follows the same ordering as CreateDocumentViewer.
7185 if (!mLSHE) {
7186 return NS_ERROR_FAILURE;
7189 nsCOMPtr<nsIDocumentViewer> viewer = mLSHE->GetDocumentViewer();
7190 if (!viewer) {
7191 return NS_ERROR_FAILURE;
7194 if (mSavingOldViewer) {
7195 // We determined that it was safe to cache the document presentation
7196 // at the time we initiated the new load. We need to check whether
7197 // it's still safe to do so, since there may have been DOM mutations
7198 // or new requests initiated.
7199 RefPtr<Document> doc = viewer->GetDocument();
7200 nsIRequest* request = nullptr;
7201 if (doc) {
7202 request = doc->GetChannel();
7204 mSavingOldViewer = CanSavePresentation(
7205 mLoadType, request, doc, /* aReportBFCacheComboTelemetry */ false);
7208 // Protect against mLSHE going away via a load triggered from
7209 // pagehide or unload.
7210 nsCOMPtr<nsISHEntry> origLSHE = mLSHE;
7212 // Make sure to blow away our mLoadingURI just in case. No loads
7213 // from inside this pagehide.
7214 mLoadingURI = nullptr;
7216 // Notify the old content viewer that it's being hidden.
7217 FirePageHideNotification(!mSavingOldViewer);
7218 // pagehide notification might destroy this docshell.
7219 if (mIsBeingDestroyed) {
7220 return NS_ERROR_DOCSHELL_DYING;
7223 // If mLSHE was changed as a result of the pagehide event, then
7224 // something else was loaded. Don't finish restoring.
7225 if (mLSHE != origLSHE) {
7226 return NS_OK;
7229 // Add the request to our load group. We do this before swapping out
7230 // the content viewers so that consumers of STATE_START can access
7231 // the old document. We only deal with the toplevel load at this time --
7232 // to be consistent with normal document loading, subframes cannot start
7233 // loading until after data arrives, which is after STATE_START completes.
7235 RefPtr<RestorePresentationEvent> currentPresentationRestoration =
7236 mRestorePresentationEvent.get();
7237 Stop();
7238 // Make sure we're still restoring the same presentation.
7239 // If we aren't, docshell is in process doing another load already.
7240 NS_ENSURE_STATE(currentPresentationRestoration ==
7241 mRestorePresentationEvent.get());
7242 BeginRestore(viewer, true);
7243 NS_ENSURE_STATE(currentPresentationRestoration ==
7244 mRestorePresentationEvent.get());
7245 forgetter.Forget();
7247 // Set mFiredUnloadEvent = false so that the unload handler for the
7248 // *new* document will fire.
7249 mFiredUnloadEvent = false;
7251 mURIResultedInDocument = true;
7252 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
7253 if (rootSH) {
7254 mPreviousEntryIndex = rootSH->Index();
7255 rootSH->LegacySHistory()->UpdateIndex();
7256 mLoadedEntryIndex = rootSH->Index();
7257 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
7258 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
7259 mLoadedEntryIndex));
7262 // Rather than call Embed(), we will retrieve the viewer from the session
7263 // history entry and swap it in.
7264 // XXX can we refactor this so that we can just call Embed()?
7265 PersistLayoutHistoryState();
7266 nsresult rv;
7267 if (mDocumentViewer) {
7268 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7269 if (mOSHE) {
7270 mOSHE->SyncPresentationState();
7272 mSavingOldViewer = false;
7276 mSavedRefreshURIList = nullptr;
7278 // In cases where we use a transient about:blank viewer between loads,
7279 // we never show the transient viewer, so _its_ previous viewer is never
7280 // unhooked from the view hierarchy. Destroy any such previous viewer now,
7281 // before we grab the root view sibling, so that we don't grab a view
7282 // that's about to go away.
7284 if (mDocumentViewer) {
7285 // Make sure to hold a strong ref to previousViewer here while we
7286 // drop the reference to it from mDocumentViewer.
7287 nsCOMPtr<nsIDocumentViewer> previousViewer =
7288 mDocumentViewer->GetPreviousViewer();
7289 if (previousViewer) {
7290 mDocumentViewer->SetPreviousViewer(nullptr);
7291 previousViewer->Destroy();
7295 // Save off the root view's parent and sibling so that we can insert the
7296 // new content viewer's root view at the same position. Also save the
7297 // bounds of the root view's widget.
7299 nsView* rootViewSibling = nullptr;
7300 nsView* rootViewParent = nullptr;
7301 nsIntRect newBounds(0, 0, 0, 0);
7303 PresShell* oldPresShell = GetPresShell();
7304 if (oldPresShell) {
7305 nsViewManager* vm = oldPresShell->GetViewManager();
7306 if (vm) {
7307 nsView* oldRootView = vm->GetRootView();
7309 if (oldRootView) {
7310 rootViewSibling = oldRootView->GetNextSibling();
7311 rootViewParent = oldRootView->GetParent();
7313 mDocumentViewer->GetBounds(newBounds);
7318 nsCOMPtr<nsIContent> container;
7319 RefPtr<Document> sibling;
7320 if (rootViewParent && rootViewParent->GetParent()) {
7321 nsIFrame* frame = rootViewParent->GetParent()->GetFrame();
7322 container = frame ? frame->GetContent() : nullptr;
7324 if (rootViewSibling) {
7325 nsIFrame* frame = rootViewSibling->GetFrame();
7326 sibling = frame ? frame->PresShell()->GetDocument() : nullptr;
7329 // Transfer ownership to mDocumentViewer. By ensuring that either the
7330 // docshell or the session history, but not both, have references to the
7331 // content viewer, we prevent the viewer from being torn down after
7332 // Destroy() is called.
7334 if (mDocumentViewer) {
7335 mDocumentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7336 viewer->SetPreviousViewer(mDocumentViewer);
7338 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
7339 // We don't plan to save a viewer in mOSHE; tell it to drop
7340 // any other state it's holding.
7341 mOSHE->SyncPresentationState();
7344 // Order the mDocumentViewer setup just like Embed does.
7345 mDocumentViewer = nullptr;
7347 // Now that we're about to switch documents, forget all of our children.
7348 // Note that we cached them as needed up in CaptureState above.
7349 DestroyChildren();
7351 mDocumentViewer.swap(viewer);
7353 // Grab all of the related presentation from the SHEntry now.
7354 // Clearing the viewer from the SHEntry will clear all of this state.
7355 nsCOMPtr<nsISupports> windowState = mLSHE->GetWindowState();
7356 mLSHE->SetWindowState(nullptr);
7358 bool sticky = mLSHE->GetSticky();
7360 RefPtr<Document> document = mDocumentViewer->GetDocument();
7362 nsCOMArray<nsIDocShellTreeItem> childShells;
7363 int32_t i = 0;
7364 nsCOMPtr<nsIDocShellTreeItem> child;
7365 while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) &&
7366 child) {
7367 childShells.AppendObject(child);
7370 // get the previous content viewer size
7371 nsIntRect oldBounds(0, 0, 0, 0);
7372 mLSHE->GetViewerBounds(oldBounds);
7374 // Restore the refresh URI list. The refresh timers will be restarted
7375 // when EndPageLoad() is called.
7376 nsCOMPtr<nsIMutableArray> refreshURIList = mLSHE->GetRefreshURIList();
7378 // Reattach to the window object.
7379 mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
7380 rv = mDocumentViewer->Open(windowState, mLSHE);
7381 mIsRestoringDocument = false;
7383 // Hack to keep nsDocShellEditorData alive across the
7384 // SetContentViewer(nullptr) call below.
7385 UniquePtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
7387 // Now remove it from the cached presentation.
7388 mLSHE->SetDocumentViewer(nullptr);
7389 mEODForCurrentDocument = false;
7391 mLSHE->SetEditorData(data.release());
7393 #ifdef DEBUG
7395 nsCOMPtr<nsIMutableArray> refreshURIs = mLSHE->GetRefreshURIList();
7396 nsCOMPtr<nsIDocShellTreeItem> childShell;
7397 mLSHE->ChildShellAt(0, getter_AddRefs(childShell));
7398 NS_ASSERTION(!refreshURIs && !childShell,
7399 "SHEntry should have cleared presentation state");
7401 #endif
7403 // Restore the sticky state of the viewer. The viewer has set this state
7404 // on the history entry in Destroy() just before marking itself non-sticky,
7405 // to avoid teardown of the presentation.
7406 mDocumentViewer->SetSticky(sticky);
7408 NS_ENSURE_SUCCESS(rv, rv);
7410 // mLSHE is now our currently-loaded document.
7411 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
7413 // We aren't going to restore any items from the LayoutHistoryState,
7414 // but we don't want them to stay around in case the page is reloaded.
7415 SetLayoutHistoryState(nullptr);
7417 // This is the end of our Embed() replacement
7419 mSavingOldViewer = false;
7420 mEODForCurrentDocument = false;
7422 if (document) {
7423 RefPtr<nsDocShell> parent = GetInProcessParentDocshell();
7424 if (parent) {
7425 RefPtr<Document> d = parent->GetDocument();
7426 if (d) {
7427 if (d->EventHandlingSuppressed()) {
7428 document->SuppressEventHandling(d->EventHandlingSuppressed());
7433 // Use the uri from the mLSHE we had when we entered this function
7434 // (which need not match the document's URI if anchors are involved),
7435 // since that's the history entry we're loading. Note that if we use
7436 // origLSHE we don't have to worry about whether the entry in question
7437 // is still mLSHE or whether it's now mOSHE.
7438 nsCOMPtr<nsIURI> uri = origLSHE->GetURI();
7439 SetCurrentURI(uri, document->GetChannel(), /* aFireLocationChange */ true,
7440 /* aIsInitialAboutBlank */ false,
7441 /* aLocationFlags */ 0);
7444 // This is the end of our CreateDocumentViewer() replacement.
7445 // Now we simulate a load. First, we restore the state of the javascript
7446 // window object.
7447 nsCOMPtr<nsPIDOMWindowOuter> privWin = GetWindow();
7448 NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface");
7450 // Now, dispatch a title change event which would happen as the
7451 // <head> is parsed.
7452 document->NotifyPossibleTitleChange(false);
7454 // Now we simulate appending child docshells for subframes.
7455 for (i = 0; i < childShells.Count(); ++i) {
7456 nsIDocShellTreeItem* childItem = childShells.ObjectAt(i);
7457 nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
7459 // Make sure to not clobber the state of the child. Since AddChild
7460 // always clobbers it, save it off first.
7461 bool allowRedirects;
7462 childShell->GetAllowMetaRedirects(&allowRedirects);
7464 bool allowSubframes;
7465 childShell->GetAllowSubframes(&allowSubframes);
7467 bool allowImages;
7468 childShell->GetAllowImages(&allowImages);
7470 bool allowMedia = childShell->GetAllowMedia();
7472 bool allowDNSPrefetch;
7473 childShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7475 bool allowContentRetargeting = childShell->GetAllowContentRetargeting();
7476 bool allowContentRetargetingOnChildren =
7477 childShell->GetAllowContentRetargetingOnChildren();
7479 // this.AddChild(child) calls child.SetDocLoaderParent(this), meaning that
7480 // the child inherits our state. Among other things, this means that the
7481 // child inherits our mPrivateBrowsingId, which is what we want.
7482 AddChild(childItem);
7484 childShell->SetAllowMetaRedirects(allowRedirects);
7485 childShell->SetAllowSubframes(allowSubframes);
7486 childShell->SetAllowImages(allowImages);
7487 childShell->SetAllowMedia(allowMedia);
7488 childShell->SetAllowDNSPrefetch(allowDNSPrefetch);
7489 childShell->SetAllowContentRetargeting(allowContentRetargeting);
7490 childShell->SetAllowContentRetargetingOnChildren(
7491 allowContentRetargetingOnChildren);
7493 rv = childShell->BeginRestore(nullptr, false);
7494 NS_ENSURE_SUCCESS(rv, rv);
7497 // Make sure to restore the window state after adding the child shells back
7498 // to the tree. This is necessary for Thaw() and Resume() to propagate
7499 // properly.
7500 rv = privWin->RestoreWindowState(windowState);
7501 NS_ENSURE_SUCCESS(rv, rv);
7503 RefPtr<PresShell> presShell = GetPresShell();
7505 // We may be displayed on a different monitor (or in a different
7506 // HiDPI mode) than when we got into the history list. So we need
7507 // to check if this has happened. See bug 838239.
7509 // Because the prescontext normally handles resolution changes via
7510 // a runnable (see nsPresContext::UIResolutionChanged), its device
7511 // context won't be -immediately- updated as a result of calling
7512 // presShell->BackingScaleFactorChanged().
7514 // But we depend on that device context when adjusting the view size
7515 // via mDocumentViewer->SetBounds(newBounds) below. So we need to
7516 // explicitly tell it to check for changed resolution here.
7517 if (presShell) {
7518 RefPtr<nsPresContext> pc = presShell->GetPresContext();
7519 if (pc->DeviceContext()->CheckDPIChange()) {
7520 presShell->BackingScaleFactorChanged();
7522 // Recompute zoom and text-zoom and such.
7523 pc->RecomputeBrowsingContextDependentData();
7526 nsViewManager* newVM = presShell ? presShell->GetViewManager() : nullptr;
7527 nsView* newRootView = newVM ? newVM->GetRootView() : nullptr;
7529 // Insert the new root view at the correct location in the view tree.
7530 if (container) {
7531 nsSubDocumentFrame* subDocFrame =
7532 do_QueryFrame(container->GetPrimaryFrame());
7533 rootViewParent = subDocFrame ? subDocFrame->EnsureInnerView() : nullptr;
7534 } else {
7535 rootViewParent = nullptr;
7537 if (sibling && sibling->GetPresShell() &&
7538 sibling->GetPresShell()->GetViewManager()) {
7539 rootViewSibling = sibling->GetPresShell()->GetViewManager()->GetRootView();
7540 } else {
7541 rootViewSibling = nullptr;
7543 if (rootViewParent && newRootView &&
7544 newRootView->GetParent() != rootViewParent) {
7545 nsViewManager* parentVM = rootViewParent->GetViewManager();
7546 if (parentVM) {
7547 // InsertChild(parent, child, sib, true) inserts the child after
7548 // sib in content order, which is before sib in view order. BUT
7549 // when sib is null it inserts at the end of the the document
7550 // order, i.e., first in view order. But when oldRootSibling is
7551 // null, the old root as at the end of the view list --- last in
7552 // content order --- and we want to call InsertChild(parent, child,
7553 // nullptr, false) in that case.
7554 parentVM->InsertChild(rootViewParent, newRootView, rootViewSibling,
7555 rootViewSibling ? true : false);
7557 NS_ASSERTION(newRootView->GetNextSibling() == rootViewSibling,
7558 "error in InsertChild");
7562 nsCOMPtr<nsPIDOMWindowInner> privWinInner = privWin->GetCurrentInnerWindow();
7564 // If parent is suspended, increase suspension count.
7565 // This can't be done as early as event suppression since this
7566 // depends on docshell tree.
7567 privWinInner->SyncStateFromParentWindow();
7569 // Now that all of the child docshells have been put into place, we can
7570 // restart the timers for the window and all of the child frames.
7571 privWinInner->Resume();
7573 // Now that we have found the inner window of the page restored
7574 // from the history, we have to make sure that
7575 // performance.navigation.type is 2.
7576 Performance* performance = privWinInner->GetPerformance();
7577 if (performance) {
7578 performance->GetDOMTiming()->NotifyRestoreStart();
7581 // Restore the refresh URI list. The refresh timers will be restarted
7582 // when EndPageLoad() is called.
7583 mRefreshURIList = refreshURIList;
7585 // Meta-refresh timers have been restarted for this shell, but not
7586 // for our children. Walk the child shells and restart their timers.
7587 for (auto* childDocLoader : mChildList.ForwardRange()) {
7588 nsCOMPtr<nsIDocShell> child = do_QueryObject(childDocLoader);
7589 if (child) {
7590 child->ResumeRefreshURIs();
7594 // Make sure this presentation is the same size as the previous
7595 // presentation. If this is not the same size we showed it at last time,
7596 // then we need to resize the widget.
7598 // XXXbryner This interacts poorly with Firefox's infobar. If the old
7599 // presentation had the infobar visible, then we will resize the new
7600 // presentation to that smaller size. However, firing the locationchanged
7601 // event will hide the infobar, which will immediately resize the window
7602 // back to the larger size. A future optimization might be to restore
7603 // the presentation at the "wrong" size, then fire the locationchanged
7604 // event and check whether the docshell's new size is the same as the
7605 // cached viewer size (skipping the resize if they are equal).
7607 if (newRootView) {
7608 if (!newBounds.IsEmpty() && !newBounds.IsEqualEdges(oldBounds)) {
7609 MOZ_LOG(gPageCacheLog, LogLevel::Debug,
7610 ("resize widget(%d, %d, %d, %d)", newBounds.x, newBounds.y,
7611 newBounds.width, newBounds.height));
7612 mDocumentViewer->SetBounds(newBounds);
7613 } else if (ScrollContainerFrame* sf =
7614 presShell->GetRootScrollContainerFrame()) {
7615 sf->PostScrolledAreaEventForCurrentArea();
7619 // The FinishRestore call below can kill these, null them out so we don't
7620 // have invalid pointer lying around.
7621 newRootView = rootViewSibling = rootViewParent = nullptr;
7622 newVM = nullptr;
7624 // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
7625 // update it.
7626 if (oldPresShell && presShell &&
7627 presShell->IsUnderHiddenEmbedderElement() !=
7628 oldPresShell->IsUnderHiddenEmbedderElement()) {
7629 presShell->SetIsUnderHiddenEmbedderElement(
7630 oldPresShell->IsUnderHiddenEmbedderElement());
7633 // Simulate the completion of the load.
7634 nsDocShell::FinishRestore();
7636 // Restart plugins, and paint the content.
7637 if (presShell) {
7638 presShell->Thaw();
7641 return privWin->FireDelayedDOMEvents(true);
7644 nsresult nsDocShell::CreateDocumentViewer(const nsACString& aContentType,
7645 nsIRequest* aRequest,
7646 nsIStreamListener** aContentHandler) {
7647 *aContentHandler = nullptr;
7649 if (!mTreeOwner || mIsBeingDestroyed) {
7650 // If we don't have a tree owner, then we're in the process of being
7651 // destroyed. Rather than continue trying to load something, just give up.
7652 return NS_ERROR_DOCSHELL_DYING;
7655 if (!mBrowsingContext->AncestorsAreCurrent() ||
7656 mBrowsingContext->IsInBFCache()) {
7657 mBrowsingContext->RemoveRootFromBFCacheSync();
7658 return NS_ERROR_NOT_AVAILABLE;
7661 // Can we check the content type of the current content viewer
7662 // and reuse it without destroying it and re-creating it?
7664 NS_ASSERTION(mLoadGroup, "Someone ignored return from Init()?");
7666 // Instantiate the content viewer object
7667 nsCOMPtr<nsIDocumentViewer> viewer;
7668 nsresult rv = NewDocumentViewerObj(aContentType, aRequest, mLoadGroup,
7669 aContentHandler, getter_AddRefs(viewer));
7671 if (NS_FAILED(rv)) {
7672 return rv;
7675 // Notify the current document that it is about to be unloaded!!
7677 // It is important to fire the unload() notification *before* any state
7678 // is changed within the DocShell - otherwise, javascript will get the
7679 // wrong information :-(
7682 if (mSavingOldViewer) {
7683 // We determined that it was safe to cache the document presentation
7684 // at the time we initiated the new load. We need to check whether
7685 // it's still safe to do so, since there may have been DOM mutations
7686 // or new requests initiated.
7687 RefPtr<Document> doc = viewer->GetDocument();
7688 mSavingOldViewer = CanSavePresentation(
7689 mLoadType, aRequest, doc, /* aReportBFCacheComboTelemetry */ false);
7692 NS_ASSERTION(!mLoadingURI, "Re-entering unload?");
7694 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7695 if (aOpenedChannel) {
7696 aOpenedChannel->GetURI(getter_AddRefs(mLoadingURI));
7699 // Grab the current URI, we need to pass it to Embed, and OnNewURI will reset
7700 // it before we do call Embed.
7701 nsCOMPtr<nsIURI> previousURI = mCurrentURI;
7703 FirePageHideNotification(!mSavingOldViewer);
7704 if (mIsBeingDestroyed) {
7705 // Force to stop the newly created orphaned viewer.
7706 viewer->Stop();
7707 return NS_ERROR_DOCSHELL_DYING;
7709 mLoadingURI = nullptr;
7711 // Set mFiredUnloadEvent = false so that the unload handler for the
7712 // *new* document will fire.
7713 mFiredUnloadEvent = false;
7715 // we've created a new document so go ahead and call
7716 // OnNewURI(), but don't fire OnLocationChange()
7717 // notifications before we've called Embed(). See bug 284993.
7718 mURIResultedInDocument = true;
7719 bool errorOnLocationChangeNeeded = false;
7720 nsCOMPtr<nsIChannel> failedChannel = mFailedChannel;
7721 nsCOMPtr<nsIURI> failedURI;
7723 if (mLoadType == LOAD_ERROR_PAGE) {
7724 // We need to set the SH entry and our current URI here and not
7725 // at the moment we load the page. We want the same behavior
7726 // of Stop() as for a normal page load. See bug 514232 for details.
7728 // Revert mLoadType to load type to state the page load failed,
7729 // following function calls need it.
7730 mLoadType = mFailedLoadType;
7732 Document* doc = viewer->GetDocument();
7733 if (doc) {
7734 doc->SetFailedChannel(failedChannel);
7737 nsCOMPtr<nsIPrincipal> triggeringPrincipal;
7738 if (failedChannel) {
7739 // Make sure we have a URI to set currentURI.
7740 NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedURI));
7741 } else {
7742 // if there is no failed channel we have to explicitly provide
7743 // a triggeringPrincipal for the history entry.
7744 triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
7747 if (!failedURI) {
7748 failedURI = mFailedURI;
7750 if (!failedURI) {
7751 // We need a URI object to store a session history entry, so make up a URI
7752 NS_NewURI(getter_AddRefs(failedURI), "about:blank");
7755 // When we don't have failedURI, something wrong will happen. See
7756 // bug 291876.
7757 MOZ_ASSERT(failedURI, "We don't have a URI for history APIs.");
7759 mFailedChannel = nullptr;
7760 mFailedURI = nullptr;
7762 // Create an shistory entry for the old load.
7763 if (failedURI) {
7764 errorOnLocationChangeNeeded =
7765 OnNewURI(failedURI, failedChannel, triggeringPrincipal, nullptr,
7766 nullptr, nullptr, false, false);
7769 // Be sure to have a correct mLSHE, it may have been cleared by
7770 // EndPageLoad. See bug 302115.
7771 ChildSHistory* shistory = GetSessionHistory();
7772 if (!mozilla::SessionHistoryInParent() && shistory && !mLSHE) {
7773 int32_t idx = shistory->LegacySHistory()->GetRequestedIndex();
7774 if (idx == -1) {
7775 idx = shistory->Index();
7777 shistory->LegacySHistory()->GetEntryAtIndex(idx, getter_AddRefs(mLSHE));
7780 mLoadType = LOAD_ERROR_PAGE;
7783 nsCOMPtr<nsIURI> finalURI;
7784 // If this a redirect, use the final url (uri)
7785 // else use the original url
7787 // Note that this should match what documents do (see Document::Reset).
7788 NS_GetFinalChannelURI(aOpenedChannel, getter_AddRefs(finalURI));
7790 bool onLocationChangeNeeded = false;
7791 if (finalURI) {
7792 // Pass false for aCloneSHChildren, since we're loading a new page here.
7793 onLocationChangeNeeded = OnNewURI(finalURI, aOpenedChannel, nullptr,
7794 nullptr, nullptr, nullptr, true, false);
7797 // let's try resetting the load group if we need to...
7798 nsCOMPtr<nsILoadGroup> currentLoadGroup;
7799 NS_ENSURE_SUCCESS(
7800 aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
7801 NS_ERROR_FAILURE);
7803 if (currentLoadGroup != mLoadGroup) {
7804 nsLoadFlags loadFlags = 0;
7806 // Cancel any URIs that are currently loading...
7807 // XXX: Need to do this eventually Stop();
7809 // Retarget the document to this loadgroup...
7811 /* First attach the channel to the right loadgroup
7812 * and then remove from the old loadgroup. This
7813 * puts the notifications in the right order and
7814 * we don't null-out mLSHE in OnStateChange() for
7815 * all redirected urls
7817 aOpenedChannel->SetLoadGroup(mLoadGroup);
7819 // Mark the channel as being a document URI...
7820 aOpenedChannel->GetLoadFlags(&loadFlags);
7821 loadFlags |= nsIChannel::LOAD_DOCUMENT_URI;
7822 nsCOMPtr<nsILoadInfo> loadInfo = aOpenedChannel->LoadInfo();
7823 if (SandboxFlagsImplyCookies(loadInfo->GetSandboxFlags())) {
7824 loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
7827 aOpenedChannel->SetLoadFlags(loadFlags);
7829 mLoadGroup->AddRequest(aRequest, nullptr);
7830 if (currentLoadGroup) {
7831 currentLoadGroup->RemoveRequest(aRequest, nullptr, NS_BINDING_RETARGETED);
7834 // Update the notification callbacks, so that progress and
7835 // status information are sent to the right docshell...
7836 aOpenedChannel->SetNotificationCallbacks(this);
7839 NS_ENSURE_SUCCESS(Embed(viewer, nullptr, false,
7840 ShouldAddToSessionHistory(finalURI, aOpenedChannel),
7841 aOpenedChannel, previousURI),
7842 NS_ERROR_FAILURE);
7844 if (!mBrowsingContext->GetHasLoadedNonInitialDocument()) {
7845 MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetHasLoadedNonInitialDocument(true));
7848 mSavedRefreshURIList = nullptr;
7849 mSavingOldViewer = false;
7850 mEODForCurrentDocument = false;
7852 // if this document is part of a multipart document,
7853 // the ID can be used to distinguish it from the other parts.
7854 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aRequest));
7855 if (multiPartChannel) {
7856 if (PresShell* presShell = GetPresShell()) {
7857 if (Document* doc = presShell->GetDocument()) {
7858 uint32_t partID;
7859 multiPartChannel->GetPartID(&partID);
7860 doc->SetPartID(partID);
7865 if (errorOnLocationChangeNeeded) {
7866 FireOnLocationChange(this, failedChannel, failedURI,
7867 LOCATION_CHANGE_ERROR_PAGE);
7868 } else if (onLocationChangeNeeded) {
7869 uint32_t locationFlags =
7870 (mLoadType & LOAD_CMD_RELOAD) ? uint32_t(LOCATION_CHANGE_RELOAD) : 0;
7871 FireOnLocationChange(this, aRequest, mCurrentURI, locationFlags);
7874 return NS_OK;
7877 nsresult nsDocShell::NewDocumentViewerObj(const nsACString& aContentType,
7878 nsIRequest* aRequest,
7879 nsILoadGroup* aLoadGroup,
7880 nsIStreamListener** aContentHandler,
7881 nsIDocumentViewer** aViewer) {
7882 nsCOMPtr<nsIChannel> aOpenedChannel = do_QueryInterface(aRequest);
7884 nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
7885 nsContentUtils::FindInternalDocumentViewer(aContentType);
7886 if (!docLoaderFactory) {
7887 return NS_ERROR_FAILURE;
7890 // Now create an instance of the content viewer nsLayoutDLF makes the
7891 // determination if it should be a "view-source" instead of "view"
7892 nsresult rv = docLoaderFactory->CreateInstance(
7893 "view", aOpenedChannel, aLoadGroup, aContentType, this, nullptr,
7894 aContentHandler, aViewer);
7895 NS_ENSURE_SUCCESS(rv, rv);
7897 (*aViewer)->SetContainer(this);
7898 return NS_OK;
7901 nsresult nsDocShell::SetupNewViewer(nsIDocumentViewer* aNewViewer,
7902 WindowGlobalChild* aWindowActor) {
7903 MOZ_ASSERT(!mIsBeingDestroyed);
7906 // Copy content viewer state from previous or parent content viewer.
7908 // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
7910 // Do NOT to maintain a reference to the old content viewer outside
7911 // of this "copying" block, or it will not be destroyed until the end of
7912 // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
7914 // In this block of code, if we get an error result, we return it
7915 // but if we get a null pointer, that's perfectly legal for parent
7916 // and parentContentViewer.
7919 int32_t x = 0;
7920 int32_t y = 0;
7921 int32_t cx = 0;
7922 int32_t cy = 0;
7924 // This will get the size from the current content viewer or from the
7925 // Init settings
7926 DoGetPositionAndSize(&x, &y, &cx, &cy);
7928 nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
7929 NS_ENSURE_SUCCESS(GetInProcessSameTypeParent(getter_AddRefs(parentAsItem)),
7930 NS_ERROR_FAILURE);
7931 nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
7933 const Encoding* reloadEncoding = nullptr;
7934 int32_t reloadEncodingSource = kCharsetUninitialized;
7935 // |newMUDV| also serves as a flag to set the data from the above vars
7936 nsCOMPtr<nsIDocumentViewer> newViewer;
7938 if (mDocumentViewer || parent) {
7939 nsCOMPtr<nsIDocumentViewer> oldViewer;
7940 if (mDocumentViewer) {
7941 // Get any interesting state from old content viewer
7942 // XXX: it would be far better to just reuse the document viewer ,
7943 // since we know we're just displaying the same document as before
7944 oldViewer = mDocumentViewer;
7946 // Tell the old content viewer to hibernate in session history when
7947 // it is destroyed.
7949 if (mSavingOldViewer && NS_FAILED(CaptureState())) {
7950 if (mOSHE) {
7951 mOSHE->SyncPresentationState();
7953 mSavingOldViewer = false;
7955 } else {
7956 // No old content viewer, so get state from parent's content viewer
7957 parent->GetDocViewer(getter_AddRefs(oldViewer));
7960 if (oldViewer) {
7961 newViewer = aNewViewer;
7962 if (newViewer) {
7963 reloadEncoding =
7964 oldViewer->GetReloadEncodingAndSource(&reloadEncodingSource);
7969 nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
7970 bool isUnderHiddenEmbedderElement = false;
7971 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
7972 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
7973 if (viewer) {
7974 // Stop any activity that may be happening in the old document before
7975 // releasing it...
7976 viewer->Stop();
7978 // Try to extract the canvas background color from the old
7979 // presentation shell, so we can use it for the next document.
7980 if (PresShell* presShell = viewer->GetPresShell()) {
7981 bgcolor = presShell->GetCanvasBackground();
7982 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
7985 viewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
7986 aNewViewer->SetPreviousViewer(viewer);
7988 if (mOSHE && (!mDocumentViewer || !mSavingOldViewer)) {
7989 // We don't plan to save a viewer in mOSHE; tell it to drop
7990 // any other state it's holding.
7991 mOSHE->SyncPresentationState();
7994 mDocumentViewer = nullptr;
7996 // Now that we're about to switch documents, forget all of our children.
7997 // Note that we cached them as needed up in CaptureState above.
7998 DestroyChildren();
8000 mDocumentViewer = aNewViewer;
8002 nsCOMPtr<nsIWidget> widget;
8003 NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
8005 nsIntRect bounds(x, y, cx, cy);
8007 mDocumentViewer->SetNavigationTiming(mTiming);
8009 if (NS_FAILED(mDocumentViewer->Init(widget, bounds, aWindowActor))) {
8010 nsCOMPtr<nsIDocumentViewer> viewer = mDocumentViewer;
8011 viewer->Close(nullptr);
8012 viewer->Destroy();
8013 mDocumentViewer = nullptr;
8014 SetCurrentURIInternal(nullptr);
8015 NS_WARNING("ContentViewer Initialization failed");
8016 return NS_ERROR_FAILURE;
8019 // If we have old state to copy, set the old state onto the new content
8020 // viewer
8021 if (newViewer) {
8022 newViewer->SetReloadEncodingAndSource(reloadEncoding, reloadEncodingSource);
8025 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8027 // Stuff the bgcolor from the old pres shell into the new
8028 // pres shell. This improves page load continuity.
8029 if (RefPtr<PresShell> presShell = mDocumentViewer->GetPresShell()) {
8030 presShell->SetCanvasBackground(bgcolor);
8031 presShell->ActivenessMaybeChanged();
8032 if (isUnderHiddenEmbedderElement) {
8033 presShell->SetIsUnderHiddenEmbedderElement(isUnderHiddenEmbedderElement);
8037 // XXX: It looks like the LayoutState gets restored again in Embed()
8038 // right after the call to SetupNewViewer(...)
8040 // We don't show the mDocumentViewer yet, since we want to draw the old page
8041 // until we have enough of the new page to show. Just return with the new
8042 // viewer still set to hidden.
8044 return NS_OK;
8047 void nsDocShell::SetDocCurrentStateObj(nsISHEntry* aShEntry,
8048 SessionHistoryInfo* aInfo) {
8049 NS_ENSURE_TRUE_VOID(mDocumentViewer);
8051 RefPtr<Document> document = GetDocument();
8052 NS_ENSURE_TRUE_VOID(document);
8054 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
8055 if (mozilla::SessionHistoryInParent()) {
8056 // If aInfo is null, just set the document's state object to null.
8057 if (aInfo) {
8058 scContainer = aInfo->GetStateData();
8060 MOZ_LOG(gSHLog, LogLevel::Debug,
8061 ("nsDocShell %p SetCurrentDocState %p", this, scContainer.get()));
8062 } else {
8063 if (aShEntry) {
8064 scContainer = aShEntry->GetStateData();
8066 // If aShEntry is null, just set the document's state object to null.
8070 // It's OK for scContainer too be null here; that just means there's no
8071 // state data associated with this history entry.
8072 document->SetStateObject(scContainer);
8075 nsresult nsDocShell::CheckLoadingPermissions() {
8076 // This method checks whether the caller may load content into
8077 // this docshell. Even though we've done our best to hide windows
8078 // from code that doesn't have the right to access them, it's
8079 // still possible for an evil site to open a window and access
8080 // frames in the new window through window.frames[] (which is
8081 // allAccess for historic reasons), so we still need to do this
8082 // check on load.
8083 nsresult rv = NS_OK;
8085 if (!IsSubframe()) {
8086 // We're not a frame. Permit all loads.
8087 return rv;
8090 // Note - The check for a current JSContext here isn't necessarily sensical.
8091 // It's just designed to preserve the old semantics during a mass-conversion
8092 // patch.
8093 if (!nsContentUtils::GetCurrentJSContext()) {
8094 return NS_OK;
8097 // Check if the caller is from the same origin as this docshell,
8098 // or any of its ancestors.
8099 for (RefPtr<BrowsingContext> bc = mBrowsingContext; bc;
8100 bc = bc->GetParent()) {
8101 // If the BrowsingContext is not in process, then it
8102 // is true by construction that its principal will not
8103 // subsume the current docshell principal.
8104 if (!bc->IsInProcess()) {
8105 continue;
8108 nsCOMPtr<nsIScriptGlobalObject> sgo =
8109 bc->GetDocShell()->GetScriptGlobalObject();
8110 nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(sgo));
8112 nsIPrincipal* p;
8113 if (!sop || !(p = sop->GetPrincipal())) {
8114 return NS_ERROR_UNEXPECTED;
8117 if (nsContentUtils::SubjectPrincipal()->Subsumes(p)) {
8118 // Same origin, permit load
8119 return NS_OK;
8123 return NS_ERROR_DOM_PROP_ACCESS_DENIED;
8126 //*****************************************************************************
8127 // nsDocShell: Site Loading
8128 //*****************************************************************************
8130 void nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
8131 bool aInPrivateBrowsing) {
8132 if (XRE_IsContentProcess()) {
8133 dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
8134 if (contentChild) {
8135 contentChild->SendCopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
8137 return;
8140 #ifdef MOZ_PLACES
8141 nsCOMPtr<nsIFaviconService> favSvc =
8142 do_GetService("@mozilla.org/browser/favicon-service;1");
8143 if (favSvc) {
8144 favSvc->CopyFavicons(aOldURI, aNewURI,
8145 aInPrivateBrowsing
8146 ? nsIFaviconService::FAVICON_LOAD_PRIVATE
8147 : nsIFaviconService::FAVICON_LOAD_NON_PRIVATE,
8148 nullptr);
8150 #endif
8153 class InternalLoadEvent : public Runnable {
8154 public:
8155 InternalLoadEvent(nsDocShell* aDocShell, nsDocShellLoadState* aLoadState)
8156 : mozilla::Runnable("InternalLoadEvent"),
8157 mDocShell(aDocShell),
8158 mLoadState(aLoadState) {
8159 // For events, both target and filename should be the version of "null" they
8160 // expect. By the time the event is fired, both window targeting and file
8161 // downloading have been handled, so we should never have an internal load
8162 // event that retargets or had a download.
8163 mLoadState->SetTarget(u""_ns);
8164 mLoadState->SetFileName(VoidString());
8167 NS_IMETHOD
8168 Run() override {
8169 #ifndef ANDROID
8170 MOZ_ASSERT(mLoadState->TriggeringPrincipal(),
8171 "InternalLoadEvent: Should always have a principal here");
8172 #endif
8173 return mDocShell->InternalLoad(mLoadState);
8176 private:
8177 RefPtr<nsDocShell> mDocShell;
8178 RefPtr<nsDocShellLoadState> mLoadState;
8182 * Returns true if we started an asynchronous load (i.e., from the network), but
8183 * the document we're loading there hasn't yet become this docshell's active
8184 * document.
8186 * When JustStartedNetworkLoad is true, you should be careful about modifying
8187 * mLoadType and mLSHE. These are both set when the asynchronous load first
8188 * starts, and the load expects that, when it eventually runs InternalLoad,
8189 * mLoadType and mLSHE will have their original values.
8191 bool nsDocShell::JustStartedNetworkLoad() {
8192 return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
8195 // The contentType will be INTERNAL_(I)FRAME if this docshell is for a
8196 // non-toplevel browsing context in spec terms. (frame, iframe, <object>,
8197 // <embed>, etc)
8199 // This return value will be used when we call NS_CheckContentLoadPolicy, and
8200 // later when we call DoURILoad.
8201 nsContentPolicyType nsDocShell::DetermineContentType() {
8202 if (!IsSubframe()) {
8203 return nsIContentPolicy::TYPE_DOCUMENT;
8206 const auto& maybeEmbedderElementType =
8207 GetBrowsingContext()->GetEmbedderElementType();
8208 if (!maybeEmbedderElementType) {
8209 // If the EmbedderElementType hasn't been set yet, just assume we're
8210 // an iframe since that's more common.
8211 return nsIContentPolicy::TYPE_INTERNAL_IFRAME;
8214 return maybeEmbedderElementType->EqualsLiteral("iframe")
8215 ? nsIContentPolicy::TYPE_INTERNAL_IFRAME
8216 : nsIContentPolicy::TYPE_INTERNAL_FRAME;
8219 bool nsDocShell::NoopenerForceEnabled() {
8220 // If current's top-level browsing context's active document's
8221 // cross-origin-opener-policy is "same-origin" or "same-origin + COEP" then
8222 // if currentDoc's origin is not same origin with currentDoc's top-level
8223 // origin, noopener is force enabled, and name is cleared to "_blank".
8224 auto topPolicy = mBrowsingContext->Top()->GetOpenerPolicy();
8225 return (topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN ||
8226 topPolicy ==
8227 nsILoadInfo::
8228 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) &&
8229 !mBrowsingContext->SameOriginWithTop();
8232 nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState) {
8233 MOZ_ASSERT(aLoadState, "need a load state!");
8234 MOZ_ASSERT(!aLoadState->Target().IsEmpty(), "should have a target here!");
8235 MOZ_ASSERT(aLoadState->TargetBrowsingContext().IsNull(),
8236 "should not have picked target yet");
8238 nsresult rv = NS_OK;
8239 RefPtr<BrowsingContext> targetContext;
8241 // Only _self, _parent, and _top are supported in noopener case. But we
8242 // have to be careful to not apply that to the noreferrer case. See bug
8243 // 1358469.
8244 bool allowNamedTarget =
8245 !aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8246 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER);
8247 if (allowNamedTarget ||
8248 aLoadState->Target().LowerCaseEqualsLiteral("_self") ||
8249 aLoadState->Target().LowerCaseEqualsLiteral("_parent") ||
8250 aLoadState->Target().LowerCaseEqualsLiteral("_top")) {
8251 Document* document = GetDocument();
8252 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
8253 WindowGlobalChild* wgc = document->GetWindowGlobalChild();
8254 NS_ENSURE_TRUE(wgc, NS_ERROR_FAILURE);
8255 targetContext = wgc->FindBrowsingContextWithName(
8256 aLoadState->Target(), /* aUseEntryGlobalForAccessCheck */ false);
8259 if (!targetContext) {
8260 // If the targetContext doesn't exist, then this is a new docShell and we
8261 // should consider this a TYPE_DOCUMENT load
8263 // For example, when target="_blank"
8265 // If there's no targetContext, that means we are about to create a new
8266 // window. Perform a content policy check before creating the window. Please
8267 // note for all other docshell loads content policy checks are performed
8268 // within the contentSecurityManager when the channel is about to be
8269 // openend.
8270 nsISupports* requestingContext = nullptr;
8271 if (XRE_IsContentProcess()) {
8272 // In e10s the child process doesn't have access to the element that
8273 // contains the browsing context (because that element is in the chrome
8274 // process). So we just pass mScriptGlobal.
8275 requestingContext = ToSupports(mScriptGlobal);
8276 } else {
8277 // This is for loading non-e10s tabs and toplevel windows of various
8278 // sorts.
8279 // For the toplevel window cases, requestingElement will be null.
8280 nsCOMPtr<Element> requestingElement =
8281 mScriptGlobal->GetFrameElementInternal();
8282 requestingContext = requestingElement;
8285 // Ideally we should use the same loadinfo as within DoURILoad which
8286 // should match this one when both are applicable.
8287 nsCOMPtr<nsILoadInfo> secCheckLoadInfo =
8288 new LoadInfo(mScriptGlobal, aLoadState->URI(),
8289 aLoadState->TriggeringPrincipal(), requestingContext,
8290 nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, 0);
8292 // Since Content Policy checks are performed within docShell as well as
8293 // the ContentSecurityManager we need a reliable way to let certain
8294 // nsIContentPolicy consumers ignore duplicate calls.
8295 secCheckLoadInfo->SetSkipContentPolicyCheckForWebRequest(true);
8297 int16_t shouldLoad = nsIContentPolicy::ACCEPT;
8298 rv = NS_CheckContentLoadPolicy(aLoadState->URI(), secCheckLoadInfo,
8299 &shouldLoad);
8301 if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
8302 if (NS_SUCCEEDED(rv)) {
8303 if (shouldLoad == nsIContentPolicy::REJECT_TYPE) {
8304 return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
8306 if (shouldLoad == nsIContentPolicy::REJECT_POLICY) {
8307 return NS_ERROR_BLOCKED_BY_POLICY;
8311 return NS_ERROR_CONTENT_BLOCKED;
8316 // Resolve the window target before going any further...
8317 // If the load has been targeted to another DocShell, then transfer the
8318 // load to it...
8321 // We've already done our owner-inheriting. Mask out that bit, so we
8322 // don't try inheriting an owner from the target window if we came up
8323 // with a null owner above.
8324 aLoadState->UnsetInternalLoadFlag(INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL);
8326 if (!targetContext) {
8327 // If the docshell's document is sandboxed, only open a new window
8328 // if the document's SANDBOXED_AUXILLARY_NAVIGATION flag is not set.
8329 // (i.e. if allow-popups is specified)
8330 NS_ENSURE_TRUE(mDocumentViewer, NS_ERROR_FAILURE);
8331 Document* doc = mDocumentViewer->GetDocument();
8333 const bool isDocumentAuxSandboxed =
8334 doc && (doc->GetSandboxFlags() & SANDBOXED_AUXILIARY_NAVIGATION);
8336 if (isDocumentAuxSandboxed) {
8337 return NS_ERROR_DOM_INVALID_ACCESS_ERR;
8340 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
8341 NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
8343 RefPtr<BrowsingContext> newBC;
8344 nsAutoCString spec;
8345 aLoadState->URI()->GetSpec(spec);
8347 // If we are a noopener load, we just hand the whole thing over to our
8348 // window.
8349 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_NO_OPENER) ||
8350 NoopenerForceEnabled()) {
8351 // Various asserts that we know to hold because NO_OPENER loads can only
8352 // happen for links.
8353 MOZ_ASSERT(!aLoadState->LoadReplace());
8354 MOZ_ASSERT(aLoadState->PrincipalToInherit() ==
8355 aLoadState->TriggeringPrincipal());
8356 MOZ_ASSERT(!(aLoadState->InternalLoadFlags() &
8357 ~(INTERNAL_LOAD_FLAGS_NO_OPENER |
8358 INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER)),
8359 "Only INTERNAL_LOAD_FLAGS_NO_OPENER and "
8360 "INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER can be set");
8361 MOZ_ASSERT_IF(aLoadState->PostDataStream(),
8362 aLoadState->IsFormSubmission());
8363 MOZ_ASSERT(!aLoadState->HeadersStream());
8364 // If OnLinkClickSync was invoked inside the onload handler, the load
8365 // type would be set to LOAD_NORMAL_REPLACE; otherwise it should be
8366 // LOAD_LINK.
8367 MOZ_ASSERT(aLoadState->LoadType() == LOAD_LINK ||
8368 aLoadState->LoadType() == LOAD_NORMAL_REPLACE);
8369 MOZ_ASSERT(!aLoadState->LoadIsFromSessionHistory());
8370 MOZ_ASSERT(aLoadState->FirstParty()); // Windowwatcher will assume this.
8372 RefPtr<nsDocShellLoadState> loadState =
8373 new nsDocShellLoadState(aLoadState->URI());
8375 // Set up our loadinfo so it will do the load as much like we would have
8376 // as possible.
8377 loadState->SetReferrerInfo(aLoadState->GetReferrerInfo());
8378 loadState->SetOriginalURI(aLoadState->OriginalURI());
8380 Maybe<nsCOMPtr<nsIURI>> resultPrincipalURI;
8381 aLoadState->GetMaybeResultPrincipalURI(resultPrincipalURI);
8383 loadState->SetMaybeResultPrincipalURI(resultPrincipalURI);
8384 loadState->SetKeepResultPrincipalURIIfSet(
8385 aLoadState->KeepResultPrincipalURIIfSet());
8386 // LoadReplace will always be false due to asserts above, skip setting
8387 // it.
8388 loadState->SetTriggeringPrincipal(aLoadState->TriggeringPrincipal());
8389 loadState->SetTriggeringSandboxFlags(
8390 aLoadState->TriggeringSandboxFlags());
8391 loadState->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
8392 loadState->SetTriggeringStorageAccess(
8393 aLoadState->TriggeringStorageAccess());
8394 loadState->SetCsp(aLoadState->Csp());
8395 loadState->SetInheritPrincipal(aLoadState->HasInternalLoadFlags(
8396 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL));
8397 // Explicit principal because we do not want any guesses as to what the
8398 // principal to inherit is: it should be aTriggeringPrincipal.
8399 loadState->SetPrincipalIsExplicit(true);
8400 loadState->SetLoadType(aLoadState->LoadType());
8401 loadState->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
8402 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
8404 loadState->SetHasValidUserGestureActivation(
8405 aLoadState->HasValidUserGestureActivation());
8407 loadState->SetTextDirectiveUserActivation(
8408 aLoadState->GetTextDirectiveUserActivation());
8410 // Propagate POST data to the new load.
8411 loadState->SetPostDataStream(aLoadState->PostDataStream());
8412 loadState->SetIsFormSubmission(aLoadState->IsFormSubmission());
8414 rv = win->Open(NS_ConvertUTF8toUTF16(spec),
8415 aLoadState->Target(), // window name
8416 u""_ns, // Features
8417 loadState,
8418 true, // aForceNoOpener
8419 getter_AddRefs(newBC));
8420 MOZ_ASSERT(!newBC);
8421 return rv;
8424 rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
8425 aLoadState->Target(), // window name
8426 u""_ns, // Features
8427 getter_AddRefs(newBC));
8429 // In some cases the Open call doesn't actually result in a new
8430 // window being opened. We can detect these cases by examining the
8431 // document in |newBC|, if any.
8432 nsCOMPtr<nsPIDOMWindowOuter> piNewWin =
8433 newBC ? newBC->GetDOMWindow() : nullptr;
8434 if (piNewWin) {
8435 RefPtr<Document> newDoc = piNewWin->GetExtantDoc();
8436 if (!newDoc || newDoc->IsInitialDocument()) {
8437 aLoadState->SetInternalLoadFlag(INTERNAL_LOAD_FLAGS_FIRST_LOAD);
8441 if (newBC) {
8442 targetContext = newBC;
8445 NS_ENSURE_SUCCESS(rv, rv);
8446 NS_ENSURE_TRUE(targetContext, rv);
8448 // If our target BrowsingContext is still pending initialization, ignore the
8449 // navigation request targeting it.
8450 if (NS_WARN_IF(targetContext->GetPendingInitialization())) {
8451 return NS_OK;
8454 aLoadState->SetTargetBrowsingContext(targetContext);
8455 if (aLoadState->IsFormSubmission()) {
8456 aLoadState->SetLoadType(
8457 GetLoadTypeForFormSubmission(targetContext, aLoadState));
8461 // Transfer the load to the target BrowsingContext... Clear the window target
8462 // name to the empty string to prevent recursive retargeting!
8464 // No window target
8465 aLoadState->SetTarget(u""_ns);
8466 // No forced download
8467 aLoadState->SetFileName(VoidString());
8468 return targetContext->InternalLoad(aLoadState);
8471 static nsAutoCString RefMaybeNull(nsIURI* aURI) {
8472 nsAutoCString result;
8473 if (NS_FAILED(aURI->GetRef(result))) {
8474 result.SetIsVoid(true);
8476 return result;
8479 uint32_t nsDocShell::GetSameDocumentNavigationFlags(nsIURI* aNewURI) {
8480 uint32_t flags = LOCATION_CHANGE_SAME_DOCUMENT;
8482 bool equal = false;
8483 if (mCurrentURI &&
8484 NS_SUCCEEDED(mCurrentURI->EqualsExceptRef(aNewURI, &equal)) && equal &&
8485 RefMaybeNull(mCurrentURI) != RefMaybeNull(aNewURI)) {
8486 flags |= LOCATION_CHANGE_HASHCHANGE;
8489 return flags;
8492 bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
8493 SameDocumentNavigationState& aState) {
8494 MOZ_ASSERT(aLoadState);
8495 if (!(aLoadState->LoadType() == LOAD_NORMAL ||
8496 aLoadState->LoadType() == LOAD_STOP_CONTENT ||
8497 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
8498 LOAD_FLAGS_REPLACE_HISTORY) ||
8499 aLoadState->LoadType() == LOAD_HISTORY ||
8500 aLoadState->LoadType() == LOAD_LINK)) {
8501 return false;
8504 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8506 nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
8507 if (NS_SUCCEEDED(rvURINew)) {
8508 rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
8511 // A Fragment Directive must be removed from the new hash in order to allow
8512 // fallback element id scroll. Additionally, the extracted parsed text
8513 // directives need to be stored for further use.
8514 nsTArray<TextDirective> textDirectives;
8515 if (FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragmentString(
8516 aState.mNewHash, &textDirectives, aLoadState->URI())) {
8517 if (Document* doc = GetDocument()) {
8518 doc->FragmentDirective()->SetTextDirectives(std::move(textDirectives));
8522 if (currentURI && NS_SUCCEEDED(rvURINew)) {
8523 nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
8524 if (NS_SUCCEEDED(rvURIOld)) {
8525 rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
8527 if (NS_SUCCEEDED(rvURIOld)) {
8528 if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
8529 &aState.mSameExceptHashes))) {
8530 aState.mSameExceptHashes = false;
8535 if (!aState.mSameExceptHashes && currentURI && NS_SUCCEEDED(rvURINew)) {
8536 // Maybe aLoadState->URI() came from the exposable form of currentURI?
8537 nsCOMPtr<nsIURI> currentExposableURI =
8538 nsIOService::CreateExposableURI(currentURI);
8539 nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
8540 if (NS_SUCCEEDED(rvURIOld)) {
8541 rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
8543 if (NS_SUCCEEDED(rvURIOld)) {
8544 if (NS_FAILED(currentExposableURI->EqualsExceptRef(
8545 aLoadState->URI(), &aState.mSameExceptHashes))) {
8546 aState.mSameExceptHashes = false;
8548 // HTTPS-Only Mode upgrades schemes from http to https in Necko, hence we
8549 // have to perform a special check here to avoid an actual navigation. If
8550 // HTTPS-Only Mode is enabled and the two URIs are same-origin (modulo the
8551 // fact that the new URI is currently http), then set mSameExceptHashes to
8552 // true and only perform a fragment navigation.
8553 if (!aState.mSameExceptHashes) {
8554 if (nsCOMPtr<nsIChannel> docChannel = GetCurrentDocChannel()) {
8555 nsCOMPtr<nsILoadInfo> docLoadInfo = docChannel->LoadInfo();
8556 if (!docLoadInfo->GetLoadErrorPage() &&
8557 nsHTTPSOnlyUtils::ShouldUpgradeConnection(docLoadInfo) &&
8558 nsHTTPSOnlyUtils::IsHttpDowngrade(currentExposableURI,
8559 aLoadState->URI())) {
8560 uint32_t status = docLoadInfo->GetHttpsOnlyStatus();
8561 if (status & (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED |
8562 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
8563 // At this point the requested URI is for sure a fragment
8564 // navigation via HTTP and HTTPS-Only mode or HTTPS-First is
8565 // enabled. Also it is not interfering the upgrade order of
8566 // https://searchfox.org/mozilla-central/source/netwerk/base/nsNetUtil.cpp#2948-2953.
8567 // Since we are on an HTTPS site the fragment
8568 // navigation should also be an HTTPS.
8569 // For that reason we should upgrade the URI to HTTPS.
8570 aState.mSecureUpgradeURI = true;
8571 aState.mSameExceptHashes = true;
8579 if (mozilla::SessionHistoryInParent()) {
8580 if (mActiveEntry && aLoadState->LoadIsFromSessionHistory()) {
8581 aState.mHistoryNavBetweenSameDoc = mActiveEntry->SharesDocumentWith(
8582 aLoadState->GetLoadingSessionHistoryInfo()->mInfo);
8584 MOZ_LOG(gSHLog, LogLevel::Debug,
8585 ("nsDocShell::IsSameDocumentNavigation %p NavBetweenSameDoc=%d",
8586 this, aState.mHistoryNavBetweenSameDoc));
8587 } else {
8588 if (mOSHE && aLoadState->LoadIsFromSessionHistory()) {
8589 // We're doing a history load.
8591 mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
8592 &aState.mHistoryNavBetweenSameDoc);
8596 // A same document navigation happens when we navigate between two SHEntries
8597 // for the same document. We do a same document navigation under two
8598 // circumstances. Either
8600 // a) we're navigating between two different SHEntries which share a
8601 // document, or
8603 // b) we're navigating to a new shentry whose URI differs from the
8604 // current URI only in its hash, the new hash is non-empty, and
8605 // we're not doing a POST.
8607 // The restriction that the SHEntries in (a) must be different ensures
8608 // that history.go(0) and the like trigger full refreshes, rather than
8609 // same document navigations.
8610 if (!mozilla::SessionHistoryInParent()) {
8611 bool doSameDocumentNavigation =
8612 (aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
8613 (!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
8614 aState.mSameExceptHashes && aState.mNewURIHasRef);
8615 MOZ_LOG(gSHLog, LogLevel::Debug,
8616 ("nsDocShell %p NavBetweenSameDoc=%d is same doc = %d", this,
8617 aState.mHistoryNavBetweenSameDoc, doSameDocumentNavigation));
8618 return doSameDocumentNavigation;
8621 if (aState.mHistoryNavBetweenSameDoc &&
8622 !aLoadState->GetLoadingSessionHistoryInfo()->mLoadingCurrentEntry) {
8623 return true;
8626 MOZ_LOG(
8627 gSHLog, LogLevel::Debug,
8628 ("nsDocShell::IsSameDocumentNavigation %p !LoadIsFromSessionHistory=%s "
8629 "!PostDataStream: %s mSameExceptHashes: %s mNewURIHasRef: %s",
8630 this, !aLoadState->LoadIsFromSessionHistory() ? "true" : "false",
8631 !aLoadState->PostDataStream() ? "true" : "false",
8632 aState.mSameExceptHashes ? "true" : "false",
8633 aState.mNewURIHasRef ? "true" : "false"));
8634 return !aLoadState->LoadIsFromSessionHistory() &&
8635 !aLoadState->PostDataStream() && aState.mSameExceptHashes &&
8636 aState.mNewURIHasRef;
8639 nsresult nsDocShell::HandleSameDocumentNavigation(
8640 nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState,
8641 bool& aSameDocument) {
8642 aSameDocument = true;
8643 #ifdef DEBUG
8644 SameDocumentNavigationState state;
8645 MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
8646 #endif
8648 MOZ_LOG(gSHLog, LogLevel::Debug,
8649 ("nsDocShell::HandleSameDocumentNavigation %p %s -> %s", this,
8650 mCurrentURI->GetSpecOrDefault().get(),
8651 aLoadState->URI()->GetSpecOrDefault().get()));
8653 RefPtr<Document> doc = GetDocument();
8654 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
8655 doc->DoNotifyPossibleTitleChange();
8657 nsCOMPtr<nsIURI> currentURI = mCurrentURI;
8659 // We need to upgrade the new URI from http: to https:
8660 nsCOMPtr<nsIURI> newURI = aLoadState->URI();
8661 if (aState.mSecureUpgradeURI) {
8662 MOZ_TRY(NS_GetSecureUpgradedURI(aLoadState->URI(), getter_AddRefs(newURI)));
8663 MOZ_LOG(gSHLog, LogLevel::Debug,
8664 ("Upgraded URI to %s", newURI->GetSpecOrDefault().get()));
8667 if (StaticPrefs::dom_security_setdocumenturi()) {
8668 // check if aLoadState->URI(), principalURI, mCurrentURI are same origin
8669 // skip handling otherwise
8670 nsCOMPtr<nsIPrincipal> origPrincipal = doc->NodePrincipal();
8671 nsCOMPtr<nsIURI> principalURI = origPrincipal->GetURI();
8672 if (origPrincipal->GetIsNullPrincipal()) {
8673 nsCOMPtr<nsIPrincipal> precursor = origPrincipal->GetPrecursorPrincipal();
8674 if (precursor) {
8675 principalURI = precursor->GetURI();
8679 if (nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
8680 newURI) ||
8681 nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
8682 mCurrentURI) ||
8683 nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(mCurrentURI,
8684 newURI)) {
8685 aSameDocument = false;
8686 MOZ_LOG(gSHLog, LogLevel::Debug,
8687 ("nsDocShell[%p]: possible violation of the same origin policy "
8688 "during same document navigation",
8689 this));
8690 return NS_OK;
8694 #ifdef DEBUG
8695 if (aState.mSameExceptHashes) {
8696 bool sameExceptHashes = false;
8697 currentURI->EqualsExceptRef(newURI, &sameExceptHashes);
8698 MOZ_ASSERT(sameExceptHashes);
8700 #endif
8701 const nsCOMPtr<nsILoadInfo> loadInfo =
8702 doc->GetChannel() ? doc->GetChannel()->LoadInfo() : nullptr;
8703 if (loadInfo) {
8704 loadInfo->SetIsSameDocumentNavigation(true);
8706 // Save the position of the scrollers.
8707 nsPoint scrollPos = GetCurScrollPos();
8709 // Reset mLoadType to its original value once we exit this block, because this
8710 // same document navigation might have started after a normal, network load,
8711 // and we don't want to clobber its load type. See bug 737307.
8712 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
8714 // If a non-same-document-navigation (i.e., a network load) is pending, make
8715 // this a replacement load, so that we don't add a SHEntry here and the
8716 // network load goes into the SHEntry it expects to.
8717 if (JustStartedNetworkLoad() && (aLoadState->LoadType() & LOAD_CMD_NORMAL)) {
8718 mLoadType = LOAD_NORMAL_REPLACE;
8719 } else {
8720 mLoadType = aLoadState->LoadType();
8723 mURIResultedInDocument = true;
8725 nsCOMPtr<nsISHEntry> oldLSHE = mLSHE;
8727 // we need to assign aLoadState->SHEntry() to mLSHE right here, so that on
8728 // History loads, SetCurrentURI() called from OnNewURI() will send proper
8729 // onLocationChange() notifications to the browser to update back/forward
8730 // buttons.
8731 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
8732 Nothing());
8733 UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> oldLoadingEntry;
8734 mLoadingEntry.swap(oldLoadingEntry);
8735 if (aLoadState->GetLoadingSessionHistoryInfo()) {
8736 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(
8737 *aLoadState->GetLoadingSessionHistoryInfo());
8738 mNeedToReportActiveAfterLoadingBecomesActive = false;
8741 // Set the doc's URI according to the new history entry's URI.
8742 doc->SetDocumentURI(newURI);
8744 /* This is a anchor traversal within the same page.
8745 * call OnNewURI() so that, this traversal will be
8746 * recorded in session and global history.
8748 nsCOMPtr<nsIPrincipal> newURITriggeringPrincipal, newURIPrincipalToInherit,
8749 newURIPartitionedPrincipalToInherit;
8750 nsCOMPtr<nsIContentSecurityPolicy> newCsp;
8751 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
8752 if (mozilla::SessionHistoryInParent()) {
8753 newURITriggeringPrincipal = mActiveEntry->GetTriggeringPrincipal();
8754 newURIPrincipalToInherit = mActiveEntry->GetPrincipalToInherit();
8755 newURIPartitionedPrincipalToInherit =
8756 mActiveEntry->GetPartitionedPrincipalToInherit();
8757 newCsp = mActiveEntry->GetCsp();
8758 } else {
8759 newURITriggeringPrincipal = mOSHE->GetTriggeringPrincipal();
8760 newURIPrincipalToInherit = mOSHE->GetPrincipalToInherit();
8761 newURIPartitionedPrincipalToInherit =
8762 mOSHE->GetPartitionedPrincipalToInherit();
8763 newCsp = mOSHE->GetCsp();
8765 } else {
8766 newURITriggeringPrincipal = aLoadState->TriggeringPrincipal();
8767 newURIPrincipalToInherit = doc->NodePrincipal();
8768 newURIPartitionedPrincipalToInherit = doc->PartitionedPrincipal();
8769 newCsp = doc->GetCsp();
8772 uint32_t locationChangeFlags = GetSameDocumentNavigationFlags(newURI);
8774 // Pass true for aCloneSHChildren, since we're not
8775 // changing documents here, so all of our subframes are
8776 // still relevant to the new session history entry.
8778 // It also makes OnNewURI(...) set LOCATION_CHANGE_SAME_DOCUMENT
8779 // flag on firing onLocationChange(...).
8780 // Anyway, aCloneSHChildren param is simply reflecting
8781 // doSameDocumentNavigation in this scope.
8783 // Note: we'll actually fire onLocationChange later, in order to preserve
8784 // ordering of HistoryCommit() in the parent vs onLocationChange (bug
8785 // 1668126)
8786 bool locationChangeNeeded = OnNewURI(
8787 newURI, nullptr, newURITriggeringPrincipal, newURIPrincipalToInherit,
8788 newURIPartitionedPrincipalToInherit, newCsp, true, true);
8790 nsCOMPtr<nsIInputStream> postData;
8791 nsCOMPtr<nsIReferrerInfo> referrerInfo;
8792 uint32_t cacheKey = 0;
8794 bool scrollRestorationIsManual = false;
8795 if (!mozilla::SessionHistoryInParent()) {
8796 if (mOSHE) {
8797 /* save current position of scroller(s) (bug 59774) */
8798 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
8799 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
8800 // Get the postdata, page ident and referrer info from the current page,
8801 // if the new load is being done via normal means. Note that "normal
8802 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
8803 // the loadType and allowScroll check above -- it filters out some
8804 // LOAD_CMD_NORMAL cases that we wouldn't want here.
8805 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8806 postData = mOSHE->GetPostData();
8807 cacheKey = mOSHE->GetCacheKey();
8808 referrerInfo = mOSHE->GetReferrerInfo();
8811 // Link our new SHEntry to the old SHEntry's back/forward
8812 // cache data, since the two SHEntries correspond to the
8813 // same document.
8814 if (mLSHE) {
8815 if (!aLoadState->LoadIsFromSessionHistory()) {
8816 // If we're not doing a history load, scroll restoration
8817 // should be inherited from the previous session history entry.
8818 SetScrollRestorationIsManualOnHistoryEntry(mLSHE,
8819 scrollRestorationIsManual);
8821 mLSHE->AdoptBFCacheEntry(mOSHE);
8824 } else {
8825 if (mActiveEntry) {
8826 mActiveEntry->SetScrollPosition(scrollPos.x, scrollPos.y);
8827 if (mBrowsingContext) {
8828 CollectWireframe();
8829 if (XRE_IsParentProcess()) {
8830 SessionHistoryEntry* entry =
8831 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
8832 if (entry) {
8833 entry->SetScrollPosition(scrollPos.x, scrollPos.y);
8835 } else {
8836 mozilla::Unused << ContentChild::GetSingleton()
8837 ->SendSessionHistoryEntryScrollPosition(
8838 mBrowsingContext, scrollPos.x,
8839 scrollPos.y);
8843 if (mLoadingEntry) {
8844 if (!mLoadingEntry->mLoadIsFromSessionHistory) {
8845 // If we're not doing a history load, scroll restoration
8846 // should be inherited from the previous session history entry.
8847 // XXX This needs most probably tweaks once fragment navigation is
8848 // fixed to work with session-history-in-parent.
8849 SetScrollRestorationIsManualOnHistoryEntry(nullptr,
8850 scrollRestorationIsManual);
8855 // If we're doing a history load, use its scroll restoration state.
8856 if (aLoadState->LoadIsFromSessionHistory()) {
8857 if (mozilla::SessionHistoryInParent()) {
8858 scrollRestorationIsManual = aLoadState->GetLoadingSessionHistoryInfo()
8859 ->mInfo.GetScrollRestorationIsManual();
8860 } else {
8861 scrollRestorationIsManual =
8862 aLoadState->SHEntry()->GetScrollRestorationIsManual();
8866 /* Assign mLSHE to mOSHE. This will either be a new entry created
8867 * by OnNewURI() for normal loads or aLoadState->SHEntry() for history
8868 * loads.
8870 if (!mozilla::SessionHistoryInParent()) {
8871 if (mLSHE) {
8872 SetHistoryEntryAndUpdateBC(Nothing(), Some<nsISHEntry*>(mLSHE));
8873 // Save the postData obtained from the previous page
8874 // in to the session history entry created for the
8875 // anchor page, so that any history load of the anchor
8876 // page will restore the appropriate postData.
8877 if (postData) {
8878 mOSHE->SetPostData(postData);
8881 // Make sure we won't just repost without hitting the
8882 // cache first
8883 if (cacheKey != 0) {
8884 mOSHE->SetCacheKey(cacheKey);
8887 // As the document has not changed, the referrer info hasn't changed too,
8888 // so we can just copy it over.
8889 if (referrerInfo) {
8890 mOSHE->SetReferrerInfo(referrerInfo);
8894 /* Set the title for the SH entry for this target url so that
8895 * SH menus in go/back/forward buttons won't be empty for this.
8896 * Note, this happens on mOSHE (and mActiveEntry in the future) because of
8897 * the code above.
8898 * Note, when session history lives in the parent process, this does not
8899 * update the title there.
8901 SetTitleOnHistoryEntry(false);
8902 } else {
8903 if (aLoadState->LoadIsFromSessionHistory()) {
8904 MOZ_LOG(
8905 gSHLog, LogLevel::Debug,
8906 ("Moving the loading entry to the active entry on nsDocShell %p to "
8907 "%s",
8908 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
8910 nsCOMPtr<nsILayoutHistoryState> currentLayoutHistoryState;
8911 if (mActiveEntry) {
8912 currentLayoutHistoryState = mActiveEntry->GetLayoutHistoryState();
8915 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
8916 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
8917 if (currentLayoutHistoryState) {
8918 // Restore the existing nsILayoutHistoryState object, since it is
8919 // possibly being used by the layout. When doing a new load, the
8920 // shared state is copied from the existing active entry, so this
8921 // special case is needed only with the history loads.
8922 mActiveEntry->SetLayoutHistoryState(currentLayoutHistoryState);
8925 if (cacheKey != 0) {
8926 mActiveEntry->SetCacheKey(cacheKey);
8928 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
8929 // does require a non-null uri if this is for a refresh load of the same
8930 // URI, but in that case mCurrentURI won't be null here.
8931 mBrowsingContext->SessionHistoryCommit(
8932 *mLoadingEntry, mLoadType, mCurrentURI, previousActiveEntry.get(),
8933 true, true,
8934 /* No expiration update on the same document loads*/
8935 false, cacheKey);
8936 // FIXME Need to set postdata.
8938 // Set the title for the SH entry for this target url so that
8939 // SH menus in go/back/forward buttons won't be empty for this.
8940 // Note, when session history lives in the parent process, this does not
8941 // update the title there.
8942 SetTitleOnHistoryEntry(false);
8943 } else {
8944 Maybe<bool> scrollRestorationIsManual;
8945 if (mActiveEntry) {
8946 scrollRestorationIsManual.emplace(
8947 mActiveEntry->GetScrollRestorationIsManual());
8949 // Get the postdata, page ident and referrer info from the current page,
8950 // if the new load is being done via normal means. Note that "normal
8951 // means" can be checked for just by checking for LOAD_CMD_NORMAL, given
8952 // the loadType and allowScroll check above -- it filters out some
8953 // LOAD_CMD_NORMAL cases that we wouldn't want here.
8954 if (aLoadState->LoadType() & LOAD_CMD_NORMAL) {
8955 postData = mActiveEntry->GetPostData();
8956 cacheKey = mActiveEntry->GetCacheKey();
8957 referrerInfo = mActiveEntry->GetReferrerInfo();
8961 MOZ_LOG(gSHLog, LogLevel::Debug,
8962 ("Creating an active entry on nsDocShell %p to %s", this,
8963 newURI->GetSpecOrDefault().get()));
8964 if (mActiveEntry) {
8965 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, newURI);
8966 } else {
8967 mActiveEntry = MakeUnique<SessionHistoryInfo>(
8968 newURI, newURITriggeringPrincipal, newURIPrincipalToInherit,
8969 newURIPartitionedPrincipalToInherit, newCsp, mContentTypeHint);
8972 // Save the postData obtained from the previous page in to the session
8973 // history entry created for the anchor page, so that any history load of
8974 // the anchor page will restore the appropriate postData.
8975 if (postData) {
8976 mActiveEntry->SetPostData(postData);
8979 // Make sure we won't just repost without hitting the
8980 // cache first
8981 if (cacheKey != 0) {
8982 mActiveEntry->SetCacheKey(cacheKey);
8985 // As the document has not changed, the referrer info hasn't changed too,
8986 // so we can just copy it over.
8987 if (referrerInfo) {
8988 mActiveEntry->SetReferrerInfo(referrerInfo);
8991 // Set the title for the SH entry for this target url so that
8992 // SH menus in go/back/forward buttons won't be empty for this.
8993 mActiveEntry->SetTitle(mTitle);
8995 if (scrollRestorationIsManual.isSome()) {
8996 mActiveEntry->SetScrollRestorationIsManual(
8997 scrollRestorationIsManual.value());
9000 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
9001 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
9002 } else {
9003 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
9004 // FIXME We should probably just compute mChildOffset in the parent
9005 // instead of passing it over IPC here.
9006 mBrowsingContext->SetActiveSessionHistoryEntry(
9007 Some(scrollPos), mActiveEntry.get(), mLoadType, cacheKey);
9008 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
9013 if (locationChangeNeeded) {
9014 FireOnLocationChange(this, nullptr, newURI, locationChangeFlags);
9017 /* Restore the original LSHE if we were loading something
9018 * while same document navigation was initiated.
9020 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(oldLSHE), Nothing());
9021 mLoadingEntry.swap(oldLoadingEntry);
9023 /* Set the title for the Global History entry for this anchor url.
9025 UpdateGlobalHistoryTitle(newURI);
9027 SetDocCurrentStateObj(mOSHE, mActiveEntry.get());
9029 // Inform the favicon service that the favicon for oldURI also
9030 // applies to newURI.
9031 CopyFavicon(currentURI, newURI, UsePrivateBrowsing());
9033 RefPtr<nsGlobalWindowOuter> scriptGlobal = mScriptGlobal;
9034 nsCOMPtr<nsPIDOMWindowInner> win =
9035 scriptGlobal ? scriptGlobal->GetCurrentInnerWindow() : nullptr;
9037 // ScrollToAnchor doesn't necessarily cause us to scroll the window;
9038 // the function decides whether a scroll is appropriate based on the
9039 // arguments it receives. But even if we don't end up scrolling,
9040 // ScrollToAnchor performs other important tasks, such as informing
9041 // the presShell that we have a new hash. See bug 680257.
9042 nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
9043 aState.mNewHash, aLoadState->LoadType());
9044 NS_ENSURE_SUCCESS(rv, rv);
9046 /* restore previous position of scroller(s), if we're moving
9047 * back in history (bug 59774)
9049 nscoord bx = 0;
9050 nscoord by = 0;
9051 bool needsScrollPosUpdate = false;
9052 if ((mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
9053 (aLoadState->LoadType() == LOAD_HISTORY ||
9054 aLoadState->LoadType() == LOAD_RELOAD_NORMAL) &&
9055 !scrollRestorationIsManual) {
9056 needsScrollPosUpdate = true;
9057 if (mozilla::SessionHistoryInParent()) {
9058 mActiveEntry->GetScrollPosition(&bx, &by);
9059 } else {
9060 mOSHE->GetScrollPosition(&bx, &by);
9064 // Dispatch the popstate and hashchange events, as appropriate.
9066 // The event dispatch below can cause us to re-enter script and
9067 // destroy the docshell, nulling out mScriptGlobal. Hold a stack
9068 // reference to avoid null derefs. See bug 914521.
9069 if (win) {
9070 // Fire a hashchange event URIs differ, and only in their hashes.
9071 bool doHashchange = aState.mSameExceptHashes &&
9072 (aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
9073 !aState.mCurrentHash.Equals(aState.mNewHash));
9075 if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
9076 win->DispatchSyncPopState();
9079 if (needsScrollPosUpdate && win->HasActiveDocument()) {
9080 SetCurScrollPosEx(bx, by);
9083 if (doHashchange) {
9084 // Note that currentURI hasn't changed because it's on the
9085 // stack, so we can just use it directly as the old URI.
9086 win->DispatchAsyncHashchange(currentURI, newURI);
9090 return NS_OK;
9093 static bool NavigationShouldTakeFocus(nsDocShell* aDocShell,
9094 nsDocShellLoadState* aLoadState) {
9095 if (!aLoadState->AllowFocusMove()) {
9096 return false;
9098 if (!aLoadState->HasValidUserGestureActivation()) {
9099 return false;
9101 const auto& sourceBC = aLoadState->SourceBrowsingContext();
9102 if (!sourceBC || !sourceBC->IsActive()) {
9103 // If the navigation didn't come from a foreground tab, then we don't steal
9104 // focus.
9105 return false;
9107 auto* bc = aDocShell->GetBrowsingContext();
9108 if (sourceBC.get() == bc) {
9109 // If it comes from the same tab / frame, don't steal focus either.
9110 return false;
9112 auto* fm = nsFocusManager::GetFocusManager();
9113 if (fm && bc->IsActive() && fm->IsInActiveWindow(bc)) {
9114 // If we're already on the foreground tab of the foreground window, then we
9115 // don't need to do this. This helps to e.g. not steal focus from the
9116 // browser chrome unnecessarily.
9117 return false;
9119 if (auto* doc = aDocShell->GetExtantDocument()) {
9120 if (doc->IsInitialDocument()) {
9121 // If we're the initial load for the browsing context, the browser
9122 // chrome determines what to focus. This is important because the
9123 // browser chrome may want to e.g focus the url-bar
9124 return false;
9127 // Take loadDivertedInBackground into account so the behavior would be the
9128 // same as how the tab first opened.
9129 return !Preferences::GetBool("browser.tabs.loadDivertedInBackground", false);
9132 uint32_t nsDocShell::GetLoadTypeForFormSubmission(
9133 BrowsingContext* aTargetBC, nsDocShellLoadState* aLoadState) {
9134 MOZ_ASSERT(aLoadState->IsFormSubmission());
9136 // https://html.spec.whatwg.org/#form-submission-algorithm
9137 // 22. Let historyHandling be "push".
9138 // 23. If form document equals targetNavigable's active document, and
9139 // form document has not yet completely loaded, then set
9140 // historyHandling to "replace".
9141 return GetBrowsingContext() == aTargetBC && !mEODForCurrentDocument
9142 ? LOAD_NORMAL_REPLACE
9143 : LOAD_LINK;
9146 nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
9147 Maybe<uint32_t> aCacheKey) {
9148 MOZ_ASSERT(aLoadState, "need a load state!");
9149 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
9150 "need a valid TriggeringPrincipal");
9152 if (!aLoadState->TriggeringPrincipal()) {
9153 MOZ_ASSERT(false, "InternalLoad needs a valid triggeringPrincipal");
9154 return NS_ERROR_FAILURE;
9156 if (NS_WARN_IF(mBrowsingContext->GetPendingInitialization())) {
9157 return NS_ERROR_NOT_AVAILABLE;
9160 const bool shouldTakeFocus = NavigationShouldTakeFocus(this, aLoadState);
9162 mOriginalUriString.Truncate();
9164 MOZ_LOG(gDocShellLeakLog, LogLevel::Debug,
9165 ("DOCSHELL %p InternalLoad %s\n", this,
9166 aLoadState->URI()->GetSpecOrDefault().get()));
9168 NS_ENSURE_TRUE(IsValidLoadType(aLoadState->LoadType()), NS_ERROR_INVALID_ARG);
9170 // Cancel loads coming from Docshells that are being destroyed.
9171 if (mIsBeingDestroyed) {
9172 return NS_ERROR_NOT_AVAILABLE;
9175 nsresult rv = EnsureScriptEnvironment();
9176 if (NS_FAILED(rv)) {
9177 return rv;
9180 // If we have a target to move to, do that now.
9181 if (!aLoadState->Target().IsEmpty()) {
9182 return PerformRetargeting(aLoadState);
9185 // This is the non-retargeting load path, we've already set the right loadtype
9186 // for form submissions in nsDocShell::OnLinkClickSync.
9187 if (aLoadState->TargetBrowsingContext().IsNull()) {
9188 aLoadState->SetTargetBrowsingContext(GetBrowsingContext());
9191 MOZ_DIAGNOSTIC_ASSERT(
9192 aLoadState->TargetBrowsingContext() == GetBrowsingContext(),
9193 "Load must be targeting this BrowsingContext");
9195 MOZ_TRY(CheckDisallowedJavascriptLoad(aLoadState));
9197 // If we don't have a target, we're loading into ourselves, and our load
9198 // delegate may want to intercept that load.
9199 SameDocumentNavigationState sameDocumentNavigationState;
9200 bool sameDocument =
9201 IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState) &&
9202 !aLoadState->GetPendingRedirectedChannel();
9204 // Note: We do this check both here and in BrowsingContext::
9205 // LoadURI/InternalLoad, since document-specific sandbox flags are only
9206 // available in the process triggering the load, and we don't want the target
9207 // process to have to trust the triggering process to do the appropriate
9208 // checks for the BrowsingContext's sandbox flags.
9209 MOZ_TRY(mBrowsingContext->CheckSandboxFlags(aLoadState));
9211 NS_ENSURE_STATE(!HasUnloadedParent());
9213 rv = CheckLoadingPermissions();
9214 if (NS_FAILED(rv)) {
9215 return rv;
9218 if (mFiredUnloadEvent) {
9219 if (IsOKToLoadURI(aLoadState->URI())) {
9220 MOZ_ASSERT(aLoadState->Target().IsEmpty(),
9221 "Shouldn't have a window target here!");
9223 // If this is a replace load, make whatever load triggered
9224 // the unload event also a replace load, so we don't
9225 // create extra history entries.
9226 if (LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
9227 LOAD_FLAGS_REPLACE_HISTORY)) {
9228 mLoadType = LOAD_NORMAL_REPLACE;
9231 // Do this asynchronously
9232 nsCOMPtr<nsIRunnable> ev = new InternalLoadEvent(this, aLoadState);
9233 return Dispatch(ev.forget());
9236 // Just ignore this load attempt
9237 return NS_OK;
9240 // If we are loading a URI that should inherit a security context (basically
9241 // javascript: at this point), and the caller has said that principal
9242 // inheritance is allowed, there are a few possible cases:
9244 // 1) We are provided with the principal to inherit. In that case, we just use
9245 // it.
9247 // 2) The load is coming from some other application. In this case we don't
9248 // want to inherit from whatever document we have loaded now, since the
9249 // load is unrelated to it.
9251 // 3) It's a load from our application, but does not provide an explicit
9252 // principal to inherit. In that case, we want to inherit the principal of
9253 // our current document, or of our parent document (if any) if we don't
9254 // have a current document.
9256 bool inherits;
9258 if (!aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL) &&
9259 !aLoadState->PrincipalToInherit() &&
9260 (aLoadState->HasInternalLoadFlags(
9261 INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL)) &&
9262 NS_SUCCEEDED(nsContentUtils::URIInheritsSecurityContext(
9263 aLoadState->URI(), &inherits)) &&
9264 inherits) {
9265 aLoadState->SetPrincipalToInherit(GetInheritedPrincipal(true));
9267 // If principalToInherit is still null (e.g. if some of the conditions of
9268 // were not satisfied), then no inheritance of any sort will happen: the
9269 // load will just get a principal based on the URI being loaded.
9272 // If this docshell is owned by a frameloader, make sure to cancel
9273 // possible frameloader initialization before loading a new page.
9274 nsCOMPtr<nsIDocShellTreeItem> parent = GetInProcessParentDocshell();
9275 if (parent) {
9276 RefPtr<Document> doc = parent->GetDocument();
9277 if (doc) {
9278 doc->TryCancelFrameLoaderInitialization(this);
9282 // Before going any further vet loads initiated by external programs.
9283 if (aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
9284 MOZ_DIAGNOSTIC_ASSERT(aLoadState->LoadType() == LOAD_NORMAL);
9286 // Disallow external chrome: loads targetted at content windows
9287 if (SchemeIsChrome(aLoadState->URI())) {
9288 NS_WARNING("blocked external chrome: url -- use '--chrome' option");
9289 return NS_ERROR_FAILURE;
9292 // clear the decks to prevent context bleed-through (bug 298255)
9293 rv = CreateAboutBlankDocumentViewer(nullptr, nullptr, nullptr, nullptr,
9294 /* aIsInitialDocument */ false);
9295 if (NS_FAILED(rv)) {
9296 return NS_ERROR_FAILURE;
9300 mAllowKeywordFixup = aLoadState->HasInternalLoadFlags(
9301 INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP);
9302 mURIResultedInDocument = false; // reset the clock...
9304 // See if this is actually a load between two history entries for the same
9305 // document. If the process fails, or if we successfully navigate within the
9306 // same document, return.
9307 if (sameDocument) {
9308 nsresult rv = HandleSameDocumentNavigation(
9309 aLoadState, sameDocumentNavigationState, sameDocument);
9310 NS_ENSURE_SUCCESS(rv, rv);
9311 if (shouldTakeFocus) {
9312 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9314 if (sameDocument) {
9315 return rv;
9319 // mDocumentViewer->PermitUnload can destroy |this| docShell, which
9320 // causes the next call of CanSavePresentation to crash.
9321 // Hold onto |this| until we return, to prevent a crash from happening.
9322 // (bug#331040)
9323 nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
9325 // Don't init timing for javascript:, since it generally doesn't
9326 // actually start a load or anything. If it does, we'll init
9327 // timing then, from OnStateChange.
9329 // XXXbz mTiming should know what channel it's for, so we don't
9330 // need this hackery.
9331 bool toBeReset = false;
9332 bool isJavaScript = SchemeIsJavascript(aLoadState->URI());
9334 if (!isJavaScript) {
9335 toBeReset = MaybeInitTiming();
9337 bool isNotDownload = aLoadState->FileName().IsVoid();
9338 if (mTiming && isNotDownload) {
9339 mTiming->NotifyBeforeUnload();
9341 // Check if the page doesn't want to be unloaded. The javascript:
9342 // protocol handler deals with this for javascript: URLs.
9343 if (!isJavaScript && isNotDownload &&
9344 !aLoadState->NotifiedBeforeUnloadListeners() && mDocumentViewer) {
9345 bool okToUnload;
9347 // Check if request is exempted from HTTPSOnlyMode and if https-first is
9348 // enabled, if so it means:
9349 // * https-first failed to upgrade request to https
9350 // * we already asked for permission to unload and the user accepted
9351 // otherwise we wouldn't be here.
9352 bool isPrivateWin = GetOriginAttributes().IsPrivateBrowsing();
9353 bool isHistoryOrReload = false;
9354 uint32_t loadType = aLoadState->LoadType();
9356 // Check if request is a reload.
9357 if (loadType == LOAD_RELOAD_NORMAL ||
9358 loadType == LOAD_RELOAD_BYPASS_CACHE ||
9359 loadType == LOAD_RELOAD_BYPASS_PROXY ||
9360 loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
9361 loadType == LOAD_HISTORY) {
9362 isHistoryOrReload = true;
9365 // If it isn't a reload, the request already failed to be upgraded and
9366 // https-first is enabled then don't ask the user again for permission to
9367 // unload and just unload.
9368 if (!isHistoryOrReload && aLoadState->IsExemptFromHTTPSFirstMode() &&
9369 nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin)) {
9370 rv = mDocumentViewer->PermitUnload(
9371 nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload,
9372 &okToUnload);
9373 } else {
9374 rv = mDocumentViewer->PermitUnload(&okToUnload);
9377 if (NS_SUCCEEDED(rv) && !okToUnload) {
9378 // The user chose not to unload the page, interrupt the
9379 // load.
9380 MaybeResetInitTiming(toBeReset);
9381 return NS_OK;
9385 if (mTiming && isNotDownload) {
9386 mTiming->NotifyUnloadAccepted(mCurrentURI);
9389 // In e10s, in the parent process, we refuse to load anything other than
9390 // "safe" resources that we ship or trust enough to give "special" URLs.
9391 // Similar check will be performed by the ParentProcessDocumentChannel if in
9392 // use.
9393 if (XRE_IsE10sParentProcess() &&
9394 !DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
9395 !CanLoadInParentProcess(aLoadState->URI())) {
9396 return NS_ERROR_FAILURE;
9399 // Whenever a top-level browsing context is navigated, the user agent MUST
9400 // lock the orientation of the document to the document's default
9401 // orientation. We don't explicitly check for a top-level browsing context
9402 // here because orientation is only set on top-level browsing contexts.
9403 if (mBrowsingContext->GetOrientationLock() != hal::ScreenOrientation::None) {
9404 MOZ_ASSERT(mBrowsingContext->IsTop());
9405 MOZ_ALWAYS_SUCCEEDS(
9406 mBrowsingContext->SetOrientationLock(hal::ScreenOrientation::None));
9407 if (mBrowsingContext->IsActive()) {
9408 ScreenOrientation::UpdateActiveOrientationLock(
9409 hal::ScreenOrientation::None);
9413 // Check for saving the presentation here, before calling Stop().
9414 // This is necessary so that we can catch any pending requests.
9415 // Since the new request has not been created yet, we pass null for the
9416 // new request parameter.
9417 // Also pass nullptr for the document, since it doesn't affect the return
9418 // value for our purposes here.
9419 bool savePresentation =
9420 CanSavePresentation(aLoadState->LoadType(), nullptr, nullptr,
9421 /* aReportBFCacheComboTelemetry */ true);
9423 // nsDocShell::CanSavePresentation is for non-SHIP version only. Do a
9424 // separate check for SHIP so that we know if there are ongoing requests
9425 // before calling Stop() below.
9426 if (mozilla::SessionHistoryInParent()) {
9427 Document* document = GetDocument();
9428 uint32_t flags = 0;
9429 if (document && !document->CanSavePresentation(nullptr, flags, true)) {
9430 // This forces some flags into the WindowGlobalParent's mBFCacheStatus,
9431 // which we'll then use in CanonicalBrowsingContext::AllowedInBFCache,
9432 // and in particular we'll store BFCacheStatus::REQUEST if needed.
9433 // Also, we want to report all the flags to the parent process here (and
9434 // not just BFCacheStatus::NOT_ALLOWED), so that it can update the
9435 // telemetry data correctly.
9436 document->DisallowBFCaching(flags);
9440 // Don't stop current network activity for javascript: URL's since
9441 // they might not result in any data, and thus nothing should be
9442 // stopped in those cases. In the case where they do result in
9443 // data, the javascript: URL channel takes care of stopping
9444 // current network activity.
9445 if (!isJavaScript && isNotDownload) {
9446 // Stop any current network activity.
9447 // Also stop content if this is a zombie doc. otherwise
9448 // the onload will be delayed by other loads initiated in the
9449 // background by the first document that
9450 // didn't fully load before the next load was initiated.
9451 // If not a zombie, don't stop content until data
9452 // starts arriving from the new URI...
9454 if ((mDocumentViewer && mDocumentViewer->GetPreviousViewer()) ||
9455 LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(), LOAD_FLAGS_STOP_CONTENT)) {
9456 rv = Stop(nsIWebNavigation::STOP_ALL);
9457 } else {
9458 rv = Stop(nsIWebNavigation::STOP_NETWORK);
9461 if (NS_FAILED(rv)) {
9462 return rv;
9466 mLoadType = aLoadState->LoadType();
9468 // aLoadState->SHEntry() should be assigned to mLSHE, only after Stop() has
9469 // been called. But when loading an error page, do not clear the
9470 // mLSHE for the real page.
9471 if (mLoadType != LOAD_ERROR_PAGE) {
9472 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(aLoadState->SHEntry()),
9473 Nothing());
9474 if (aLoadState->LoadIsFromSessionHistory() &&
9475 !mozilla::SessionHistoryInParent()) {
9476 // We're making history navigation or a reload. Make sure our history ID
9477 // points to the same ID as SHEntry's docshell ID.
9478 nsID historyID = {};
9479 aLoadState->SHEntry()->GetDocshellID(historyID);
9481 Unused << mBrowsingContext->SetHistoryID(historyID);
9485 mSavingOldViewer = savePresentation;
9487 // If we have a saved content viewer in history, restore and show it now.
9488 if (aLoadState->LoadIsFromSessionHistory() &&
9489 (mLoadType & LOAD_CMD_HISTORY)) {
9490 // https://html.spec.whatwg.org/#history-traversal:
9491 // To traverse the history
9492 // "If entry has a different Document object than the current entry, then
9493 // run the following substeps: Remove any tasks queued by the history
9494 // traversal task source..."
9495 // Same document object case was handled already above with
9496 // HandleSameDocumentNavigation call.
9497 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
9498 if (shistory) {
9499 shistory->RemovePendingHistoryNavigations();
9501 if (!mozilla::SessionHistoryInParent()) {
9502 // It's possible that the previous viewer of mDocumentViewer is the
9503 // viewer that will end up in aLoadState->SHEntry() when it gets closed.
9504 // If that's the case, we need to go ahead and force it into its shentry
9505 // so we can restore it.
9506 if (mDocumentViewer) {
9507 nsCOMPtr<nsIDocumentViewer> prevViewer =
9508 mDocumentViewer->GetPreviousViewer();
9509 if (prevViewer) {
9510 #ifdef DEBUG
9511 nsCOMPtr<nsIDocumentViewer> prevPrevViewer =
9512 prevViewer->GetPreviousViewer();
9513 NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
9514 #endif
9515 nsCOMPtr<nsISHEntry> viewerEntry;
9516 prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
9517 if (viewerEntry == aLoadState->SHEntry()) {
9518 // Make sure this viewer ends up in the right place
9519 mDocumentViewer->SetPreviousViewer(nullptr);
9520 prevViewer->Destroy();
9524 nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
9525 bool restoring;
9526 rv = RestorePresentation(aLoadState->SHEntry(), &restoring);
9527 if (restoring) {
9528 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, true);
9529 return rv;
9531 Telemetry::Accumulate(Telemetry::BFCACHE_PAGE_RESTORED, false);
9533 // We failed to restore the presentation, so clean up.
9534 // Both the old and new history entries could potentially be in
9535 // an inconsistent state.
9536 if (NS_FAILED(rv)) {
9537 if (oldEntry) {
9538 oldEntry->SyncPresentationState();
9541 aLoadState->SHEntry()->SyncPresentationState();
9546 bool isTopLevelDoc = mBrowsingContext->IsTopContent();
9548 OriginAttributes attrs = GetOriginAttributes();
9549 attrs.SetFirstPartyDomain(isTopLevelDoc, aLoadState->URI());
9551 PredictorLearn(aLoadState->URI(), nullptr,
9552 nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
9553 PredictorPredict(aLoadState->URI(), nullptr,
9554 nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
9556 nsCOMPtr<nsIRequest> req;
9557 rv = DoURILoad(aLoadState, aCacheKey, getter_AddRefs(req));
9559 if (NS_SUCCEEDED(rv)) {
9560 if (shouldTakeFocus) {
9561 mBrowsingContext->Focus(CallerType::System, IgnoreErrors());
9565 if (NS_FAILED(rv)) {
9566 nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
9567 UnblockEmbedderLoadEventForFailure();
9568 nsCOMPtr<nsIURI> uri = aLoadState->URI();
9569 if (DisplayLoadError(rv, uri, nullptr, chan) &&
9570 // FIXME: At this point code was using internal load flags, but checking
9571 // non-internal load flags?
9572 aLoadState->HasLoadFlags(LOAD_FLAGS_ERROR_LOAD_CHANGES_RV)) {
9573 return NS_ERROR_LOAD_SHOWED_ERRORPAGE;
9576 // We won't report any error if this is an unknown protocol error. The
9577 // reason behind this is that it will allow enumeration of external
9578 // protocols if we report an error for each unknown protocol.
9579 if (NS_ERROR_UNKNOWN_PROTOCOL == rv) {
9580 return NS_OK;
9584 return rv;
9587 /* static */
9588 bool nsDocShell::CanLoadInParentProcess(nsIURI* aURI) {
9589 nsCOMPtr<nsIURI> uri = aURI;
9590 // In e10s, in the parent process, we refuse to load anything other than
9591 // "safe" resources that we ship or trust enough to give "special" URLs.
9592 bool canLoadInParent = false;
9593 if (NS_SUCCEEDED(NS_URIChainHasFlags(
9594 uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, &canLoadInParent)) &&
9595 canLoadInParent) {
9596 // We allow UI resources.
9597 return true;
9599 // For about: and extension-based URIs, which don't get
9600 // URI_IS_UI_RESOURCE, first remove layers of view-source:, if present.
9601 while (uri && uri->SchemeIs("view-source")) {
9602 nsCOMPtr<nsINestedURI> nested = do_QueryInterface(uri);
9603 if (nested) {
9604 nested->GetInnerURI(getter_AddRefs(uri));
9605 } else {
9606 break;
9609 // Allow about: URIs, and allow moz-extension ones if we're running
9610 // extension content in the parent process.
9611 if (!uri || uri->SchemeIs("about") ||
9612 (!StaticPrefs::extensions_webextensions_remote() &&
9613 uri->SchemeIs("moz-extension"))) {
9614 return true;
9616 #ifdef MOZ_THUNDERBIRD
9617 if (uri->SchemeIs("imap") || uri->SchemeIs("mailbox") ||
9618 uri->SchemeIs("news") || uri->SchemeIs("nntp") ||
9619 uri->SchemeIs("snews") || uri->SchemeIs("x-moz-ews")) {
9620 return true;
9622 #endif
9623 nsAutoCString scheme;
9624 uri->GetScheme(scheme);
9625 // Allow ext+foo URIs (extension-registered custom protocols). See
9626 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/protocol_handlers
9627 if (StringBeginsWith(scheme, "ext+"_ns) &&
9628 !StaticPrefs::extensions_webextensions_remote()) {
9629 return true;
9631 // Final exception for some legacy automated tests:
9632 if (xpc::IsInAutomation() &&
9633 StaticPrefs::security_allow_unsafe_parent_loads()) {
9634 return true;
9636 return false;
9639 nsIPrincipal* nsDocShell::GetInheritedPrincipal(
9640 bool aConsiderCurrentDocument, bool aConsiderPartitionedPrincipal) {
9641 RefPtr<Document> document;
9642 bool inheritedFromCurrent = false;
9644 if (aConsiderCurrentDocument && mDocumentViewer) {
9645 document = mDocumentViewer->GetDocument();
9646 inheritedFromCurrent = true;
9649 if (!document) {
9650 nsCOMPtr<nsIDocShellTreeItem> parentItem;
9651 GetInProcessSameTypeParent(getter_AddRefs(parentItem));
9652 if (parentItem) {
9653 document = parentItem->GetDocument();
9657 if (!document) {
9658 if (!aConsiderCurrentDocument) {
9659 return nullptr;
9662 // Make sure we end up with _something_ as the principal no matter
9663 // what.If this fails, we'll just get a null docViewer and bail.
9664 EnsureDocumentViewer();
9665 if (!mDocumentViewer) {
9666 return nullptr;
9668 document = mDocumentViewer->GetDocument();
9671 //-- Get the document's principal
9672 if (document) {
9673 nsIPrincipal* docPrincipal = aConsiderPartitionedPrincipal
9674 ? document->PartitionedPrincipal()
9675 : document->NodePrincipal();
9677 // Don't allow loads in typeContent docShells to inherit the system
9678 // principal from existing documents.
9679 if (inheritedFromCurrent && mItemType == typeContent &&
9680 docPrincipal->IsSystemPrincipal()) {
9681 return nullptr;
9684 return docPrincipal;
9687 return nullptr;
9690 /* static */ nsresult nsDocShell::CreateRealChannelForDocument(
9691 nsIChannel** aChannel, nsIURI* aURI, nsILoadInfo* aLoadInfo,
9692 nsIInterfaceRequestor* aCallbacks, nsLoadFlags aLoadFlags,
9693 const nsAString& aSrcdoc, nsIURI* aBaseURI) {
9694 nsCOMPtr<nsIChannel> channel;
9695 if (aSrcdoc.IsVoid()) {
9696 MOZ_TRY(NS_NewChannelInternal(getter_AddRefs(channel), aURI, aLoadInfo,
9697 nullptr, // PerformanceStorage
9698 nullptr, // loadGroup
9699 aCallbacks, aLoadFlags));
9701 if (aBaseURI) {
9702 nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
9703 if (vsc) {
9704 MOZ_ALWAYS_SUCCEEDS(vsc->SetBaseURI(aBaseURI));
9707 } else if (SchemeIsViewSource(aURI)) {
9708 // Instantiate view source handler protocol, if it doesn't exist already.
9709 nsCOMPtr<nsIIOService> io(do_GetIOService());
9710 MOZ_ASSERT(io);
9711 nsCOMPtr<nsIProtocolHandler> handler;
9712 nsresult rv =
9713 io->GetProtocolHandler("view-source", getter_AddRefs(handler));
9714 if (NS_FAILED(rv)) {
9715 return rv;
9718 nsViewSourceHandler* vsh = nsViewSourceHandler::GetInstance();
9719 if (!vsh) {
9720 return NS_ERROR_FAILURE;
9723 MOZ_TRY(vsh->NewSrcdocChannel(aURI, aBaseURI, aSrcdoc, aLoadInfo,
9724 getter_AddRefs(channel)));
9725 } else {
9726 MOZ_TRY(NS_NewInputStreamChannelInternal(getter_AddRefs(channel), aURI,
9727 aSrcdoc, "text/html"_ns, aLoadInfo,
9728 true));
9729 nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
9730 MOZ_ASSERT(isc);
9731 isc->SetBaseURI(aBaseURI);
9734 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
9735 nsresult rv = channel->SetLoadFlags(aLoadFlags);
9736 NS_ENSURE_SUCCESS(rv, rv);
9739 channel.forget(aChannel);
9740 return NS_OK;
9743 /* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
9744 BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
9745 LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
9746 nsDocShell* aDocShell, const OriginAttributes& aOriginAttributes,
9747 nsLoadFlags aLoadFlags, uint32_t aCacheKey, nsresult& aRv,
9748 nsIChannel** aChannel) {
9749 MOZ_ASSERT(aLoadInfo);
9751 nsString srcdoc = VoidString();
9752 bool isSrcdoc =
9753 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
9754 if (isSrcdoc) {
9755 srcdoc = aLoadState->SrcdocData();
9758 aLoadInfo->SetTriggeringRemoteType(
9759 aLoadState->GetEffectiveTriggeringRemoteType());
9761 if (aLoadState->PrincipalToInherit()) {
9762 aLoadInfo->SetPrincipalToInherit(aLoadState->PrincipalToInherit());
9764 aLoadInfo->SetLoadTriggeredFromExternal(
9765 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL));
9766 aLoadInfo->SetForceAllowDataURI(aLoadState->HasInternalLoadFlags(
9767 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_DATA_URI));
9768 aLoadInfo->SetOriginalFrameSrcLoad(
9769 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_ORIGINAL_FRAME_SRC));
9771 bool inheritAttrs = false;
9772 if (aLoadState->PrincipalToInherit()) {
9773 inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
9774 aLoadState->PrincipalToInherit(), aLoadState->URI(),
9775 true, // aInheritForAboutBlank
9776 isSrcdoc);
9779 // Strip the target query parameters before creating the channel.
9780 aLoadState->MaybeStripTrackerQueryStrings(aBrowsingContext);
9782 OriginAttributes attrs;
9784 // Inherit origin attributes from PrincipalToInherit if inheritAttrs is
9785 // true. Otherwise we just use the origin attributes from docshell.
9786 if (inheritAttrs) {
9787 MOZ_ASSERT(aLoadState->PrincipalToInherit(),
9788 "We should have PrincipalToInherit here.");
9789 attrs = aLoadState->PrincipalToInherit()->OriginAttributesRef();
9790 // If firstPartyIsolation is not enabled, then PrincipalToInherit should
9791 // have the same origin attributes with docshell.
9792 MOZ_ASSERT_IF(!OriginAttributes::IsFirstPartyEnabled(),
9793 attrs == aOriginAttributes);
9794 } else {
9795 attrs = aOriginAttributes;
9796 attrs.SetFirstPartyDomain(IsTopLevelDoc(aBrowsingContext, aLoadInfo),
9797 aLoadState->URI());
9800 aRv = aLoadInfo->SetOriginAttributes(attrs);
9801 if (NS_WARN_IF(NS_FAILED(aRv))) {
9802 return false;
9805 if (aLoadState->GetIsFromProcessingFrameAttributes()) {
9806 aLoadInfo->SetIsFromProcessingFrameAttributes();
9809 // Propagate the IsFormSubmission flag to the loadInfo.
9810 if (aLoadState->IsFormSubmission()) {
9811 aLoadInfo->SetIsFormSubmission(true);
9814 aLoadInfo->SetUnstrippedURI(aLoadState->GetUnstrippedURI());
9816 nsCOMPtr<nsIChannel> channel;
9817 aRv = CreateRealChannelForDocument(getter_AddRefs(channel), aLoadState->URI(),
9818 aLoadInfo, aCallbacks, aLoadFlags, srcdoc,
9819 aLoadState->BaseURI());
9820 NS_ENSURE_SUCCESS(aRv, false);
9822 if (!channel) {
9823 return false;
9826 // If the HTTPS-Only mode is enabled, every insecure request gets upgraded to
9827 // HTTPS by default. This behavior can be disabled through the loadinfo flag
9828 // HTTPS_ONLY_EXEMPT.
9829 nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(channel);
9831 // hack
9832 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
9833 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
9834 do_QueryInterface(channel));
9835 nsCOMPtr<nsIURI> referrer;
9836 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9837 if (referrerInfo) {
9838 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9840 if (httpChannelInternal) {
9841 if (aLoadState->HasInternalLoadFlags(
9842 INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES)) {
9843 aRv = httpChannelInternal->SetThirdPartyFlags(
9844 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
9845 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9847 if (aLoadState->FirstParty()) {
9848 aRv = httpChannelInternal->SetDocumentURI(aLoadState->URI());
9849 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9850 } else {
9851 aRv = httpChannelInternal->SetDocumentURI(referrer);
9852 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9854 aRv = httpChannelInternal->SetRedirectMode(
9855 nsIHttpChannelInternal::REDIRECT_MODE_MANUAL);
9856 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9859 if (httpChannel) {
9860 if (aLoadState->HeadersStream()) {
9861 aRv = AddHeadersToChannel(aLoadState->HeadersStream(), httpChannel);
9863 // Set the referrer explicitly
9864 // Referrer is currenly only set for link clicks here.
9865 if (referrerInfo) {
9866 aRv = httpChannel->SetReferrerInfo(referrerInfo);
9867 MOZ_ASSERT(NS_SUCCEEDED(aRv));
9870 // Mark the http channel as UrgentStart for top level document loading in
9871 // active tab.
9872 if (IsUrgentStart(aBrowsingContext, aLoadInfo, aLoadState->LoadType())) {
9873 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
9874 if (cos) {
9875 cos->AddClassFlags(nsIClassOfService::UrgentStart);
9876 if (StaticPrefs::dom_document_priority_incremental()) {
9877 cos->SetIncremental(true);
9883 channel->SetOriginalURI(aLoadState->OriginalURI() ? aLoadState->OriginalURI()
9884 : aLoadState->URI());
9886 const nsACString& typeHint = aLoadState->TypeHint();
9887 if (!typeHint.IsVoid()) {
9888 channel->SetContentType(typeHint);
9891 const nsAString& fileName = aLoadState->FileName();
9892 if (!fileName.IsVoid()) {
9893 aRv = channel->SetContentDisposition(nsIChannel::DISPOSITION_ATTACHMENT);
9894 NS_ENSURE_SUCCESS(aRv, false);
9895 if (!fileName.IsEmpty()) {
9896 aRv = channel->SetContentDispositionFilename(fileName);
9897 NS_ENSURE_SUCCESS(aRv, false);
9901 if (nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(channel)) {
9902 nsCOMPtr<nsIURI> referrer;
9903 nsIReferrerInfo* referrerInfo = aLoadState->GetReferrerInfo();
9904 if (referrerInfo) {
9905 referrerInfo->GetOriginalReferrer(getter_AddRefs(referrer));
9907 // save true referrer for those who need it (e.g. xpinstall whitelisting)
9908 // Currently only http and ftp channels support this.
9909 props->SetPropertyAsInterface(u"docshell.internalReferrer"_ns, referrer);
9911 if (aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_FIRST_LOAD)) {
9912 props->SetPropertyAsBool(u"docshell.newWindowTarget"_ns, true);
9916 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(channel));
9917 auto loadType = aLoadState->LoadType();
9919 if (loadType == LOAD_RELOAD_NORMAL &&
9920 StaticPrefs::
9921 browser_soft_reload_only_force_validate_top_level_document()) {
9922 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
9923 if (cachingChannel) {
9924 cachingChannel->SetForceValidateCacheContent(true);
9928 // figure out if we need to set the post data stream on the channel...
9929 if (aLoadState->PostDataStream()) {
9930 if (nsCOMPtr<nsIFormPOSTActionChannel> postChannel =
9931 do_QueryInterface(channel)) {
9932 // XXX it's a bit of a hack to rewind the postdata stream here but
9933 // it has to be done in case the post data is being reused multiple
9934 // times.
9935 nsCOMPtr<nsISeekableStream> postDataSeekable =
9936 do_QueryInterface(aLoadState->PostDataStream());
9937 if (postDataSeekable) {
9938 aRv = postDataSeekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
9939 NS_ENSURE_SUCCESS(aRv, false);
9942 // we really need to have a content type associated with this stream!!
9943 postChannel->SetUploadStream(aLoadState->PostDataStream(), ""_ns, -1);
9945 // Ownership of the stream has transferred to the channel, clear our
9946 // reference.
9947 aLoadState->SetPostDataStream(nullptr);
9950 /* If there is a valid postdata *and* it is a History Load,
9951 * set up the cache key on the channel, to retrieve the
9952 * data *only* from the cache. If it is a normal reload, the
9953 * cache is free to go to the server for updated postdata.
9955 if (cacheChannel && aCacheKey != 0) {
9956 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_CHARSET_CHANGE) {
9957 cacheChannel->SetCacheKey(aCacheKey);
9958 uint32_t loadFlags;
9959 if (NS_SUCCEEDED(channel->GetLoadFlags(&loadFlags))) {
9960 channel->SetLoadFlags(loadFlags |
9961 nsICachingChannel::LOAD_ONLY_FROM_CACHE);
9963 } else if (loadType == LOAD_RELOAD_NORMAL) {
9964 cacheChannel->SetCacheKey(aCacheKey);
9967 } else {
9968 /* If there is no postdata, set the cache key on the channel, and
9969 * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
9970 * will be free to get it from net if it is not found in cache.
9971 * New cache may use it creatively on CGI pages with GET
9972 * method and even on those that say "no-cache"
9974 if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
9975 loadType == LOAD_RELOAD_CHARSET_CHANGE ||
9976 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
9977 loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
9978 if (cacheChannel && aCacheKey != 0) {
9979 cacheChannel->SetCacheKey(aCacheKey);
9984 if (nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(channel)) {
9985 // Allow execution against our context if the principals match
9986 scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
9989 if (nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(channel)) {
9990 timedChannel->SetTimingEnabled(true);
9992 nsString initiatorType;
9993 switch (aLoadInfo->InternalContentPolicyType()) {
9994 case nsIContentPolicy::TYPE_INTERNAL_EMBED:
9995 initiatorType = u"embed"_ns;
9996 break;
9997 case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
9998 initiatorType = u"object"_ns;
9999 break;
10000 default: {
10001 const auto& embedderElementType =
10002 aBrowsingContext->GetEmbedderElementType();
10003 if (embedderElementType) {
10004 initiatorType = *embedderElementType;
10006 break;
10010 if (!initiatorType.IsEmpty()) {
10011 timedChannel->SetInitiatorType(initiatorType);
10015 nsCOMPtr<nsIURI> rpURI;
10016 aLoadInfo->GetResultPrincipalURI(getter_AddRefs(rpURI));
10017 Maybe<nsCOMPtr<nsIURI>> originalResultPrincipalURI;
10018 aLoadState->GetMaybeResultPrincipalURI(originalResultPrincipalURI);
10019 if (originalResultPrincipalURI &&
10020 (!aLoadState->KeepResultPrincipalURIIfSet() || !rpURI)) {
10021 // Unconditionally override, we want the replay to be equal to what has
10022 // been captured.
10023 aLoadInfo->SetResultPrincipalURI(originalResultPrincipalURI.ref());
10026 if (aLoadState->OriginalURI() && aLoadState->LoadReplace()) {
10027 // The LOAD_REPLACE flag and its handling here will be removed as part
10028 // of bug 1319110. For now preserve its restoration here to not break
10029 // any code expecting it being set specially on redirected channels.
10030 // If the flag has originally been set to change result of
10031 // NS_GetFinalChannelURI it won't have any effect and also won't cause
10032 // any harm.
10033 uint32_t loadFlags;
10034 aRv = channel->GetLoadFlags(&loadFlags);
10035 NS_ENSURE_SUCCESS(aRv, false);
10036 channel->SetLoadFlags(loadFlags | nsIChannel::LOAD_REPLACE);
10039 nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp();
10040 if (csp) {
10041 // Navigational requests that are same origin need to be upgraded in case
10042 // upgrade-insecure-requests is present. Please note that for document
10043 // navigations that bit is re-computed in case we encounter a server
10044 // side redirect so the navigation is not same-origin anymore.
10045 bool upgradeInsecureRequests = false;
10046 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests);
10047 if (upgradeInsecureRequests) {
10048 // only upgrade if the navigation is same origin
10049 nsCOMPtr<nsIPrincipal> resultPrincipal;
10050 aRv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
10051 channel, getter_AddRefs(resultPrincipal));
10052 NS_ENSURE_SUCCESS(aRv, false);
10053 if (nsContentSecurityUtils::IsConsideredSameOriginForUIR(
10054 aLoadState->TriggeringPrincipal(), resultPrincipal)) {
10055 aLoadInfo->SetUpgradeInsecureRequests(true);
10059 // For document loads we store the CSP that potentially needs to
10060 // be inherited by the new document, e.g. in case we are loading
10061 // an opaque origin like a data: URI. The actual inheritance
10062 // check happens within Document::InitCSP().
10063 // Please create an actual copy of the CSP (do not share the same
10064 // reference) otherwise a Meta CSP of an opaque origin will
10065 // incorrectly be propagated to the embedding document.
10066 RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
10067 cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
10068 aLoadInfo->SetCSPToInherit(cspToInherit);
10071 channel.forget(aChannel);
10072 return true;
10075 bool nsDocShell::IsAboutBlankLoadOntoInitialAboutBlank(
10076 nsIURI* aURI, bool aInheritPrincipal, nsIPrincipal* aPrincipalToInherit) {
10077 return NS_IsAboutBlankAllowQueryAndFragment(aURI) && aInheritPrincipal &&
10078 (aPrincipalToInherit == GetInheritedPrincipal(false)) &&
10079 (!mDocumentViewer || !mDocumentViewer->GetDocument() ||
10080 mDocumentViewer->GetDocument()->IsInitialDocument());
10083 nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
10084 Maybe<uint32_t> aCacheKey,
10085 nsIRequest** aRequest) {
10086 // Double-check that we're still around to load this URI.
10087 if (mIsBeingDestroyed) {
10088 // Return NS_OK despite not doing anything to avoid throwing exceptions
10089 // from nsLocation::SetHref if the unload handler of the existing page
10090 // tears us down.
10091 return NS_OK;
10094 nsCOMPtr<nsIURILoader> uriLoader = components::URILoader::Service();
10095 if (NS_WARN_IF(!uriLoader)) {
10096 return NS_ERROR_UNEXPECTED;
10099 // Persist and sync layout history state before we load a new uri, as this
10100 // might be our last chance to do so, in the content process.
10101 PersistLayoutHistoryState();
10102 SynchronizeLayoutHistoryState();
10104 nsresult rv;
10105 nsContentPolicyType contentPolicyType = DetermineContentType();
10107 if (IsSubframe()) {
10108 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
10109 contentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
10110 "DoURILoad thinks this is a frame and InternalLoad does not");
10112 if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
10113 // Only allow URLs able to return data in iframes.
10114 if (nsContentUtils::IsExternalProtocol(aLoadState->URI())) {
10115 // The context to check user-interaction with for the purposes of
10116 // popup-blocking.
10118 // We generally want to check the context that initiated the navigation.
10119 WindowContext* sourceWindowContext = [&] {
10120 const MaybeDiscardedBrowsingContext& sourceBC =
10121 aLoadState->SourceBrowsingContext();
10122 if (!sourceBC.IsNullOrDiscarded()) {
10123 if (WindowContext* wc = sourceBC.get()->GetCurrentWindowContext()) {
10124 return wc;
10127 return mBrowsingContext->GetParentWindowContext();
10128 }();
10130 MOZ_ASSERT(sourceWindowContext);
10131 // FIXME: We can't check user-interaction against an OOP window. This is
10132 // the next best thing we can really do. The load state keeps whether
10133 // the navigation had a user interaction in process
10134 // (aLoadState->HasValidUserGestureActivation()), but we can't really
10135 // consume it, which we want to prevent popup-spamming from the same
10136 // click event.
10137 WindowContext* context =
10138 sourceWindowContext->IsInProcess()
10139 ? sourceWindowContext
10140 : mBrowsingContext->GetCurrentWindowContext();
10141 const bool popupBlocked = [&] {
10142 const bool active = mBrowsingContext->IsActive();
10144 // For same-origin-with-top windows, we grant a single free popup
10145 // without user activation, see bug 1680721.
10147 // We consume the flag now even if there's no user activation.
10148 const bool hasFreePass = [&] {
10149 if (!active ||
10150 !(context->IsInProcess() && context->SameOriginWithTop())) {
10151 return false;
10153 nsGlobalWindowInner* win =
10154 context->TopWindowContext()->GetInnerWindow();
10155 return win && win->TryOpenExternalProtocolIframe();
10156 }();
10158 if (context->IsInProcess() &&
10159 context->ConsumeTransientUserGestureActivation()) {
10160 // If the user has interacted with the page, consume it.
10161 return false;
10164 // TODO(emilio): Can we remove this check? It seems like what prompted
10165 // this code (bug 1514547) should be covered by transient user
10166 // activation, see bug 1514547.
10167 if (active &&
10168 PopupBlocker::ConsumeTimerTokenForExternalProtocolIframe()) {
10169 return false;
10172 if (sourceWindowContext->CanShowPopup()) {
10173 return false;
10176 if (hasFreePass) {
10177 return false;
10180 return true;
10181 }();
10183 // No error must be returned when iframes are blocked.
10184 if (popupBlocked) {
10185 nsAutoString message;
10186 nsresult rv = nsContentUtils::GetLocalizedString(
10187 nsContentUtils::eDOM_PROPERTIES,
10188 "ExternalProtocolFrameBlockedNoUserActivation", message);
10189 if (NS_SUCCEEDED(rv)) {
10190 nsContentUtils::ReportToConsoleByWindowID(
10191 message, nsIScriptError::warningFlag, "DOM"_ns,
10192 context->InnerWindowId());
10194 return NS_OK;
10199 // Only allow view-source scheme in top-level docshells. view-source is
10200 // the only scheme to which this applies at the moment due to potential
10201 // timing attacks to read data from cross-origin iframes. If this widens
10202 // we should add a protocol flag for whether the scheme is allowed in
10203 // frames and use something like nsNetUtil::NS_URIChainHasFlags.
10204 nsCOMPtr<nsIURI> tempURI = aLoadState->URI();
10205 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(tempURI);
10206 while (nestedURI) {
10207 // view-source should always be an nsINestedURI, loop and check the
10208 // scheme on this and all inner URIs that are also nested URIs.
10209 if (SchemeIsViewSource(tempURI)) {
10210 return NS_ERROR_UNKNOWN_PROTOCOL;
10212 nestedURI->GetInnerURI(getter_AddRefs(tempURI));
10213 nestedURI = do_QueryInterface(tempURI);
10215 } else {
10216 MOZ_ASSERT(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10217 "DoURILoad thinks this is a document and InternalLoad does not");
10220 // We want to inherit aLoadState->PrincipalToInherit() when:
10221 // 1. ChannelShouldInheritPrincipal returns true.
10222 // 2. aLoadState->URI() is not data: URI, or data: URI is not
10223 // configured as unique opaque origin.
10224 bool inheritPrincipal = false;
10226 if (aLoadState->PrincipalToInherit()) {
10227 bool isSrcdoc =
10228 aLoadState->HasInternalLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC);
10229 bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
10230 aLoadState->PrincipalToInherit(), aLoadState->URI(),
10231 true, // aInheritForAboutBlank
10232 isSrcdoc);
10234 inheritPrincipal = inheritAttrs && !SchemeIsData(aLoadState->URI());
10237 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1736570
10238 const bool isAboutBlankLoadOntoInitialAboutBlank =
10239 IsAboutBlankLoadOntoInitialAboutBlank(aLoadState->URI(), inheritPrincipal,
10240 aLoadState->PrincipalToInherit());
10242 // FIXME We still have a ton of codepaths that don't pass through
10243 // DocumentLoadListener, so probably need to create session history info
10244 // in more places.
10245 if (aLoadState->GetLoadingSessionHistoryInfo()) {
10246 SetLoadingSessionHistoryInfo(*aLoadState->GetLoadingSessionHistoryInfo());
10247 } else if (isAboutBlankLoadOntoInitialAboutBlank &&
10248 mozilla::SessionHistoryInParent()) {
10249 // Materialize LoadingSessionHistoryInfo here, because DocumentChannel
10250 // loads have it, and later history behavior depends on it existing.
10251 UniquePtr<SessionHistoryInfo> entry = MakeUnique<SessionHistoryInfo>(
10252 aLoadState->URI(), aLoadState->TriggeringPrincipal(),
10253 aLoadState->PrincipalToInherit(),
10254 aLoadState->PartitionedPrincipalToInherit(), aLoadState->Csp(),
10255 mContentTypeHint);
10256 mozilla::dom::LoadingSessionHistoryInfo info(*entry);
10257 SetLoadingSessionHistoryInfo(info, true);
10260 // open a channel for the url
10262 // If we have a pending channel, use the channel we've already created here.
10263 // We don't need to set up load flags for our channel, as it has already been
10264 // created.
10266 if (nsCOMPtr<nsIChannel> channel =
10267 aLoadState->GetPendingRedirectedChannel()) {
10268 // If we have a request outparameter, shove our channel into it.
10269 if (aRequest) {
10270 nsCOMPtr<nsIRequest> outRequest = channel;
10271 outRequest.forget(aRequest);
10274 return OpenRedirectedChannel(aLoadState);
10277 // There are two cases we care about:
10278 // * Top-level load: In this case, loadingNode is null, but loadingWindow
10279 // is our mScriptGlobal. We pass null for loadingPrincipal in this case.
10280 // * Subframe load: loadingWindow is null, but loadingNode is the frame
10281 // element for the load. loadingPrincipal is the NodePrincipal of the
10282 // frame element.
10283 nsCOMPtr<nsINode> loadingNode;
10284 nsCOMPtr<nsPIDOMWindowOuter> loadingWindow;
10285 nsCOMPtr<nsIPrincipal> loadingPrincipal;
10286 nsCOMPtr<nsISupports> topLevelLoadingContext;
10288 if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) {
10289 loadingNode = nullptr;
10290 loadingPrincipal = nullptr;
10291 loadingWindow = mScriptGlobal;
10292 if (XRE_IsContentProcess()) {
10293 // In e10s the child process doesn't have access to the element that
10294 // contains the browsing context (because that element is in the chrome
10295 // process).
10296 nsCOMPtr<nsIBrowserChild> browserChild = GetBrowserChild();
10297 topLevelLoadingContext = ToSupports(browserChild);
10298 } else {
10299 // This is for loading non-e10s tabs and toplevel windows of various
10300 // sorts.
10301 // For the toplevel window cases, requestingElement will be null.
10302 nsCOMPtr<Element> requestingElement =
10303 loadingWindow->GetFrameElementInternal();
10304 topLevelLoadingContext = requestingElement;
10306 } else {
10307 loadingWindow = nullptr;
10308 loadingNode = mScriptGlobal->GetFrameElementInternal();
10309 if (loadingNode) {
10310 // If we have a loading node, then use that as our loadingPrincipal.
10311 loadingPrincipal = loadingNode->NodePrincipal();
10312 #ifdef DEBUG
10313 // Get the docshell type for requestingElement.
10314 RefPtr<Document> requestingDoc = loadingNode->OwnerDoc();
10315 nsCOMPtr<nsIDocShell> elementDocShell = requestingDoc->GetDocShell();
10316 // requestingElement docshell type = current docshell type.
10317 MOZ_ASSERT(
10318 mItemType == elementDocShell->ItemType(),
10319 "subframes should have the same docshell type as their parent");
10320 #endif
10321 } else {
10322 if (mIsBeingDestroyed) {
10323 // If this isn't a top-level load and mScriptGlobal's frame element is
10324 // null, then the element got removed from the DOM while we were trying
10325 // to load this resource. This docshell is scheduled for destruction
10326 // already, so bail out here.
10327 return NS_OK;
10329 // If we are not being destroyed and we do not have access to the loading
10330 // node, then we are a remote subframe. Set the loading principal
10331 // to be a null principal and then set it correctly in the parent.
10332 loadingPrincipal = NullPrincipal::Create(GetOriginAttributes(), nullptr);
10336 if (!aLoadState->TriggeringPrincipal()) {
10337 MOZ_ASSERT(false, "DoURILoad needs a valid triggeringPrincipal");
10338 return NS_ERROR_FAILURE;
10341 uint32_t sandboxFlags = mBrowsingContext->GetSandboxFlags();
10342 nsSecurityFlags securityFlags =
10343 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL;
10345 if (mLoadType == LOAD_ERROR_PAGE) {
10346 securityFlags |= nsILoadInfo::SEC_LOAD_ERROR_PAGE;
10349 if (inheritPrincipal) {
10350 securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
10353 // Must never have a parent for TYPE_DOCUMENT loads
10354 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT,
10355 !mBrowsingContext->GetParent());
10356 // Subdocuments must have a parent
10357 MOZ_ASSERT_IF(contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT,
10358 mBrowsingContext->GetParent());
10359 mBrowsingContext->SetTriggeringAndInheritPrincipals(
10360 aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
10361 aLoadState->GetLoadIdentifier());
10362 RefPtr<LoadInfo> loadInfo =
10363 (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT)
10364 ? new LoadInfo(loadingWindow, aLoadState->URI(),
10365 aLoadState->TriggeringPrincipal(),
10366 topLevelLoadingContext, securityFlags, sandboxFlags)
10367 : new LoadInfo(loadingPrincipal, aLoadState->TriggeringPrincipal(),
10368 loadingNode, securityFlags, contentPolicyType,
10369 Maybe<mozilla::dom::ClientInfo>(),
10370 Maybe<mozilla::dom::ServiceWorkerDescriptor>(),
10371 sandboxFlags);
10372 RefPtr<WindowContext> context = mBrowsingContext->GetCurrentWindowContext();
10374 if (isAboutBlankLoadOntoInitialAboutBlank) {
10375 // Match the DocumentChannel case where the default for third-partiness
10376 // differs from the default in LoadInfo construction here.
10377 // toolkit/components/antitracking/test/browser/browser_aboutblank.js
10378 // fails without this.
10379 BrowsingContext* top = mBrowsingContext->Top();
10380 if (top == mBrowsingContext) {
10381 // If we're at the top, this must be a window.open()ed
10382 // window, and we can't be third-party relative to ourselves.
10383 loadInfo->SetIsThirdPartyContextToTopWindow(false);
10384 } else {
10385 if (Document* topDoc = top->GetDocument()) {
10386 bool thirdParty = false;
10387 mozilla::Unused << topDoc->GetPrincipal()->IsThirdPartyPrincipal(
10388 aLoadState->PrincipalToInherit(), &thirdParty);
10389 loadInfo->SetIsThirdPartyContextToTopWindow(thirdParty);
10390 } else {
10391 // If top is in a different process, we have to be third-party relative
10392 // to it.
10393 loadInfo->SetIsThirdPartyContextToTopWindow(true);
10398 if (mLoadType != LOAD_ERROR_PAGE && context && context->IsInProcess()) {
10399 if (context->HasValidTransientUserGestureActivation()) {
10400 aLoadState->SetHasValidUserGestureActivation(true);
10401 aLoadState->SetTextDirectiveUserActivation(true);
10403 if (!aLoadState->TriggeringWindowId()) {
10404 aLoadState->SetTriggeringWindowId(context->Id());
10406 if (!aLoadState->TriggeringStorageAccess()) {
10407 Document* contextDoc = context->GetExtantDoc();
10408 if (contextDoc) {
10409 aLoadState->SetTriggeringStorageAccess(
10410 contextDoc->UsingStorageAccess());
10415 // in case this docshell load was triggered by a valid transient user gesture,
10416 // or also the load originates from external, then we pass that information on
10417 // to the loadinfo, which allows e.g. setting Sec-Fetch-User request headers.
10418 if (aLoadState->HasValidUserGestureActivation() ||
10419 aLoadState->HasLoadFlags(LOAD_FLAGS_FROM_EXTERNAL)) {
10420 loadInfo->SetHasValidUserGestureActivation(true);
10421 aLoadState->SetTextDirectiveUserActivation(true);
10424 loadInfo->SetTextDirectiveUserActivation(
10425 aLoadState->GetTextDirectiveUserActivation());
10427 loadInfo->SetTriggeringWindowId(aLoadState->TriggeringWindowId());
10428 loadInfo->SetTriggeringStorageAccess(aLoadState->TriggeringStorageAccess());
10429 loadInfo->SetTriggeringSandboxFlags(aLoadState->TriggeringSandboxFlags());
10430 loadInfo->SetIsMetaRefresh(aLoadState->IsMetaRefresh());
10432 uint32_t cacheKey = 0;
10433 if (aCacheKey) {
10434 cacheKey = *aCacheKey;
10435 } else if (mozilla::SessionHistoryInParent()) {
10436 if (mLoadingEntry) {
10437 cacheKey = mLoadingEntry->mInfo.GetCacheKey();
10438 } else if (mActiveEntry) { // for reload cases
10439 cacheKey = mActiveEntry->GetCacheKey();
10441 } else {
10442 if (mLSHE) {
10443 cacheKey = mLSHE->GetCacheKey();
10444 } else if (mOSHE) { // for reload cases
10445 cacheKey = mOSHE->GetCacheKey();
10449 bool uriModified;
10450 if (mLSHE || mLoadingEntry) {
10451 if (mLoadingEntry) {
10452 uriModified = mLoadingEntry->mInfo.GetURIWasModified();
10453 } else {
10454 uriModified = mLSHE->GetURIWasModified();
10456 } else {
10457 uriModified = false;
10460 bool isEmbeddingBlockedError = false;
10461 if (mFailedChannel) {
10462 nsresult status;
10463 mFailedChannel->GetStatus(&status);
10464 isEmbeddingBlockedError = status == NS_ERROR_XFO_VIOLATION ||
10465 status == NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION;
10468 nsLoadFlags loadFlags = aLoadState->CalculateChannelLoadFlags(
10469 mBrowsingContext, Some(uriModified), Some(isEmbeddingBlockedError));
10471 nsCOMPtr<nsIChannel> channel;
10472 if (DocumentChannel::CanUseDocumentChannel(aLoadState->URI()) &&
10473 !isAboutBlankLoadOntoInitialAboutBlank) {
10474 channel = DocumentChannel::CreateForDocument(
10475 aLoadState, loadInfo, loadFlags, this, cacheKey, uriModified,
10476 isEmbeddingBlockedError);
10477 MOZ_ASSERT(channel);
10479 // Disable keyword fixup when using DocumentChannel, since
10480 // DocumentLoadListener will handle this for us (in the parent process).
10481 mAllowKeywordFixup = false;
10482 } else if (!CreateAndConfigureRealChannelForLoadState(
10483 mBrowsingContext, aLoadState, loadInfo, this, this,
10484 GetOriginAttributes(), loadFlags, cacheKey, rv,
10485 getter_AddRefs(channel))) {
10486 return rv;
10489 // Make sure to give the caller a channel if we managed to create one
10490 // This is important for correct error page/session history interaction
10491 if (aRequest) {
10492 NS_ADDREF(*aRequest = channel);
10495 const nsACString& typeHint = aLoadState->TypeHint();
10496 if (!typeHint.IsVoid()) {
10497 mContentTypeHint = typeHint;
10498 } else {
10499 mContentTypeHint.Truncate();
10502 // Load attributes depend on load type...
10503 if (mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
10504 // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we
10505 // only want to force cache load for this channel, not the whole
10506 // loadGroup.
10507 nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(channel);
10508 if (cachingChannel) {
10509 cachingChannel->SetAllowStaleCacheContent(true);
10513 uint32_t openFlags =
10514 nsDocShell::ComputeURILoaderFlags(mBrowsingContext, mLoadType);
10515 return OpenInitializedChannel(channel, uriLoader, openFlags);
10518 static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
10519 const char* aFromRawSegment,
10520 uint32_t aToOffset, uint32_t aCount,
10521 uint32_t* aWriteCount) {
10522 // aFromSegment now contains aCount bytes of data.
10524 nsAutoCString* buf = static_cast<nsAutoCString*>(aClosure);
10525 buf->Append(aFromRawSegment, aCount);
10527 // Indicate that we have consumed all of aFromSegment
10528 *aWriteCount = aCount;
10529 return NS_OK;
10532 /* static */ nsresult nsDocShell::AddHeadersToChannel(
10533 nsIInputStream* aHeadersData, nsIChannel* aGenericChannel) {
10534 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aGenericChannel);
10535 NS_ENSURE_STATE(httpChannel);
10537 uint32_t numRead;
10538 nsAutoCString headersString;
10539 nsresult rv = aHeadersData->ReadSegments(
10540 AppendSegmentToString, &headersString, UINT32_MAX, &numRead);
10541 NS_ENSURE_SUCCESS(rv, rv);
10543 // used during the manipulation of the String from the InputStream
10544 nsAutoCString headerName;
10545 nsAutoCString headerValue;
10546 int32_t crlf;
10547 int32_t colon;
10550 // Iterate over the headersString: for each "\r\n" delimited chunk,
10551 // add the value as a header to the nsIHttpChannel
10554 static const char kWhitespace[] = "\b\t\r\n ";
10555 while (true) {
10556 crlf = headersString.Find("\r\n");
10557 if (crlf == kNotFound) {
10558 return NS_OK;
10561 const nsACString& oneHeader = StringHead(headersString, crlf);
10563 colon = oneHeader.FindChar(':');
10564 if (colon == kNotFound) {
10565 return NS_ERROR_UNEXPECTED;
10568 headerName = StringHead(oneHeader, colon);
10569 headerValue = Substring(oneHeader, colon + 1);
10571 headerName.Trim(kWhitespace);
10572 headerValue.Trim(kWhitespace);
10574 headersString.Cut(0, crlf + 2);
10577 // FINALLY: we can set the header!
10580 rv = httpChannel->SetRequestHeader(headerName, headerValue, true);
10581 NS_ENSURE_SUCCESS(rv, rv);
10584 MOZ_ASSERT_UNREACHABLE("oops");
10585 return NS_ERROR_UNEXPECTED;
10588 /* static */ uint32_t nsDocShell::ComputeURILoaderFlags(
10589 BrowsingContext* aBrowsingContext, uint32_t aLoadType,
10590 bool aIsDocumentLoad) {
10591 MOZ_ASSERT(aBrowsingContext);
10593 uint32_t openFlags = 0;
10594 if (aLoadType == LOAD_LINK) {
10595 openFlags |= nsIURILoader::IS_CONTENT_PREFERRED;
10597 if (!aBrowsingContext->GetAllowContentRetargeting()) {
10598 openFlags |= nsIURILoader::DONT_RETARGET;
10601 // Unless the pref is set, object/embed loads always specify DONT_RETARGET.
10602 // See bug 1868001 for details.
10603 if (!aIsDocumentLoad &&
10604 !StaticPrefs::dom_navigation_object_embed_allow_retargeting()) {
10605 openFlags |= nsIURILoader::DONT_RETARGET;
10608 return openFlags;
10611 nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
10612 nsIURILoader* aURILoader,
10613 uint32_t aOpenFlags) {
10614 nsresult rv = NS_OK;
10616 // If anything fails here, make sure to clear our initial ClientSource.
10617 auto cleanupInitialClient =
10618 MakeScopeExit([&] { mInitialClientSource.reset(); });
10620 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10621 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10623 MaybeCreateInitialClientSource();
10625 // Let the client channel helper know if we are using DocumentChannel,
10626 // since redirects get handled in the parent process in that case.
10627 RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
10628 if (docChannel && XRE_IsContentProcess()) {
10629 // Tell the content process nsDocumentOpenInfo to not try to do
10630 // any sort of targeting.
10631 aOpenFlags |= nsIURILoader::DONT_RETARGET;
10634 // Since we are loading a document we need to make sure the proper reserved
10635 // and initial client data is stored on the nsILoadInfo. The
10636 // ClientChannelHelper does this and ensures that it is propagated properly
10637 // on redirects. We pass no reserved client here so that the helper will
10638 // create the reserved ClientSource if necessary.
10639 Maybe<ClientInfo> noReservedClient;
10640 if (docChannel) {
10641 // When using DocumentChannel, all redirect handling is done in the parent,
10642 // so we just need the child variant to watch for the internal redirect
10643 // to the final channel.
10644 rv = AddClientChannelHelperInChild(aChannel,
10645 GetMainThreadSerialEventTarget());
10646 docChannel->SetInitialClientInfo(GetInitialClientInfo());
10647 } else {
10648 rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
10649 GetInitialClientInfo(),
10650 GetMainThreadSerialEventTarget());
10652 NS_ENSURE_SUCCESS(rv, rv);
10654 rv = aURILoader->OpenURI(aChannel, aOpenFlags, this);
10655 NS_ENSURE_SUCCESS(rv, rv);
10657 // We're about to load a new page and it may take time before necko
10658 // gives back any data, so main thread might have a chance to process a
10659 // collector slice
10660 nsJSContext::MaybeRunNextCollectorSlice(this, JS::GCReason::DOCSHELL);
10662 // Success. Keep the initial ClientSource if it exists.
10663 cleanupInitialClient.release();
10665 return NS_OK;
10668 nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
10669 nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
10670 MOZ_ASSERT(channel);
10672 // If anything fails here, make sure to clear our initial ClientSource.
10673 auto cleanupInitialClient =
10674 MakeScopeExit([&] { mInitialClientSource.reset(); });
10676 nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
10677 NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
10679 MaybeCreateInitialClientSource();
10681 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
10683 LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
10684 if (loadInfo->GetExternalContentPolicyType() ==
10685 ExtContentPolicy::TYPE_DOCUMENT) {
10686 li->UpdateBrowsingContextID(mBrowsingContext->Id());
10687 } else if (loadInfo->GetExternalContentPolicyType() ==
10688 ExtContentPolicy::TYPE_SUBDOCUMENT) {
10689 li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
10692 // If we did a process switch, then we should have an existing allocated
10693 // ClientInfo, so we just need to allocate a corresponding ClientSource.
10694 CreateReservedSourceIfNeeded(channel, GetMainThreadSerialEventTarget());
10696 RefPtr<nsDocumentOpenInfo> loader =
10697 new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
10698 channel->SetLoadGroup(mLoadGroup);
10700 MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
10702 nsresult rv = NS_OK;
10703 if (XRE_IsParentProcess()) {
10704 // If we're in the parent, the we don't have an nsIChildChannel, just
10705 // the original channel, which is already open in this process.
10707 // DocumentLoadListener expects to get an nsIParentChannel, so
10708 // we create a wrapper around the channel and nsIStreamListener
10709 // that forwards functionality as needed, and then we register
10710 // it under the provided identifier.
10711 RefPtr<ParentChannelWrapper> wrapper =
10712 new ParentChannelWrapper(channel, loader);
10713 wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
10715 mLoadGroup->AddRequest(channel, nullptr);
10716 } else if (nsCOMPtr<nsIChildChannel> childChannel =
10717 do_QueryInterface(channel)) {
10718 // Our channel was redirected from another process, so doesn't need to
10719 // be opened again. However, it does need its listener hooked up
10720 // correctly.
10721 rv = childChannel->CompleteRedirectSetup(loader);
10722 } else {
10723 // It's possible for the redirected channel to not implement
10724 // nsIChildChannel and be entirely local (like srcdoc). In that case we
10725 // can just open the local instance and it will work.
10726 rv = channel->AsyncOpen(loader);
10728 if (rv == NS_ERROR_NO_CONTENT) {
10729 return NS_OK;
10731 NS_ENSURE_SUCCESS(rv, rv);
10733 // Success. Keep the initial ClientSource if it exists.
10734 cleanupInitialClient.release();
10735 return NS_OK;
10738 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
10739 nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
10740 nsACString& aNewHash, uint32_t aLoadType) {
10741 if (!mCurrentURI) {
10742 return NS_OK;
10745 RefPtr<PresShell> presShell = GetPresShell();
10746 if (!presShell) {
10747 // If we failed to get the shell, or if there is no shell,
10748 // nothing left to do here.
10749 return NS_OK;
10752 ScrollContainerFrame* rootScroll = presShell->GetRootScrollContainerFrame();
10753 if (rootScroll) {
10754 rootScroll->ClearDidHistoryRestore();
10757 // If it's a load from history, we don't have any anchor jumping to do.
10758 // Scrollbar position will be restored by the caller based on positions stored
10759 // in session history.
10760 bool scroll = aLoadType != LOAD_HISTORY && aLoadType != LOAD_RELOAD_NORMAL;
10761 // If the load contains text directives, try to apply them. This may fail if
10762 // the load is a same-document load that was initiated before the document was
10763 // fully loaded and the target is not yet included in the DOM tree.
10764 // For this case, the `uninvokedTextDirectives` are not cleared, so that
10765 // `Document::ScrollToRef()` can re-apply the text directive.
10766 // `Document::ScrollToRef()` is (presumably) the second "async" call mentioned
10767 // in sec. 7.4.2.3.3 in the HTML spec, "Fragment navigations":
10768 // https://html.spec.whatwg.org/#scroll-to-fragid:~:text=This%20algorithm%20will%20be%20called%20twice
10770 const RefPtr fragmentDirective = GetDocument()->FragmentDirective();
10771 const nsTArray<RefPtr<nsRange>> textDirectiveRanges =
10772 fragmentDirective->FindTextFragmentsInDocument();
10773 fragmentDirective->HighlightTextDirectives(textDirectiveRanges);
10774 const bool scrollToTextDirective =
10775 !textDirectiveRanges.IsEmpty() &&
10776 fragmentDirective->IsTextDirectiveAllowedToBeScrolledTo();
10777 const RefPtr<nsRange> textDirectiveToScroll =
10778 scrollToTextDirective ? textDirectiveRanges[0] : nullptr;
10780 // If we have no new anchor, we do not want to scroll, unless there is a
10781 // current anchor and we are doing a history load. So return if we have no
10782 // new anchor, and there is no current anchor or the load is not a history
10783 // load.
10784 if ((!aCurHasRef || aLoadType != LOAD_HISTORY) && !aNewHasRef &&
10785 !scrollToTextDirective) {
10786 return NS_OK;
10789 // Both the new and current URIs refer to the same page. We can now
10790 // browse to the hash stored in the new URI.
10792 if (aNewHash.IsEmpty() && !scrollToTextDirective) {
10793 // 2. If fragment is the empty string, then return the special value top of
10794 // the document.
10796 // Tell the shell it's at an anchor without scrolling.
10797 presShell->GoToAnchor(u""_ns, nullptr, false);
10799 if (scroll) {
10800 // Scroll to the top of the page. Ignore the return value; failure to
10801 // scroll here (e.g. if there is no root scrollframe) is not grounds for
10802 // canceling the load!
10803 SetCurScrollPosEx(0, 0);
10806 return NS_OK;
10809 // 3. Let potentialIndicatedElement be the result of finding a potential
10810 // indicated element given document and fragment.
10811 NS_ConvertUTF8toUTF16 uStr(aNewHash);
10813 MOZ_ASSERT(!uStr.IsEmpty() || scrollToTextDirective);
10815 auto rv = presShell->GoToAnchor(uStr, textDirectiveToScroll, scroll,
10816 ScrollFlags::ScrollSmoothAuto);
10818 // 4. If potentialIndicatedElement is not null, then return
10819 // potentialIndicatedElement.
10820 if (NS_SUCCEEDED(rv)) {
10821 return NS_OK;
10824 // 5. Let fragmentBytes be the result of percent-decoding fragment.
10825 nsAutoCString fragmentBytes;
10826 const bool unescaped = NS_UnescapeURL(aNewHash.Data(), aNewHash.Length(),
10827 /* aFlags = */ 0, fragmentBytes);
10829 if (!unescaped) {
10830 // Another attempt is only necessary if characters were unescaped.
10831 return NS_OK;
10834 if (fragmentBytes.IsEmpty()) {
10835 // When aNewHash contains "%00", the unescaped string may be empty, and
10836 // GoToAnchor asserts if we ask it to scroll to an empty ref.
10837 presShell->GoToAnchor(u""_ns, nullptr, false);
10838 return NS_OK;
10841 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
10842 // fragmentBytes.
10843 nsAutoString decodedFragment;
10844 rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
10845 NS_ENSURE_SUCCESS(rv, rv);
10847 // 7. Set potentialIndicatedElement to the result of finding a potential
10848 // indicated element given document and decodedFragment.
10850 // Ignore the return value of GoToAnchor, since it will return an error if
10851 // there is no such anchor in the document, which is actually a success
10852 // condition for us (we want to update the session history with the new URI no
10853 // matter whether we actually scrolled somewhere).
10854 presShell->GoToAnchor(decodedFragment, nullptr, scroll,
10855 ScrollFlags::ScrollSmoothAuto);
10857 return NS_OK;
10860 bool nsDocShell::OnNewURI(nsIURI* aURI, nsIChannel* aChannel,
10861 nsIPrincipal* aTriggeringPrincipal,
10862 nsIPrincipal* aPrincipalToInherit,
10863 nsIPrincipal* aPartitionedPrincipalToInherit,
10864 nsIContentSecurityPolicy* aCsp,
10865 bool aAddToGlobalHistory, bool aCloneSHChildren) {
10866 MOZ_ASSERT(aURI, "uri is null");
10867 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
10869 MOZ_ASSERT(!aPrincipalToInherit ||
10870 (aPrincipalToInherit && aTriggeringPrincipal));
10872 #if defined(DEBUG)
10873 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
10874 nsAutoCString chanName;
10875 if (aChannel) {
10876 aChannel->GetName(chanName);
10877 } else {
10878 chanName.AssignLiteral("<no channel>");
10881 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10882 ("nsDocShell[%p]::OnNewURI(\"%s\", [%s], 0x%x)\n", this,
10883 aURI->GetSpecOrDefault().get(), chanName.get(), mLoadType));
10885 #endif
10887 bool equalUri = false;
10889 // Get the post data and the HTTP response code from the channel.
10890 uint32_t responseStatus = 0;
10891 nsCOMPtr<nsIInputStream> inputStream;
10892 if (aChannel) {
10893 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
10895 // Check if the HTTPChannel is hiding under a multiPartChannel
10896 if (!httpChannel) {
10897 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
10900 if (httpChannel) {
10901 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
10902 if (uploadChannel) {
10903 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
10906 // If the response status indicates an error, unlink this session
10907 // history entry from any entries sharing its document.
10908 nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
10909 if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
10910 mLSHE->AbandonBFCacheEntry();
10911 // FIXME Do the same for mLoadingEntry
10916 // Determine if this type of load should update history.
10917 bool updateGHistory = ShouldUpdateGlobalHistory(mLoadType);
10919 // We don't update session history on reload unless we're loading
10920 // an iframe in shift-reload case.
10921 bool updateSHistory = mBrowsingContext->ShouldUpdateSessionHistory(mLoadType);
10923 // Create SH Entry (mLSHE) only if there is a SessionHistory object in the
10924 // root browsing context.
10925 // FIXME If session history in the parent is enabled then we only do this if
10926 // the session history object is in process, otherwise we can't really
10927 // use the mLSHE anyway. Once session history is only stored in the
10928 // parent then this code will probably be removed anyway.
10929 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
10930 if (!rootSH) {
10931 updateSHistory = false;
10932 updateGHistory = false; // XXX Why global history too?
10935 // Check if the url to be loaded is the same as the one already loaded.
10936 if (mCurrentURI) {
10937 aURI->Equals(mCurrentURI, &equalUri);
10940 #ifdef DEBUG
10941 bool shAvailable = (rootSH != nullptr);
10943 // XXX This log message is almost useless because |updateSHistory|
10944 // and |updateGHistory| are not correct at this point.
10946 MOZ_LOG(gDocShellLog, LogLevel::Debug,
10947 (" shAvailable=%i updateSHistory=%i updateGHistory=%i"
10948 " equalURI=%i\n",
10949 shAvailable, updateSHistory, updateGHistory, equalUri));
10950 #endif
10952 /* If the url to be loaded is the same as the one already there,
10953 * and the original loadType is LOAD_NORMAL, LOAD_LINK, or
10954 * LOAD_STOP_CONTENT, set loadType to LOAD_NORMAL_REPLACE so that
10955 * AddToSessionHistory() won't mess with the current SHEntry and
10956 * if this page has any frame children, it also will be handled
10957 * properly. see bug 83684
10959 * NB: If mOSHE is null but we have a current URI, then it probably
10960 * means that we must be at the transient about:blank content viewer;
10961 * we should let the normal load continue, since there's nothing to
10962 * replace. Sometimes this happens after a session restore (eg process
10963 * switch) and mCurrentURI is not about:blank; we assume we can let the load
10964 * continue (Bug 1301399).
10966 * XXX Hopefully changing the loadType at this time will not hurt
10967 * anywhere. The other way to take care of sequentially repeating
10968 * frameset pages is to add new methods to nsIDocShellTreeItem.
10969 * Hopefully I don't have to do that.
10971 if (equalUri &&
10972 (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) &&
10973 (mLoadType == LOAD_NORMAL || mLoadType == LOAD_LINK ||
10974 mLoadType == LOAD_STOP_CONTENT) &&
10975 !inputStream) {
10976 mLoadType = LOAD_NORMAL_REPLACE;
10979 // If this is a refresh to the currently loaded url, we don't
10980 // have to update session or global history.
10981 if (mLoadType == LOAD_REFRESH && !inputStream && equalUri) {
10982 SetHistoryEntryAndUpdateBC(Some<nsISHEntry*>(mOSHE), Nothing());
10985 /* If the user pressed shift-reload, cache will create a new cache key
10986 * for the page. Save the new cacheKey in Session History.
10987 * see bug 90098
10989 if (aChannel && IsForceReloadType(mLoadType)) {
10990 MOZ_ASSERT(!updateSHistory || IsSubframe(),
10991 "We shouldn't be updating session history for forced"
10992 " reloads unless we're in a newly created iframe!");
10994 nsCOMPtr<nsICacheInfoChannel> cacheChannel(do_QueryInterface(aChannel));
10995 uint32_t cacheKey = 0;
10996 // Get the Cache Key and store it in SH.
10997 if (cacheChannel) {
10998 cacheChannel->GetCacheKey(&cacheKey);
11000 // If we already have a loading history entry, store the new cache key
11001 // in it. Otherwise, since we're doing a reload and won't be updating
11002 // our history entry, store the cache key in our current history entry.
11003 SetCacheKeyOnHistoryEntry(mLSHE ? mLSHE : mOSHE, cacheKey);
11005 if (!mozilla::SessionHistoryInParent()) {
11006 // Since we're force-reloading, clear all the sub frame history.
11007 ClearFrameHistory(mLSHE);
11008 ClearFrameHistory(mOSHE);
11012 if (!mozilla::SessionHistoryInParent()) {
11013 // Clear subframe history on refresh.
11014 // XXX: history.go(0) won't go this path as mLoadType is LOAD_HISTORY in
11015 // this case. One should re-validate after bug 1331865 fixed.
11016 if (mLoadType == LOAD_REFRESH) {
11017 ClearFrameHistory(mLSHE);
11018 ClearFrameHistory(mOSHE);
11021 if (updateSHistory) {
11022 // Update session history if necessary...
11023 if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
11024 /* This is a fresh page getting loaded for the first time
11025 *.Create a Entry for it and add it to SH, if this is the
11026 * rootDocShell
11028 (void)AddToSessionHistory(aURI, aChannel, aTriggeringPrincipal,
11029 aPrincipalToInherit,
11030 aPartitionedPrincipalToInherit, aCsp,
11031 aCloneSHChildren, getter_AddRefs(mLSHE));
11033 } else if (GetSessionHistory() && mLSHE && mURIResultedInDocument) {
11034 // Even if we don't add anything to SHistory, ensure the current index
11035 // points to the same SHEntry as our mLSHE.
11037 GetSessionHistory()->LegacySHistory()->EnsureCorrectEntryAtCurrIndex(
11038 mLSHE);
11042 // If this is a POST request, we do not want to include this in global
11043 // history.
11044 if (ShouldAddURIVisit(aChannel) && updateGHistory && aAddToGlobalHistory &&
11045 !net::ChannelIsPost(aChannel)) {
11046 nsCOMPtr<nsIURI> previousURI;
11047 uint32_t previousFlags = 0;
11049 if (mLoadType & LOAD_CMD_RELOAD) {
11050 // On a reload request, we don't set redirecting flags.
11051 previousURI = aURI;
11052 } else {
11053 ExtractLastVisit(aChannel, getter_AddRefs(previousURI), &previousFlags);
11056 AddURIVisit(aURI, previousURI, previousFlags, responseStatus);
11059 // If this was a history load or a refresh, or it was a history load but
11060 // later changed to LOAD_NORMAL_REPLACE due to redirection, update the index
11061 // in session history.
11062 if (!mozilla::SessionHistoryInParent() && rootSH &&
11063 ((mLoadType & (LOAD_CMD_HISTORY | LOAD_CMD_RELOAD)) ||
11064 mLoadType == LOAD_NORMAL_REPLACE || mLoadType == LOAD_REFRESH_REPLACE)) {
11065 mPreviousEntryIndex = rootSH->Index();
11066 if (!mozilla::SessionHistoryInParent()) {
11067 rootSH->LegacySHistory()->UpdateIndex();
11069 mLoadedEntryIndex = rootSH->Index();
11070 MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11071 ("Previous index: %d, Loaded index: %d", mPreviousEntryIndex,
11072 mLoadedEntryIndex));
11075 // aCloneSHChildren exactly means "we are not loading a new document".
11076 uint32_t locationFlags =
11077 aCloneSHChildren ? uint32_t(LOCATION_CHANGE_SAME_DOCUMENT) : 0;
11079 bool onLocationChangeNeeded =
11080 SetCurrentURI(aURI, aChannel, false,
11081 /* aIsInitialAboutBlank */ false, locationFlags);
11082 // Make sure to store the referrer from the channel, if any
11083 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11084 if (httpChannel) {
11085 mReferrerInfo = httpChannel->GetReferrerInfo();
11087 return onLocationChangeNeeded;
11090 Maybe<Wireframe> nsDocShell::GetWireframe() {
11091 const bool collectWireFrame =
11092 mozilla::SessionHistoryInParent() &&
11093 StaticPrefs::browser_history_collectWireframes() &&
11094 mBrowsingContext->IsTopContent() && mActiveEntry;
11096 if (!collectWireFrame) {
11097 return Nothing();
11100 RefPtr<Document> doc = mDocumentViewer->GetDocument();
11101 Nullable<Wireframe> wireframe;
11102 doc->GetWireframeWithoutFlushing(false, wireframe);
11103 if (wireframe.IsNull()) {
11104 return Nothing();
11106 return Some(wireframe.Value());
11109 bool nsDocShell::CollectWireframe() {
11110 Maybe<Wireframe> wireframe = GetWireframe();
11111 if (wireframe.isNothing()) {
11112 return false;
11115 if (XRE_IsParentProcess()) {
11116 SessionHistoryEntry* entry =
11117 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11118 if (entry) {
11119 entry->SetWireframe(wireframe);
11121 } else {
11122 mozilla::Unused
11123 << ContentChild::GetSingleton()->SendSessionHistoryEntryWireframe(
11124 mBrowsingContext, wireframe.ref());
11127 return true;
11130 //*****************************************************************************
11131 // nsDocShell: Session History
11132 //*****************************************************************************
11134 NS_IMETHODIMP
11135 nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
11136 const nsAString& aURL, bool aReplace, JSContext* aCx) {
11137 MOZ_LOG(gSHLog, LogLevel::Debug,
11138 ("nsDocShell[%p]: AddState(..., %s, %s, %d)", this,
11139 NS_ConvertUTF16toUTF8(aTitle).get(),
11140 NS_ConvertUTF16toUTF8(aURL).get(), aReplace));
11141 // Implements History.pushState and History.replaceState
11143 // Here's what we do, roughly in the order specified by HTML5. The specific
11144 // steps we are executing are at
11145 // <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
11146 // and
11147 // <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
11148 // This function basically implements #dom-history-pushstate and
11149 // UpdateURLAndHistory implements #url-and-history-update-steps.
11151 // A. Serialize aData using structured clone. This is #dom-history-pushstate
11152 // step 5.
11153 // B. If the third argument is present, #dom-history-pushstate step 7.
11154 // 7.1. Resolve the url, relative to our document.
11155 // 7.2. If (a) fails, raise a SECURITY_ERR
11156 // 7.4. Compare the resulting absolute URL to the document's address. If
11157 // any part of the URLs difer other than the <path>, <query>, and
11158 // <fragment> components, raise a SECURITY_ERR and abort.
11159 // C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
11160 // Remove from the session history all entries after the current entry,
11161 // as we would after a regular navigation, and save the current
11162 // entry's scroll position (bug 590573).
11163 // D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
11164 // either add a state object entry to the session history after the
11165 // current entry with the following properties, or modify the current
11166 // session history entry to set
11167 // a. cloned data as the state object,
11168 // b. if the third argument was present, the absolute URL found in
11169 // step 2
11170 // Also clear the new history entry's POST data (see bug 580069).
11171 // E. If aReplace is false (i.e. we're doing a pushState instead of a
11172 // replaceState), notify bfcache that we've navigated to a new page.
11173 // F. If the third argument is present, set the document's current address
11174 // to the absolute URL found in step B. This is
11175 // #url-and-history-update-steps step 4.
11177 // It's important that this function not run arbitrary scripts after step A
11178 // and before completing step E. For example, if a script called
11179 // history.back() before we completed step E, bfcache might destroy an
11180 // active content viewer. Since EvictOutOfRangeDocumentViewers at the end of
11181 // step E might run script, we can't just put a script blocker around the
11182 // critical section.
11184 // Note that we completely ignore the aTitle parameter.
11186 nsresult rv;
11188 // Don't clobber the load type of an existing network load.
11189 AutoRestore<uint32_t> loadTypeResetter(mLoadType);
11191 // pushState effectively becomes replaceState when we've started a network
11192 // load but haven't adopted its document yet. This mirrors what we do with
11193 // changes to the hash at this stage of the game.
11194 if (JustStartedNetworkLoad()) {
11195 aReplace = true;
11198 RefPtr<Document> document = GetDocument();
11199 NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
11201 // Step A: Serialize aData using structured clone.
11202 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11203 // step 5.
11204 nsCOMPtr<nsIStructuredCloneContainer> scContainer;
11206 // scContainer->Init might cause arbitrary JS to run, and this code might
11207 // navigate the page we're on, potentially to a different origin! (bug
11208 // 634834) To protect against this, we abort if our principal changes due
11209 // to the InitFromJSVal() call.
11211 RefPtr<Document> origDocument = GetDocument();
11212 if (!origDocument) {
11213 return NS_ERROR_DOM_SECURITY_ERR;
11215 nsCOMPtr<nsIPrincipal> origPrincipal = origDocument->NodePrincipal();
11217 scContainer = new nsStructuredCloneContainer();
11218 rv = scContainer->InitFromJSVal(aData, aCx);
11219 NS_ENSURE_SUCCESS(rv, rv);
11221 RefPtr<Document> newDocument = GetDocument();
11222 if (!newDocument) {
11223 return NS_ERROR_DOM_SECURITY_ERR;
11225 nsCOMPtr<nsIPrincipal> newPrincipal = newDocument->NodePrincipal();
11227 bool principalsEqual = false;
11228 origPrincipal->Equals(newPrincipal, &principalsEqual);
11229 NS_ENSURE_TRUE(principalsEqual, NS_ERROR_DOM_SECURITY_ERR);
11232 // Check that the state object isn't too long.
11233 int32_t maxStateObjSize = StaticPrefs::browser_history_maxStateObjectSize();
11234 if (maxStateObjSize < 0) {
11235 maxStateObjSize = 0;
11238 uint64_t scSize;
11239 rv = scContainer->GetSerializedNBytes(&scSize);
11240 NS_ENSURE_SUCCESS(rv, rv);
11242 NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
11244 // Step B: Resolve aURL.
11245 // https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
11246 // step 7.
11247 bool equalURIs = true;
11248 nsCOMPtr<nsIURI> currentURI;
11249 if (mCurrentURI) {
11250 currentURI = nsIOService::CreateExposableURI(mCurrentURI);
11251 } else {
11252 currentURI = mCurrentURI;
11254 nsCOMPtr<nsIURI> newURI;
11255 if (aURL.Length() == 0) {
11256 newURI = currentURI;
11257 } else {
11258 // 7.1: Resolve aURL relative to mURI
11260 nsIURI* docBaseURI = document->GetDocBaseURI();
11261 if (!docBaseURI) {
11262 return NS_ERROR_FAILURE;
11265 nsAutoCString spec;
11266 docBaseURI->GetSpec(spec);
11268 rv = NS_NewURI(getter_AddRefs(newURI), aURL,
11269 document->GetDocumentCharacterSet(), docBaseURI);
11271 // 7.2: If 2a fails, raise a SECURITY_ERR
11272 if (NS_FAILED(rv)) {
11273 return NS_ERROR_DOM_SECURITY_ERR;
11276 // 7.4 and 7.5: Same-origin check.
11277 if (!nsContentUtils::URIIsLocalFile(newURI)) {
11278 // In addition to checking that the security manager says that
11279 // the new URI has the same origin as our current URI, we also
11280 // check that the two URIs have the same userpass. (The
11281 // security manager says that |http://foo.com| and
11282 // |http://me@foo.com| have the same origin.) currentURI
11283 // won't contain the password part of the userpass, so this
11284 // means that it's never valid to specify a password in a
11285 // pushState or replaceState URI.
11287 nsCOMPtr<nsIScriptSecurityManager> secMan =
11288 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
11289 NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
11291 // It's very important that we check that newURI is of the same
11292 // origin as currentURI, not docBaseURI, because a page can
11293 // set docBaseURI arbitrarily to any domain.
11294 nsAutoCString currentUserPass, newUserPass;
11295 NS_ENSURE_SUCCESS(currentURI->GetUserPass(currentUserPass),
11296 NS_ERROR_FAILURE);
11297 NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass), NS_ERROR_FAILURE);
11298 bool isPrivateWin =
11299 document->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
11300 if (NS_FAILED(secMan->CheckSameOriginURI(currentURI, newURI, true,
11301 isPrivateWin)) ||
11302 !currentUserPass.Equals(newUserPass)) {
11303 return NS_ERROR_DOM_SECURITY_ERR;
11305 } else {
11306 // It's a file:// URI
11307 nsCOMPtr<nsIPrincipal> principal = document->GetPrincipal();
11309 if (!principal || NS_FAILED(principal->CheckMayLoadWithReporting(
11310 newURI, false, document->InnerWindowID()))) {
11311 return NS_ERROR_DOM_SECURITY_ERR;
11315 if (currentURI) {
11316 currentURI->Equals(newURI, &equalURIs);
11317 } else {
11318 equalURIs = false;
11321 } // end of same-origin check
11323 // Step 8: call "URL and history update steps"
11324 rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
11325 currentURI, equalURIs);
11326 NS_ENSURE_SUCCESS(rv, rv);
11328 return NS_OK;
11331 nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
11332 nsIStructuredCloneContainer* aData,
11333 const nsAString& aTitle, bool aReplace,
11334 nsIURI* aCurrentURI, bool aEqualURIs) {
11335 // Implements
11336 // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
11338 // If we have a pending title change, handle it before creating a new entry.
11339 aDocument->DoNotifyPossibleTitleChange();
11341 // Step 2, if aReplace is false: Create a new entry in the session
11342 // history. This will erase all SHEntries after the new entry and make this
11343 // entry the current one. This operation may modify mOSHE, which we need
11344 // later, so we keep a reference here.
11345 NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE);
11346 nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
11348 // If this push/replaceState changed the document's current URI and the new
11349 // URI differs from the old URI in more than the hash, or if the old
11350 // SHEntry's URI was modified in this way by a push/replaceState call
11351 // set URIWasModified to true for the current SHEntry (bug 669671).
11352 bool sameExceptHashes = true;
11353 aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
11354 bool uriWasModified;
11355 if (sameExceptHashes) {
11356 if (mozilla::SessionHistoryInParent()) {
11357 uriWasModified = mActiveEntry && mActiveEntry->GetURIWasModified();
11358 } else {
11359 uriWasModified = oldOSHE && oldOSHE->GetURIWasModified();
11361 } else {
11362 uriWasModified = true;
11365 mLoadType = LOAD_PUSHSTATE;
11367 nsCOMPtr<nsISHEntry> newSHEntry;
11368 if (!aReplace) {
11369 // Step 2.
11371 // Step 2.2, "Remove any tasks queued by the history traversal task
11372 // source that are associated with any Document objects in the
11373 // top-level browsing context's document family." This is very hard in
11374 // SessionHistoryInParent since we can't synchronously access the
11375 // pending navigations that are already sent to the parent. We can
11376 // abort any AsyncGo navigations that are waiting to be sent. If we
11377 // send a message to the parent, it would be processed after any
11378 // navigations previously sent. So long as we consider the "history
11379 // traversal task source" to be the list in this process we match the
11380 // spec. If we move the entire list to the parent, we can handle the
11381 // aborting of loads there, but we don't have a way to synchronously
11382 // remove entries as we do here for non-SHIP.
11383 RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
11384 if (shistory) {
11385 shistory->RemovePendingHistoryNavigations();
11388 nsPoint scrollPos = GetCurScrollPos();
11390 bool scrollRestorationIsManual;
11391 if (mozilla::SessionHistoryInParent()) {
11392 // FIXME Need to save the current scroll position on mActiveEntry.
11393 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
11394 } else {
11395 // Save the current scroll position (bug 590573). Step 2.3.
11396 mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);
11398 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
11401 nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
11403 if (mozilla::SessionHistoryInParent()) {
11404 MOZ_LOG(gSHLog, LogLevel::Debug,
11405 ("nsDocShell %p UpdateActiveEntry (not replacing)", this));
11407 nsString title(mActiveEntry->GetTitle());
11408 nsCOMPtr<nsIReferrerInfo> referrerInfo = mActiveEntry->GetReferrerInfo();
11410 UpdateActiveEntry(false,
11411 /* aPreviousScrollPos = */ Some(scrollPos), aNewURI,
11412 /* aOriginalURI = */ nullptr,
11413 /* aReferrerInfo = */ referrerInfo,
11414 /* aTriggeringPrincipal = */ aDocument->NodePrincipal(),
11415 csp, title, scrollRestorationIsManual, aData,
11416 uriWasModified);
11417 } else {
11418 // Since we're not changing which page we have loaded, pass
11419 // true for aCloneChildren.
11420 nsresult rv = AddToSessionHistory(
11421 aNewURI, nullptr,
11422 aDocument->NodePrincipal(), // triggeringPrincipal
11423 nullptr, nullptr, csp, true, getter_AddRefs(newSHEntry));
11424 NS_ENSURE_SUCCESS(rv, rv);
11426 NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
11428 // Session history entries created by pushState inherit scroll restoration
11429 // mode from the current entry.
11430 newSHEntry->SetScrollRestorationIsManual(scrollRestorationIsManual);
11432 // Set the new SHEntry's title (bug 655273).
11433 nsString title;
11434 mOSHE->GetTitle(title);
11435 newSHEntry->SetTitle(title);
11437 nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo();
11438 newSHEntry->SetReferrerInfo(referrerInfo);
11440 // Link the new SHEntry to the old SHEntry's BFCache entry, since the
11441 // two entries correspond to the same document.
11442 NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
11443 NS_ERROR_FAILURE);
11445 // AddToSessionHistory may not modify mOSHE. In case it doesn't,
11446 // we'll just set mOSHE here.
11447 mOSHE = newSHEntry;
11449 } else if (mozilla::SessionHistoryInParent()) {
11450 MOZ_LOG(gSHLog, LogLevel::Debug,
11451 ("nsDocShell %p UpdateActiveEntry (replacing) mActiveEntry %p",
11452 this, mActiveEntry.get()));
11453 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11454 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11455 // in our case. We could also set it to aNewURI, with the same result.
11456 // We don't use aTitle here, see bug 544535.
11457 nsString title;
11458 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11459 if (mActiveEntry) {
11460 title = mActiveEntry->GetTitle();
11461 referrerInfo = mActiveEntry->GetReferrerInfo();
11462 } else {
11463 referrerInfo = nullptr;
11465 UpdateActiveEntry(
11466 true, /* aPreviousScrollPos = */ Nothing(), aNewURI, aNewURI,
11467 /* aReferrerInfo = */ referrerInfo, aDocument->NodePrincipal(),
11468 aDocument->GetCsp(), title,
11469 mActiveEntry && mActiveEntry->GetScrollRestorationIsManual(), aData,
11470 uriWasModified);
11471 } else {
11472 // Step 3.
11473 newSHEntry = mOSHE;
11475 MOZ_LOG(gSHLog, LogLevel::Debug, ("nsDocShell %p step 3", this));
11476 // Since we're not changing which page we have loaded, pass
11477 // true for aCloneChildren.
11478 if (!newSHEntry) {
11479 nsresult rv = AddToSessionHistory(
11480 aNewURI, nullptr,
11481 aDocument->NodePrincipal(), // triggeringPrincipal
11482 nullptr, nullptr, aDocument->GetCsp(), true,
11483 getter_AddRefs(newSHEntry));
11484 NS_ENSURE_SUCCESS(rv, rv);
11485 mOSHE = newSHEntry;
11488 nsCOMPtr<nsIReferrerInfo> referrerInfo = mOSHE->GetReferrerInfo();
11490 newSHEntry->SetURI(aNewURI);
11491 newSHEntry->SetOriginalURI(aNewURI);
11492 // We replaced the URI of the entry, clear the unstripped URI as it
11493 // shouldn't be used for reloads anymore.
11494 newSHEntry->SetUnstrippedURI(nullptr);
11495 // Setting the resultPrincipalURI to nullptr is fine here: it will cause
11496 // NS_GetFinalChannelURI to use the originalURI as the URI, which is aNewURI
11497 // in our case. We could also set it to aNewURI, with the same result.
11498 newSHEntry->SetResultPrincipalURI(nullptr);
11499 newSHEntry->SetLoadReplace(false);
11500 newSHEntry->SetReferrerInfo(referrerInfo);
11503 if (!mozilla::SessionHistoryInParent()) {
11504 // Step 2.4 and 3: Modify new/original session history entry and clear its
11505 // POST data, if there is any.
11506 newSHEntry->SetStateData(aData);
11507 newSHEntry->SetPostData(nullptr);
11509 newSHEntry->SetURIWasModified(uriWasModified);
11511 // Step E as described at the top of AddState: If aReplace is false,
11512 // indicating that we're doing a pushState rather than a replaceState,
11513 // notify bfcache that we've added a page to the history so it can evict
11514 // content viewers if appropriate. Otherwise call ReplaceEntry so that we
11515 // notify nsIHistoryListeners that an entry was replaced. We may not have a
11516 // root session history if this call is coming from a document.open() in a
11517 // docshell subtree that disables session history.
11518 RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
11519 if (rootSH) {
11520 rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry,
11521 aReplace);
11525 // Step 4: If the document's URI changed, update document's URI and update
11526 // global history.
11528 // We need to call FireOnLocationChange so that the browser's address bar
11529 // gets updated and the back button is enabled, but we only need to
11530 // explicitly call FireOnLocationChange if we're not calling SetCurrentURI,
11531 // since SetCurrentURI will call FireOnLocationChange for us.
11533 // Both SetCurrentURI(...) and FireDummyOnLocationChange() pass
11534 // nullptr for aRequest param to FireOnLocationChange(...). Such an update
11535 // notification is allowed only when we know docshell is not loading a new
11536 // document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
11537 // FireOnLocationChange(...) breaks security UI.
11539 // If the docshell is shutting down, don't update the document URI, as we
11540 // can't load into a docshell that is being destroyed.
11541 if (!aEqualURIs && !mIsBeingDestroyed) {
11542 aDocument->SetDocumentURI(aNewURI);
11543 SetCurrentURI(aNewURI, nullptr, /* aFireLocationChange */ true,
11544 /* aIsInitialAboutBlank */ false,
11545 GetSameDocumentNavigationFlags(aNewURI));
11547 AddURIVisit(aNewURI, aCurrentURI, 0);
11549 // AddURIVisit doesn't set the title for the new URI in global history,
11550 // so do that here.
11551 UpdateGlobalHistoryTitle(aNewURI);
11553 // Inform the favicon service that our old favicon applies to this new
11554 // URI.
11555 CopyFavicon(aCurrentURI, aNewURI, UsePrivateBrowsing());
11556 } else {
11557 FireDummyOnLocationChange();
11559 aDocument->SetStateObject(aData);
11561 return NS_OK;
11564 NS_IMETHODIMP
11565 nsDocShell::GetCurrentScrollRestorationIsManual(bool* aIsManual) {
11566 if (mozilla::SessionHistoryInParent()) {
11567 *aIsManual = mActiveEntry && mActiveEntry->GetScrollRestorationIsManual();
11568 return NS_OK;
11571 *aIsManual = false;
11572 if (mOSHE) {
11573 return mOSHE->GetScrollRestorationIsManual(aIsManual);
11576 return NS_OK;
11579 NS_IMETHODIMP
11580 nsDocShell::SetCurrentScrollRestorationIsManual(bool aIsManual) {
11581 SetScrollRestorationIsManualOnHistoryEntry(mOSHE, aIsManual);
11583 return NS_OK;
11586 void nsDocShell::SetScrollRestorationIsManualOnHistoryEntry(
11587 nsISHEntry* aSHEntry, bool aIsManual) {
11588 if (aSHEntry) {
11589 aSHEntry->SetScrollRestorationIsManual(aIsManual);
11592 if (mActiveEntry && mBrowsingContext) {
11593 mActiveEntry->SetScrollRestorationIsManual(aIsManual);
11594 if (XRE_IsParentProcess()) {
11595 SessionHistoryEntry* entry =
11596 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11597 if (entry) {
11598 entry->SetScrollRestorationIsManual(aIsManual);
11600 } else {
11601 mozilla::Unused << ContentChild::GetSingleton()
11602 ->SendSessionHistoryEntryScrollRestorationIsManual(
11603 mBrowsingContext, aIsManual);
11608 void nsDocShell::SetCacheKeyOnHistoryEntry(nsISHEntry* aSHEntry,
11609 uint32_t aCacheKey) {
11610 if (aSHEntry) {
11611 aSHEntry->SetCacheKey(aCacheKey);
11614 if (mActiveEntry && mBrowsingContext) {
11615 mActiveEntry->SetCacheKey(aCacheKey);
11616 if (XRE_IsParentProcess()) {
11617 SessionHistoryEntry* entry =
11618 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry();
11619 if (entry) {
11620 entry->SetCacheKey(aCacheKey);
11622 } else {
11623 mozilla::Unused
11624 << ContentChild::GetSingleton()->SendSessionHistoryEntryCacheKey(
11625 mBrowsingContext, aCacheKey);
11630 /* static */
11631 bool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI, nsIChannel* aChannel) {
11632 // I believe none of the about: urls should go in the history. But then
11633 // that could just be me... If the intent is only deny about:blank then we
11634 // should just do a spec compare, rather than two gets of the scheme and
11635 // then the path. -Gagan
11636 nsresult rv;
11637 nsAutoCString buf;
11639 rv = aURI->GetScheme(buf);
11640 if (NS_FAILED(rv)) {
11641 return false;
11644 if (buf.EqualsLiteral("about")) {
11645 rv = aURI->GetPathQueryRef(buf);
11646 if (NS_FAILED(rv)) {
11647 return false;
11650 if (buf.EqualsLiteral("blank")) {
11651 return false;
11653 // We only want to add about:newtab if it's not privileged, and
11654 // if it is not configured to show the blank page.
11655 if (buf.EqualsLiteral("newtab")) {
11656 if (!StaticPrefs::browser_newtabpage_enabled()) {
11657 return false;
11660 NS_ENSURE_TRUE(aChannel, false);
11661 nsCOMPtr<nsIPrincipal> resultPrincipal;
11662 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
11663 aChannel, getter_AddRefs(resultPrincipal));
11664 NS_ENSURE_SUCCESS(rv, false);
11665 return !resultPrincipal->IsSystemPrincipal();
11669 return true;
11672 nsresult nsDocShell::AddToSessionHistory(
11673 nsIURI* aURI, nsIChannel* aChannel, nsIPrincipal* aTriggeringPrincipal,
11674 nsIPrincipal* aPrincipalToInherit,
11675 nsIPrincipal* aPartitionedPrincipalToInherit,
11676 nsIContentSecurityPolicy* aCsp, bool aCloneChildren,
11677 nsISHEntry** aNewEntry) {
11678 MOZ_ASSERT(aURI, "uri is null");
11679 MOZ_ASSERT(!aChannel || !aTriggeringPrincipal, "Shouldn't have both set");
11680 MOZ_DIAGNOSTIC_ASSERT(!mozilla::SessionHistoryInParent());
11682 #if defined(DEBUG)
11683 if (MOZ_LOG_TEST(gDocShellLog, LogLevel::Debug)) {
11684 nsAutoCString chanName;
11685 if (aChannel) {
11686 aChannel->GetName(chanName);
11687 } else {
11688 chanName.AssignLiteral("<no channel>");
11691 MOZ_LOG(gDocShellLog, LogLevel::Debug,
11692 ("nsDocShell[%p]::AddToSessionHistory(\"%s\", [%s])\n", this,
11693 aURI->GetSpecOrDefault().get(), chanName.get()));
11695 #endif
11697 nsresult rv = NS_OK;
11698 nsCOMPtr<nsISHEntry> entry;
11701 * If this is a LOAD_FLAGS_REPLACE_HISTORY in a subframe, we use
11702 * the existing SH entry in the page and replace the url and
11703 * other vitalities.
11705 if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY) &&
11706 !mBrowsingContext->IsTop()) {
11707 // This is a subframe
11708 entry = mOSHE;
11709 if (entry) {
11710 entry->ClearEntry();
11714 // Create a new entry if necessary.
11715 if (!entry) {
11716 entry = new nsSHEntry();
11719 // Get the post data & referrer
11720 nsCOMPtr<nsIInputStream> inputStream;
11721 nsCOMPtr<nsIURI> originalURI;
11722 nsCOMPtr<nsIURI> resultPrincipalURI;
11723 nsCOMPtr<nsIURI> unstrippedURI;
11724 bool loadReplace = false;
11725 nsCOMPtr<nsIReferrerInfo> referrerInfo;
11726 uint32_t cacheKey = 0;
11727 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
11728 nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
11729 nsCOMPtr<nsIPrincipal> partitionedPrincipalToInherit =
11730 aPartitionedPrincipalToInherit;
11731 nsCOMPtr<nsIContentSecurityPolicy> csp = aCsp;
11732 bool expired = false; // by default the page is not expired
11733 bool discardLayoutState = false;
11734 nsCOMPtr<nsICacheInfoChannel> cacheChannel;
11735 bool userActivation = false;
11737 if (aChannel) {
11738 cacheChannel = do_QueryInterface(aChannel);
11740 /* If there is a caching channel, get the Cache Key and store it
11741 * in SH.
11743 if (cacheChannel) {
11744 cacheChannel->GetCacheKey(&cacheKey);
11746 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
11748 // Check if the httpChannel is hiding under a multipartChannel
11749 if (!httpChannel) {
11750 GetHttpChannel(aChannel, getter_AddRefs(httpChannel));
11752 if (httpChannel) {
11753 nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
11754 if (uploadChannel) {
11755 uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
11757 httpChannel->GetOriginalURI(getter_AddRefs(originalURI));
11758 uint32_t loadFlags;
11759 aChannel->GetLoadFlags(&loadFlags);
11760 loadReplace = loadFlags & nsIChannel::LOAD_REPLACE;
11761 rv = httpChannel->GetReferrerInfo(getter_AddRefs(referrerInfo));
11762 MOZ_ASSERT(NS_SUCCEEDED(rv));
11764 discardLayoutState = ShouldDiscardLayoutState(httpChannel);
11767 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
11768 if (!triggeringPrincipal) {
11769 triggeringPrincipal = loadInfo->TriggeringPrincipal();
11771 if (!csp) {
11772 csp = loadInfo->GetCspToInherit();
11775 loadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI));
11777 loadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
11779 userActivation = loadInfo->GetHasValidUserGestureActivation();
11781 // For now keep storing just the principal in the SHEntry.
11782 if (!principalToInherit) {
11783 if (loadInfo->GetLoadingSandboxed()) {
11784 if (loadInfo->GetLoadingPrincipal()) {
11785 principalToInherit = NullPrincipal::CreateWithInheritedAttributes(
11786 loadInfo->GetLoadingPrincipal());
11787 } else {
11788 // get the OriginAttributes
11789 OriginAttributes attrs;
11790 loadInfo->GetOriginAttributes(&attrs);
11791 principalToInherit = NullPrincipal::Create(attrs);
11793 } else {
11794 principalToInherit = loadInfo->PrincipalToInherit();
11798 if (!partitionedPrincipalToInherit) {
11799 // XXXehsan is it correct to fall back to the principal to inherit in all
11800 // cases? For example, what about the cases where we are using the load
11801 // info's principal to inherit? Do we need to add a similar concept to
11802 // load info for partitioned principal?
11803 partitionedPrincipalToInherit = principalToInherit;
11807 nsAutoString srcdoc;
11808 bool srcdocEntry = false;
11809 nsCOMPtr<nsIURI> baseURI;
11811 nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
11812 if (inStrmChan) {
11813 bool isSrcdocChannel;
11814 inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
11815 if (isSrcdocChannel) {
11816 inStrmChan->GetSrcdocData(srcdoc);
11817 srcdocEntry = true;
11818 inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
11819 } else {
11820 srcdoc.SetIsVoid(true);
11823 /* If cache got a 'no-store', ask SH not to store
11824 * HistoryLayoutState. By default, SH will set this
11825 * flag to true and save HistoryLayoutState.
11827 bool saveLayoutState = !discardLayoutState;
11829 if (cacheChannel) {
11830 // Check if the page has expired from cache
11831 uint32_t expTime = 0;
11832 cacheChannel->GetCacheTokenExpirationTime(&expTime);
11833 uint32_t now = PRTimeToSeconds(PR_Now());
11834 if (expTime <= now) {
11835 expired = true;
11839 // Title is set in nsDocShell::SetTitle()
11840 entry->Create(aURI, // uri
11841 u""_ns, // Title
11842 inputStream, // Post data stream
11843 cacheKey, // CacheKey
11844 mContentTypeHint, // Content-type
11845 triggeringPrincipal, // Channel or provided principal
11846 principalToInherit, partitionedPrincipalToInherit, csp,
11847 HistoryID(), GetCreatedDynamically(), originalURI,
11848 resultPrincipalURI, unstrippedURI, loadReplace, referrerInfo,
11849 srcdoc, srcdocEntry, baseURI, saveLayoutState, expired,
11850 userActivation);
11852 if (mBrowsingContext->IsTop() && GetSessionHistory()) {
11853 bool shouldPersist = ShouldAddToSessionHistory(aURI, aChannel);
11854 Maybe<int32_t> previousEntryIndex;
11855 Maybe<int32_t> loadedEntryIndex;
11856 rv = GetSessionHistory()->LegacySHistory()->AddToRootSessionHistory(
11857 aCloneChildren, mOSHE, mBrowsingContext, entry, mLoadType,
11858 shouldPersist, &previousEntryIndex, &loadedEntryIndex);
11860 MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not add entry to root session history");
11861 if (previousEntryIndex.isSome()) {
11862 mPreviousEntryIndex = previousEntryIndex.value();
11864 if (loadedEntryIndex.isSome()) {
11865 mLoadedEntryIndex = loadedEntryIndex.value();
11868 // aCloneChildren implies that we are retaining the same document, thus we
11869 // need to signal to the top WC that the new SHEntry may receive a fresh
11870 // user interaction flag.
11871 if (aCloneChildren) {
11872 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11873 if (topWc && !topWc->IsDiscarded()) {
11874 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11877 } else {
11878 // This is a subframe, make sure that this new SHEntry will be
11879 // marked with user interaction.
11880 WindowContext* topWc = mBrowsingContext->GetTopWindowContext();
11881 if (topWc && !topWc->IsDiscarded()) {
11882 MOZ_ALWAYS_SUCCEEDS(topWc->SetSHEntryHasUserInteraction(false));
11884 if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
11885 rv = AddChildSHEntryToParent(entry, mBrowsingContext->ChildOffset(),
11886 aCloneChildren);
11890 // Return the new SH entry...
11891 if (aNewEntry) {
11892 *aNewEntry = nullptr;
11893 if (NS_SUCCEEDED(rv)) {
11894 entry.forget(aNewEntry);
11898 return rv;
11901 void nsDocShell::UpdateActiveEntry(
11902 bool aReplace, const Maybe<nsPoint>& aPreviousScrollPos, nsIURI* aURI,
11903 nsIURI* aOriginalURI, nsIReferrerInfo* aReferrerInfo,
11904 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
11905 const nsAString& aTitle, bool aScrollRestorationIsManual,
11906 nsIStructuredCloneContainer* aData, bool aURIWasModified) {
11907 MOZ_ASSERT(mozilla::SessionHistoryInParent());
11908 MOZ_ASSERT(aURI, "uri is null");
11909 MOZ_ASSERT(mLoadType == LOAD_PUSHSTATE,
11910 "This code only deals with pushState");
11911 MOZ_ASSERT_IF(aPreviousScrollPos.isSome(), !aReplace);
11913 MOZ_LOG(gSHLog, LogLevel::Debug,
11914 ("Creating an active entry on nsDocShell %p to %s", this,
11915 aURI->GetSpecOrDefault().get()));
11917 // Even if we're replacing an existing entry we create new a
11918 // SessionHistoryInfo. In the parent process we'll keep the existing
11919 // SessionHistoryEntry, but just replace its SessionHistoryInfo, that way the
11920 // entry keeps identity but its data is replaced.
11921 bool replace = aReplace && mActiveEntry;
11923 if (!replace) {
11924 CollectWireframe();
11927 if (mActiveEntry) {
11928 // Link this entry to the previous active entry.
11929 mActiveEntry = MakeUnique<SessionHistoryInfo>(*mActiveEntry, aURI);
11930 } else {
11931 mActiveEntry = MakeUnique<SessionHistoryInfo>(
11932 aURI, aTriggeringPrincipal, nullptr, nullptr, aCsp, mContentTypeHint);
11934 mActiveEntry->SetOriginalURI(aOriginalURI);
11935 mActiveEntry->SetUnstrippedURI(nullptr);
11936 mActiveEntry->SetReferrerInfo(aReferrerInfo);
11937 mActiveEntry->SetTitle(aTitle);
11938 mActiveEntry->SetStateData(static_cast<nsStructuredCloneContainer*>(aData));
11939 mActiveEntry->SetURIWasModified(aURIWasModified);
11940 mActiveEntry->SetScrollRestorationIsManual(aScrollRestorationIsManual);
11942 if (replace) {
11943 mBrowsingContext->ReplaceActiveSessionHistoryEntry(mActiveEntry.get());
11944 } else {
11945 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
11946 // FIXME We should probably just compute mChildOffset in the parent
11947 // instead of passing it over IPC here.
11948 mBrowsingContext->SetActiveSessionHistoryEntry(
11949 aPreviousScrollPos, mActiveEntry.get(), mLoadType,
11950 /* aCacheKey = */ 0);
11951 // FIXME Do we need to update mPreviousEntryIndex and mLoadedEntryIndex?
11955 nsresult nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType,
11956 bool aUserActivation) {
11957 NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
11959 nsresult rv;
11960 RefPtr<nsDocShellLoadState> loadState;
11961 rv = aEntry->CreateLoadInfo(getter_AddRefs(loadState));
11962 NS_ENSURE_SUCCESS(rv, rv);
11964 // Calling CreateAboutBlankDocumentViewer can set mOSHE to null, and if
11965 // that's the only thing holding a ref to aEntry that will cause aEntry to
11966 // die while we're loading it. So hold a strong ref to aEntry here, just
11967 // in case.
11968 nsCOMPtr<nsISHEntry> kungFuDeathGrip(aEntry);
11970 loadState->SetHasValidUserGestureActivation(
11971 loadState->HasValidUserGestureActivation() || aUserActivation);
11973 loadState->SetTextDirectiveUserActivation(
11974 loadState->GetTextDirectiveUserActivation() || aUserActivation);
11976 return LoadHistoryEntry(loadState, aLoadType, aEntry == mOSHE);
11979 nsresult nsDocShell::LoadHistoryEntry(const LoadingSessionHistoryInfo& aEntry,
11980 uint32_t aLoadType,
11981 bool aUserActivation) {
11982 RefPtr<nsDocShellLoadState> loadState = aEntry.CreateLoadInfo();
11983 loadState->SetHasValidUserGestureActivation(
11984 loadState->HasValidUserGestureActivation() || aUserActivation);
11986 loadState->SetTextDirectiveUserActivation(
11987 loadState->GetTextDirectiveUserActivation() || aUserActivation);
11989 return LoadHistoryEntry(loadState, aLoadType, aEntry.mLoadingCurrentEntry);
11992 nsresult nsDocShell::LoadHistoryEntry(nsDocShellLoadState* aLoadState,
11993 uint32_t aLoadType,
11994 bool aLoadingCurrentEntry) {
11995 if (!IsNavigationAllowed()) {
11996 return NS_OK;
11999 // We are setting load type afterwards so we don't have to
12000 // send it in an IPC message
12001 aLoadState->SetLoadType(aLoadType);
12003 nsresult rv;
12004 if (SchemeIsJavascript(aLoadState->URI())) {
12005 // We're loading a URL that will execute script from inside asyncOpen.
12006 // Replace the current document with about:blank now to prevent
12007 // anything from the current document from leaking into any JavaScript
12008 // code in the URL.
12009 // Don't cache the presentation if we're going to just reload the
12010 // current entry. Caching would lead to trying to save the different
12011 // content viewers in the same nsISHEntry object.
12012 rv = CreateAboutBlankDocumentViewer(
12013 aLoadState->PrincipalToInherit(),
12014 aLoadState->PartitionedPrincipalToInherit(), nullptr, nullptr,
12015 /* aIsInitialDocument */ false, Nothing(), !aLoadingCurrentEntry);
12017 if (NS_FAILED(rv)) {
12018 // The creation of the intermittent about:blank content
12019 // viewer failed for some reason (potentially because the
12020 // user prevented it). Interrupt the history load.
12021 return NS_OK;
12024 if (!aLoadState->TriggeringPrincipal()) {
12025 // Ensure that we have a triggeringPrincipal. Otherwise javascript:
12026 // URIs will pick it up from the about:blank page we just loaded,
12027 // and we don't really want even that in this case.
12028 nsCOMPtr<nsIPrincipal> principal =
12029 NullPrincipal::Create(GetOriginAttributes());
12030 aLoadState->SetTriggeringPrincipal(principal);
12034 /* If there is a valid postdata *and* the user pressed
12035 * reload or shift-reload, take user's permission before we
12036 * repost the data to the server.
12038 if ((aLoadType & LOAD_CMD_RELOAD) && aLoadState->PostDataStream()) {
12039 bool repost;
12040 rv = ConfirmRepost(&repost);
12041 if (NS_FAILED(rv)) {
12042 return rv;
12045 // If the user pressed cancel in the dialog, return. We're done here.
12046 if (!repost) {
12047 return NS_BINDING_ABORTED;
12051 // If there is no valid triggeringPrincipal, we deny the load
12052 MOZ_ASSERT(aLoadState->TriggeringPrincipal(),
12053 "need a valid triggeringPrincipal to load from history");
12054 if (!aLoadState->TriggeringPrincipal()) {
12055 return NS_ERROR_FAILURE;
12058 return InternalLoad(aLoadState); // No nsIRequest
12061 NS_IMETHODIMP
12062 nsDocShell::PersistLayoutHistoryState() {
12063 nsresult rv = NS_OK;
12065 if (mozilla::SessionHistoryInParent() ? !!mActiveEntry : !!mOSHE) {
12066 bool scrollRestorationIsManual;
12067 if (mozilla::SessionHistoryInParent()) {
12068 scrollRestorationIsManual = mActiveEntry->GetScrollRestorationIsManual();
12069 } else {
12070 scrollRestorationIsManual = mOSHE->GetScrollRestorationIsManual();
12072 nsCOMPtr<nsILayoutHistoryState> layoutState;
12073 if (RefPtr<PresShell> presShell = GetPresShell()) {
12074 rv = presShell->CaptureHistoryState(getter_AddRefs(layoutState));
12075 } else if (scrollRestorationIsManual) {
12076 // Even if we don't have layout anymore, we may want to reset the
12077 // current scroll state in layout history.
12078 GetLayoutHistoryState(getter_AddRefs(layoutState));
12081 if (scrollRestorationIsManual && layoutState) {
12082 layoutState->ResetScrollState();
12086 return rv;
12089 void nsDocShell::SwapHistoryEntries(nsISHEntry* aOldEntry,
12090 nsISHEntry* aNewEntry) {
12091 if (aOldEntry == mOSHE) {
12092 mOSHE = aNewEntry;
12095 if (aOldEntry == mLSHE) {
12096 mLSHE = aNewEntry;
12100 void nsDocShell::SetHistoryEntryAndUpdateBC(const Maybe<nsISHEntry*>& aLSHE,
12101 const Maybe<nsISHEntry*>& aOSHE) {
12102 // We want to hold on to the reference in mLSHE before we update it.
12103 // Otherwise, SetHistoryEntry could release the last reference to
12104 // the entry while aOSHE is pointing to it.
12105 nsCOMPtr<nsISHEntry> deathGripOldLSHE;
12106 if (aLSHE.isSome()) {
12107 deathGripOldLSHE = SetHistoryEntry(&mLSHE, aLSHE.value());
12108 MOZ_ASSERT(mLSHE.get() == aLSHE.value());
12110 nsCOMPtr<nsISHEntry> deathGripOldOSHE;
12111 if (aOSHE.isSome()) {
12112 deathGripOldOSHE = SetHistoryEntry(&mOSHE, aOSHE.value());
12113 MOZ_ASSERT(mOSHE.get() == aOSHE.value());
12117 already_AddRefed<nsISHEntry> nsDocShell::SetHistoryEntry(
12118 nsCOMPtr<nsISHEntry>* aPtr, nsISHEntry* aEntry) {
12119 // We need to sync up the docshell and session history trees for
12120 // subframe navigation. If the load was in a subframe, we forward up to
12121 // the root docshell, which will then recursively sync up all docshells
12122 // to their corresponding entries in the new session history tree.
12123 // If we don't do this, then we can cache a content viewer on the wrong
12124 // cloned entry, and subsequently restore it at the wrong time.
12125 RefPtr<BrowsingContext> topBC = mBrowsingContext->Top();
12126 if (topBC->IsDiscarded()) {
12127 topBC = nullptr;
12129 RefPtr<BrowsingContext> currBC =
12130 mBrowsingContext->IsDiscarded() ? nullptr : mBrowsingContext;
12131 if (topBC && *aPtr) {
12132 (*aPtr)->SyncTreesForSubframeNavigation(aEntry, topBC, currBC);
12134 nsCOMPtr<nsISHEntry> entry(aEntry);
12135 entry.swap(*aPtr);
12136 return entry.forget();
12139 already_AddRefed<ChildSHistory> nsDocShell::GetRootSessionHistory() {
12140 RefPtr<ChildSHistory> childSHistory =
12141 mBrowsingContext->Top()->GetChildSessionHistory();
12142 return childSHistory.forget();
12145 nsresult nsDocShell::GetHttpChannel(nsIChannel* aChannel,
12146 nsIHttpChannel** aReturn) {
12147 NS_ENSURE_ARG_POINTER(aReturn);
12148 if (!aChannel) {
12149 return NS_ERROR_FAILURE;
12152 nsCOMPtr<nsIMultiPartChannel> multiPartChannel(do_QueryInterface(aChannel));
12153 if (multiPartChannel) {
12154 nsCOMPtr<nsIChannel> baseChannel;
12155 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
12156 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(baseChannel));
12157 *aReturn = httpChannel;
12158 NS_IF_ADDREF(*aReturn);
12160 return NS_OK;
12163 bool nsDocShell::ShouldDiscardLayoutState(nsIHttpChannel* aChannel) {
12164 // By default layout State will be saved.
12165 if (!aChannel) {
12166 return false;
12169 // figure out if SH should be saving layout state
12170 bool noStore = false;
12171 Unused << aChannel->IsNoStoreResponse(&noStore);
12172 return noStore;
12175 NS_IMETHODIMP
12176 nsDocShell::GetEditor(nsIEditor** aEditor) {
12177 NS_ENSURE_ARG_POINTER(aEditor);
12178 RefPtr<HTMLEditor> htmlEditor = GetHTMLEditorInternal();
12179 htmlEditor.forget(aEditor);
12180 return NS_OK;
12183 NS_IMETHODIMP
12184 nsDocShell::SetEditor(nsIEditor* aEditor) {
12185 HTMLEditor* htmlEditor = aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
12186 // If TextEditor comes, throw an error.
12187 if (aEditor && !htmlEditor) {
12188 return NS_ERROR_INVALID_ARG;
12190 return SetHTMLEditorInternal(htmlEditor);
12193 HTMLEditor* nsDocShell::GetHTMLEditorInternal() {
12194 return mEditorData ? mEditorData->GetHTMLEditor() : nullptr;
12197 nsresult nsDocShell::SetHTMLEditorInternal(HTMLEditor* aHTMLEditor) {
12198 if (!aHTMLEditor && !mEditorData) {
12199 return NS_OK;
12202 nsresult rv = EnsureEditorData();
12203 if (NS_FAILED(rv)) {
12204 return rv;
12207 return mEditorData->SetHTMLEditor(aHTMLEditor);
12210 NS_IMETHODIMP
12211 nsDocShell::GetEditable(bool* aEditable) {
12212 NS_ENSURE_ARG_POINTER(aEditable);
12213 *aEditable = mEditorData && mEditorData->GetEditable();
12214 return NS_OK;
12217 NS_IMETHODIMP
12218 nsDocShell::GetHasEditingSession(bool* aHasEditingSession) {
12219 NS_ENSURE_ARG_POINTER(aHasEditingSession);
12221 if (mEditorData) {
12222 *aHasEditingSession = !!mEditorData->GetEditingSession();
12223 } else {
12224 *aHasEditingSession = false;
12227 return NS_OK;
12230 NS_IMETHODIMP
12231 nsDocShell::MakeEditable(bool aInWaitForUriLoad) {
12232 nsresult rv = EnsureEditorData();
12233 if (NS_FAILED(rv)) {
12234 return rv;
12237 return mEditorData->MakeEditable(aInWaitForUriLoad);
12240 /* static */ bool nsDocShell::ShouldAddURIVisit(nsIChannel* aChannel) {
12241 bool needToAddURIVisit = true;
12242 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12243 if (props) {
12244 mozilla::Unused << props->GetPropertyAsBool(
12245 u"docshell.needToAddURIVisit"_ns, &needToAddURIVisit);
12248 return needToAddURIVisit;
12251 /* static */ void nsDocShell::ExtractLastVisit(
12252 nsIChannel* aChannel, nsIURI** aURI, uint32_t* aChannelRedirectFlags) {
12253 nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(aChannel));
12254 if (!props) {
12255 return;
12258 nsresult rv;
12259 nsCOMPtr<nsIURI> uri(do_GetProperty(props, u"docshell.previousURI"_ns, &rv));
12260 if (NS_SUCCEEDED(rv)) {
12261 uri.forget(aURI);
12263 rv = props->GetPropertyAsUint32(u"docshell.previousFlags"_ns,
12264 aChannelRedirectFlags);
12266 NS_WARNING_ASSERTION(
12267 NS_SUCCEEDED(rv),
12268 "Could not fetch previous flags, URI will be treated like referrer");
12270 } else {
12271 // There is no last visit for this channel, so this must be the first
12272 // link. Link the visit to the referrer of this request, if any.
12273 // Treat referrer as null if there is an error getting it.
12274 NS_GetReferrerFromChannel(aChannel, aURI);
12278 void nsDocShell::SaveLastVisit(nsIChannel* aChannel, nsIURI* aURI,
12279 uint32_t aChannelRedirectFlags) {
12280 nsCOMPtr<nsIWritablePropertyBag2> props(do_QueryInterface(aChannel));
12281 if (!props || !aURI) {
12282 return;
12285 props->SetPropertyAsInterface(u"docshell.previousURI"_ns, aURI);
12286 props->SetPropertyAsUint32(u"docshell.previousFlags"_ns,
12287 aChannelRedirectFlags);
12290 /* static */ void nsDocShell::InternalAddURIVisit(
12291 nsIURI* aURI, nsIURI* aPreviousURI, uint32_t aChannelRedirectFlags,
12292 uint32_t aResponseStatus, BrowsingContext* aBrowsingContext,
12293 nsIWidget* aWidget, uint32_t aLoadType, bool aWasUpgraded) {
12294 MOZ_ASSERT(aURI, "Visited URI is null!");
12295 MOZ_ASSERT(aLoadType != LOAD_ERROR_PAGE && aLoadType != LOAD_BYPASS_HISTORY,
12296 "Do not add error or bypass pages to global history");
12298 bool usePrivateBrowsing = false;
12299 aBrowsingContext->GetUsePrivateBrowsing(&usePrivateBrowsing);
12301 // Only content-type docshells save URI visits. Also don't do
12302 // anything here if we're not supposed to use global history.
12303 if (!aBrowsingContext->IsContent() ||
12304 !aBrowsingContext->GetUseGlobalHistory() || usePrivateBrowsing) {
12305 return;
12308 nsCOMPtr<IHistory> history = components::History::Service();
12310 if (history) {
12311 uint32_t visitURIFlags = 0;
12313 if (aBrowsingContext->IsTop()) {
12314 visitURIFlags |= IHistory::TOP_LEVEL;
12317 if (aChannelRedirectFlags & nsIChannelEventSink::REDIRECT_TEMPORARY) {
12318 visitURIFlags |= IHistory::REDIRECT_TEMPORARY;
12319 } else if (aChannelRedirectFlags &
12320 nsIChannelEventSink::REDIRECT_PERMANENT) {
12321 visitURIFlags |= IHistory::REDIRECT_PERMANENT;
12322 } else {
12323 MOZ_ASSERT(!aChannelRedirectFlags,
12324 "One of REDIRECT_TEMPORARY or REDIRECT_PERMANENT must be set "
12325 "if any flags in aChannelRedirectFlags is set.");
12328 if (aResponseStatus >= 300 && aResponseStatus < 400) {
12329 visitURIFlags |= IHistory::REDIRECT_SOURCE;
12330 if (aResponseStatus == 301 || aResponseStatus == 308) {
12331 visitURIFlags |= IHistory::REDIRECT_SOURCE_PERMANENT;
12334 // Errors 400-501 and 505 are considered unrecoverable, in the sense a
12335 // simple retry attempt by the user is unlikely to solve them.
12336 // 408 is special cased, since may actually indicate a temporary
12337 // connection problem.
12338 else if (aResponseStatus != 408 &&
12339 ((aResponseStatus >= 400 && aResponseStatus <= 501) ||
12340 aResponseStatus == 505)) {
12341 visitURIFlags |= IHistory::UNRECOVERABLE_ERROR;
12344 if (aWasUpgraded) {
12345 visitURIFlags |=
12346 IHistory::REDIRECT_SOURCE | IHistory::REDIRECT_SOURCE_UPGRADED;
12349 mozilla::Unused << history->VisitURI(aWidget, aURI, aPreviousURI,
12350 visitURIFlags,
12351 aBrowsingContext->BrowserId());
12355 void nsDocShell::AddURIVisit(nsIURI* aURI, nsIURI* aPreviousURI,
12356 uint32_t aChannelRedirectFlags,
12357 uint32_t aResponseStatus) {
12358 nsPIDOMWindowOuter* outer = GetWindow();
12359 nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(outer);
12361 InternalAddURIVisit(aURI, aPreviousURI, aChannelRedirectFlags,
12362 aResponseStatus, mBrowsingContext, widget, mLoadType,
12363 false);
12366 //*****************************************************************************
12367 // nsDocShell: Helper Routines
12368 //*****************************************************************************
12370 NS_IMETHODIMP
12371 nsDocShell::SetLoadType(uint32_t aLoadType) {
12372 mLoadType = aLoadType;
12373 return NS_OK;
12376 NS_IMETHODIMP
12377 nsDocShell::GetLoadType(uint32_t* aLoadType) {
12378 *aLoadType = mLoadType;
12379 return NS_OK;
12382 nsresult nsDocShell::ConfirmRepost(bool* aRepost) {
12383 if (StaticPrefs::dom_confirm_repost_testing_always_accept()) {
12384 *aRepost = true;
12385 return NS_OK;
12388 nsCOMPtr<nsIPromptCollection> prompter =
12389 do_GetService("@mozilla.org/embedcomp/prompt-collection;1");
12390 if (!prompter) {
12391 return NS_ERROR_NOT_AVAILABLE;
12394 return prompter->ConfirmRepost(mBrowsingContext, aRepost);
12397 nsresult nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
12398 nsIStringBundle** aStringBundle) {
12399 NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt),
12400 NS_ERROR_FAILURE);
12402 nsCOMPtr<nsIStringBundleService> stringBundleService =
12403 mozilla::components::StringBundle::Service();
12404 NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);
12406 NS_ENSURE_SUCCESS(
12407 stringBundleService->CreateBundle(kAppstringsBundleURL, aStringBundle),
12408 NS_ERROR_FAILURE);
12410 return NS_OK;
12413 ScrollContainerFrame* nsDocShell::GetRootScrollContainerFrame() {
12414 PresShell* presShell = GetPresShell();
12415 NS_ENSURE_TRUE(presShell, nullptr);
12417 return presShell->GetRootScrollContainerFrame();
12420 nsresult nsDocShell::EnsureScriptEnvironment() {
12421 if (mScriptGlobal) {
12422 return NS_OK;
12425 if (mIsBeingDestroyed) {
12426 return NS_ERROR_NOT_AVAILABLE;
12429 #ifdef DEBUG
12430 NS_ASSERTION(!mInEnsureScriptEnv,
12431 "Infinite loop! Calling EnsureScriptEnvironment() from "
12432 "within EnsureScriptEnvironment()!");
12434 // Yeah, this isn't re-entrant safe, but that's ok since if we
12435 // re-enter this method, we'll infinitely loop...
12436 AutoRestore<bool> boolSetter(mInEnsureScriptEnv);
12437 mInEnsureScriptEnv = true;
12438 #endif
12440 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
12441 NS_ENSURE_TRUE(browserChrome, NS_ERROR_NOT_AVAILABLE);
12443 uint32_t chromeFlags;
12444 browserChrome->GetChromeFlags(&chromeFlags);
12446 // If our window is modal and we're not opened as chrome, make
12447 // this window a modal content window.
12448 mScriptGlobal = nsGlobalWindowOuter::Create(this, mItemType == typeChrome);
12449 MOZ_ASSERT(mScriptGlobal);
12451 // Ensure the script object is set up to run script.
12452 return mScriptGlobal->EnsureScriptEnvironment();
12455 nsresult nsDocShell::EnsureEditorData() {
12456 MOZ_ASSERT(!mIsBeingDestroyed);
12458 bool openDocHasDetachedEditor = mOSHE && mOSHE->HasDetachedEditor();
12459 if (!mEditorData && !mIsBeingDestroyed && !openDocHasDetachedEditor) {
12460 // We shouldn't recreate the editor data if it already exists, or
12461 // we're shutting down, or we already have a detached editor data
12462 // stored in the session history. We should only have one editordata
12463 // per docshell.
12464 mEditorData = MakeUnique<nsDocShellEditorData>(this);
12467 return mEditorData ? NS_OK : NS_ERROR_NOT_AVAILABLE;
12470 nsresult nsDocShell::EnsureFind() {
12471 if (!mFind) {
12472 mFind = new nsWebBrowserFind();
12475 // we promise that the nsIWebBrowserFind that we return has been set
12476 // up to point to the focused, or content window, so we have to
12477 // set that up each time.
12479 nsIScriptGlobalObject* scriptGO = GetScriptGlobalObject();
12480 NS_ENSURE_TRUE(scriptGO, NS_ERROR_UNEXPECTED);
12482 // default to our window
12483 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_QueryInterface(scriptGO);
12484 nsCOMPtr<nsPIDOMWindowOuter> windowToSearch;
12485 nsFocusManager::GetFocusedDescendant(ourWindow,
12486 nsFocusManager::eIncludeAllDescendants,
12487 getter_AddRefs(windowToSearch));
12489 nsCOMPtr<nsIWebBrowserFindInFrames> findInFrames = do_QueryInterface(mFind);
12490 if (!findInFrames) {
12491 return NS_ERROR_NO_INTERFACE;
12494 nsresult rv = findInFrames->SetRootSearchFrame(ourWindow);
12495 if (NS_FAILED(rv)) {
12496 return rv;
12498 rv = findInFrames->SetCurrentSearchFrame(windowToSearch);
12499 if (NS_FAILED(rv)) {
12500 return rv;
12503 return NS_OK;
12506 NS_IMETHODIMP
12507 nsDocShell::IsBeingDestroyed(bool* aDoomed) {
12508 NS_ENSURE_ARG(aDoomed);
12509 *aDoomed = mIsBeingDestroyed;
12510 return NS_OK;
12513 NS_IMETHODIMP
12514 nsDocShell::GetIsExecutingOnLoadHandler(bool* aResult) {
12515 NS_ENSURE_ARG(aResult);
12516 *aResult = mIsExecutingOnLoadHandler;
12517 return NS_OK;
12520 NS_IMETHODIMP
12521 nsDocShell::GetLayoutHistoryState(nsILayoutHistoryState** aLayoutHistoryState) {
12522 nsCOMPtr<nsILayoutHistoryState> state;
12523 if (mozilla::SessionHistoryInParent()) {
12524 if (mActiveEntry) {
12525 state = mActiveEntry->GetLayoutHistoryState();
12527 } else {
12528 if (mOSHE) {
12529 state = mOSHE->GetLayoutHistoryState();
12532 state.forget(aLayoutHistoryState);
12533 return NS_OK;
12536 NS_IMETHODIMP
12537 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState) {
12538 if (mOSHE) {
12539 mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
12541 if (mActiveEntry) {
12542 mActiveEntry->SetLayoutHistoryState(aLayoutHistoryState);
12544 return NS_OK;
12547 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
12548 nsIInterfaceRequestor* aRequestor) {
12549 if (aRequestor) {
12550 mWeakPtr = do_GetWeakReference(aRequestor);
12554 nsDocShell::InterfaceRequestorProxy::~InterfaceRequestorProxy() {
12555 mWeakPtr = nullptr;
12558 NS_IMPL_ISUPPORTS(nsDocShell::InterfaceRequestorProxy, nsIInterfaceRequestor)
12560 NS_IMETHODIMP
12561 nsDocShell::InterfaceRequestorProxy::GetInterface(const nsIID& aIID,
12562 void** aSink) {
12563 NS_ENSURE_ARG_POINTER(aSink);
12564 nsCOMPtr<nsIInterfaceRequestor> ifReq = do_QueryReferent(mWeakPtr);
12565 if (ifReq) {
12566 return ifReq->GetInterface(aIID, aSink);
12568 *aSink = nullptr;
12569 return NS_NOINTERFACE;
12572 //*****************************************************************************
12573 // nsDocShell::nsIAuthPromptProvider
12574 //*****************************************************************************
12576 NS_IMETHODIMP
12577 nsDocShell::GetAuthPrompt(uint32_t aPromptReason, const nsIID& aIID,
12578 void** aResult) {
12579 // a priority prompt request will override a false mAllowAuth setting
12580 bool priorityPrompt = (aPromptReason == PROMPT_PROXY);
12582 if (!mAllowAuth && !priorityPrompt) {
12583 return NS_ERROR_NOT_AVAILABLE;
12586 // we're either allowing auth, or it's a proxy request
12587 nsresult rv;
12588 nsCOMPtr<nsIPromptFactory> wwatch =
12589 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
12590 NS_ENSURE_SUCCESS(rv, rv);
12592 rv = EnsureScriptEnvironment();
12593 NS_ENSURE_SUCCESS(rv, rv);
12595 // Get the an auth prompter for our window so that the parenting
12596 // of the dialogs works as it should when using tabs.
12598 return wwatch->GetPrompt(mScriptGlobal, aIID,
12599 reinterpret_cast<void**>(aResult));
12602 //*****************************************************************************
12603 // nsDocShell::nsILoadContext
12604 //*****************************************************************************
12606 NS_IMETHODIMP
12607 nsDocShell::GetAssociatedWindow(mozIDOMWindowProxy** aWindow) {
12608 CallGetInterface(this, aWindow);
12609 return NS_OK;
12612 NS_IMETHODIMP
12613 nsDocShell::GetTopWindow(mozIDOMWindowProxy** aWindow) {
12614 return mBrowsingContext->GetTopWindow(aWindow);
12617 NS_IMETHODIMP
12618 nsDocShell::GetTopFrameElement(Element** aElement) {
12619 return mBrowsingContext->GetTopFrameElement(aElement);
12622 NS_IMETHODIMP
12623 nsDocShell::GetUseTrackingProtection(bool* aUseTrackingProtection) {
12624 return mBrowsingContext->GetUseTrackingProtection(aUseTrackingProtection);
12627 NS_IMETHODIMP
12628 nsDocShell::SetUseTrackingProtection(bool aUseTrackingProtection) {
12629 return mBrowsingContext->SetUseTrackingProtection(aUseTrackingProtection);
12632 NS_IMETHODIMP
12633 nsDocShell::GetIsContent(bool* aIsContent) {
12634 *aIsContent = (mItemType == typeContent);
12635 return NS_OK;
12638 bool nsDocShell::IsOKToLoadURI(nsIURI* aURI) {
12639 MOZ_ASSERT(aURI, "Must have a URI!");
12641 if (!mFiredUnloadEvent) {
12642 return true;
12645 if (!mLoadingURI) {
12646 return false;
12649 bool isPrivateWin = false;
12650 Document* doc = GetDocument();
12651 if (doc) {
12652 isPrivateWin =
12653 doc->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
12656 nsCOMPtr<nsIScriptSecurityManager> secMan =
12657 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
12658 return secMan && NS_SUCCEEDED(secMan->CheckSameOriginURI(
12659 aURI, mLoadingURI, false, isPrivateWin));
12663 // Routines for selection and clipboard
12665 nsresult nsDocShell::GetControllerForCommand(const char* aCommand,
12666 nsIController** aResult) {
12667 NS_ENSURE_ARG_POINTER(aResult);
12668 *aResult = nullptr;
12670 NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
12672 nsCOMPtr<nsPIWindowRoot> root = mScriptGlobal->GetTopWindowRoot();
12673 NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
12675 return root->GetControllerForCommand(aCommand, false /* for any window */,
12676 aResult);
12679 NS_IMETHODIMP
12680 nsDocShell::IsCommandEnabled(const char* aCommand, bool* aResult) {
12681 NS_ENSURE_ARG_POINTER(aResult);
12682 *aResult = false;
12684 nsresult rv = NS_ERROR_FAILURE;
12686 nsCOMPtr<nsIController> controller;
12687 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12688 if (controller) {
12689 rv = controller->IsCommandEnabled(aCommand, aResult);
12692 return rv;
12695 NS_IMETHODIMP
12696 nsDocShell::DoCommand(const char* aCommand) {
12697 nsresult rv = NS_ERROR_FAILURE;
12699 nsCOMPtr<nsIController> controller;
12700 rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12701 if (controller) {
12702 rv = controller->DoCommand(aCommand);
12705 return rv;
12708 NS_IMETHODIMP
12709 nsDocShell::DoCommandWithParams(const char* aCommand,
12710 nsICommandParams* aParams) {
12711 nsCOMPtr<nsIController> controller;
12712 nsresult rv = GetControllerForCommand(aCommand, getter_AddRefs(controller));
12713 if (NS_WARN_IF(NS_FAILED(rv))) {
12714 return rv;
12717 nsCOMPtr<nsICommandController> commandController =
12718 do_QueryInterface(controller, &rv);
12719 if (NS_WARN_IF(NS_FAILED(rv))) {
12720 return rv;
12723 return commandController->DoCommandWithParams(aCommand, aParams);
12726 nsresult nsDocShell::EnsureCommandHandler() {
12727 if (!mCommandManager) {
12728 if (nsCOMPtr<nsPIDOMWindowOuter> domWindow = GetWindow()) {
12729 mCommandManager = new nsCommandManager(domWindow);
12732 return mCommandManager ? NS_OK : NS_ERROR_FAILURE;
12735 // link handling
12737 class OnLinkClickEvent : public Runnable {
12738 public:
12739 OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12740 nsDocShellLoadState* aLoadState, bool aNoOpenerImplied,
12741 bool aIsTrusted, nsIPrincipal* aTriggeringPrincipal);
12743 NS_IMETHOD Run() override {
12744 // We need to set up an AutoJSAPI here for the following reason: When we
12745 // do OnLinkClickSync we'll eventually end up in
12746 // nsGlobalWindow::OpenInternal which only does popup blocking if
12747 // !LegacyIsCallerChromeOrNativeCode(). So we need to fake things so that
12748 // we don't look like native code as far as LegacyIsCallerNativeCode() is
12749 // concerned.
12750 AutoJSAPI jsapi;
12751 if (mIsTrusted || jsapi.Init(mContent->OwnerDoc()->GetScopeObject())) {
12752 mHandler->OnLinkClickSync(mContent, mLoadState, mNoOpenerImplied,
12753 mTriggeringPrincipal);
12755 return NS_OK;
12758 private:
12759 RefPtr<nsDocShell> mHandler;
12760 nsCOMPtr<nsIContent> mContent;
12761 RefPtr<nsDocShellLoadState> mLoadState;
12762 nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
12763 bool mNoOpenerImplied;
12764 bool mIsTrusted;
12767 OnLinkClickEvent::OnLinkClickEvent(nsDocShell* aHandler, nsIContent* aContent,
12768 nsDocShellLoadState* aLoadState,
12769 bool aNoOpenerImplied, bool aIsTrusted,
12770 nsIPrincipal* aTriggeringPrincipal)
12771 : mozilla::Runnable("OnLinkClickEvent"),
12772 mHandler(aHandler),
12773 mContent(aContent),
12774 mLoadState(aLoadState),
12775 mTriggeringPrincipal(aTriggeringPrincipal),
12776 mNoOpenerImplied(aNoOpenerImplied),
12777 mIsTrusted(aIsTrusted) {}
12779 nsresult nsDocShell::OnLinkClick(
12780 nsIContent* aContent, nsIURI* aURI, const nsAString& aTargetSpec,
12781 const nsAString& aFileName, nsIInputStream* aPostDataStream,
12782 nsIInputStream* aHeadersDataStream, bool aIsUserTriggered, bool aIsTrusted,
12783 nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp) {
12784 #ifndef ANDROID
12785 MOZ_ASSERT(aTriggeringPrincipal, "Need a valid triggeringPrincipal");
12786 #endif
12787 NS_ASSERTION(NS_IsMainThread(), "wrong thread");
12789 if (!IsNavigationAllowed() || !IsOKToLoadURI(aURI)) {
12790 return NS_OK;
12793 // On history navigation through Back/Forward buttons, don't execute
12794 // automatic JavaScript redirection such as |anchorElement.click()| or
12795 // |formElement.submit()|.
12797 // XXX |formElement.submit()| bypasses this checkpoint because it calls
12798 // nsDocShell::OnLinkClickSync(...) instead.
12799 if (ShouldBlockLoadingForBackButton()) {
12800 return NS_OK;
12803 if (aContent->IsEditable()) {
12804 return NS_OK;
12807 Document* ownerDoc = aContent->OwnerDoc();
12808 if (nsContentUtils::IsExternalProtocol(aURI)) {
12809 ownerDoc->EnsureNotEnteringAndExitFullscreen();
12812 bool noOpenerImplied = false;
12813 nsAutoString target(aTargetSpec);
12814 if (aFileName.IsVoid() &&
12815 ShouldOpenInBlankTarget(aTargetSpec, aURI, aContent, aIsUserTriggered)) {
12816 target = u"_blank";
12817 if (!aTargetSpec.Equals(target)) {
12818 noOpenerImplied = true;
12822 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI);
12823 loadState->SetTarget(target);
12824 loadState->SetFileName(aFileName);
12825 loadState->SetPostDataStream(aPostDataStream);
12826 loadState->SetHeadersStream(aHeadersDataStream);
12827 loadState->SetFirstParty(true);
12828 loadState->SetTriggeringPrincipal(
12829 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal());
12830 loadState->SetPrincipalToInherit(aContent->NodePrincipal());
12831 loadState->SetCsp(aCsp ? aCsp : aContent->GetCsp());
12832 loadState->SetAllowFocusMove(UserActivation::IsHandlingUserInput());
12834 const bool hasValidUserGestureActivation =
12835 ownerDoc->HasValidTransientUserGestureActivation();
12836 loadState->SetHasValidUserGestureActivation(hasValidUserGestureActivation);
12837 loadState->SetTextDirectiveUserActivation(
12838 ownerDoc->ConsumeTextDirectiveUserActivation() ||
12839 hasValidUserGestureActivation);
12841 nsCOMPtr<nsIRunnable> ev =
12842 new OnLinkClickEvent(this, aContent, loadState, noOpenerImplied,
12843 aIsTrusted, aTriggeringPrincipal);
12844 return Dispatch(ev.forget());
12847 bool nsDocShell::ShouldOpenInBlankTarget(const nsAString& aOriginalTarget,
12848 nsIURI* aLinkURI, nsIContent* aContent,
12849 bool aIsUserTriggered) {
12850 if (net::SchemeIsJavascript(aLinkURI)) {
12851 return false;
12854 // External links from within app tabs should always open in new tabs
12855 // instead of replacing the app tab's page (Bug 575561)
12856 // nsIURI.host can throw for non-nsStandardURL nsIURIs. If we fail to
12857 // get either host, just return false to use the original target.
12858 nsAutoCString linkHost;
12859 if (NS_FAILED(aLinkURI->GetHost(linkHost))) {
12860 return false;
12863 // The targetTopLevelLinkClicksToBlank property on BrowsingContext allows
12864 // privileged code to change the default targeting behaviour. In particular,
12865 // if a user-initiated link click for the (or targetting the) top-level frame
12866 // is detected, we default the target to "_blank" to give it a new
12867 // top-level BrowsingContext.
12868 if (mBrowsingContext->TargetTopLevelLinkClicksToBlank() && aIsUserTriggered &&
12869 ((aOriginalTarget.IsEmpty() && mBrowsingContext->IsTop()) ||
12870 aOriginalTarget == u"_top"_ns)) {
12871 return true;
12874 // Don't modify non-default targets.
12875 if (!aOriginalTarget.IsEmpty()) {
12876 return false;
12879 // Only check targets that are in extension panels or app tabs.
12880 // (isAppTab will be false for app tab subframes).
12881 nsString mmGroup = mBrowsingContext->Top()->GetMessageManagerGroup();
12882 if (!mmGroup.EqualsLiteral("webext-browsers") &&
12883 !mBrowsingContext->IsAppTab()) {
12884 return false;
12887 nsCOMPtr<nsIURI> docURI = aContent->OwnerDoc()->GetDocumentURIObject();
12888 if (!docURI) {
12889 return false;
12892 nsAutoCString docHost;
12893 if (NS_FAILED(docURI->GetHost(docHost))) {
12894 return false;
12897 if (linkHost.Equals(docHost)) {
12898 return false;
12901 // Special case: ignore "www" prefix if it is part of host string
12902 return linkHost.Length() < docHost.Length()
12903 ? !docHost.Equals("www."_ns + linkHost)
12904 : !linkHost.Equals("www."_ns + docHost);
12907 static bool ElementCanHaveNoopener(nsIContent* aContent) {
12908 // Make sure we are dealing with either an <A>, <AREA>, or <FORM> element in
12909 // the HTML, XHTML, or SVG namespace.
12910 return aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area,
12911 nsGkAtoms::form) ||
12912 aContent->IsSVGElement(nsGkAtoms::a);
12915 nsresult nsDocShell::OnLinkClickSync(nsIContent* aContent,
12916 nsDocShellLoadState* aLoadState,
12917 bool aNoOpenerImplied,
12918 nsIPrincipal* aTriggeringPrincipal) {
12919 if (!IsNavigationAllowed() || !IsOKToLoadURI(aLoadState->URI())) {
12920 return NS_OK;
12923 // XXX When the linking node was HTMLFormElement, it is synchronous event.
12924 // That is, the caller of this method is not |OnLinkClickEvent::Run()|
12925 // but |HTMLFormElement::SubmitSubmission(...)|.
12926 if (aContent->IsHTMLElement(nsGkAtoms::form) &&
12927 ShouldBlockLoadingForBackButton()) {
12928 return NS_OK;
12931 if (aContent->IsEditable()) {
12932 return NS_OK;
12935 // if the triggeringPrincipal is not passed explicitly, then we
12936 // fall back to using doc->NodePrincipal() as the triggeringPrincipal.
12937 nsCOMPtr<nsIPrincipal> triggeringPrincipal =
12938 aTriggeringPrincipal ? aTriggeringPrincipal : aContent->NodePrincipal();
12941 // defer to an external protocol handler if necessary...
12942 nsCOMPtr<nsIExternalProtocolService> extProtService =
12943 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
12944 if (extProtService) {
12945 nsAutoCString scheme;
12946 aLoadState->URI()->GetScheme(scheme);
12947 if (!scheme.IsEmpty()) {
12948 // if the URL scheme does not correspond to an exposed protocol, then
12949 // we need to hand this link click over to the external protocol
12950 // handler.
12951 bool isExposed;
12952 nsresult rv =
12953 extProtService->IsExposedProtocol(scheme.get(), &isExposed);
12954 if (NS_SUCCEEDED(rv) && !isExposed) {
12955 return extProtService->LoadURI(
12956 aLoadState->URI(), triggeringPrincipal, nullptr, mBrowsingContext,
12957 /* aTriggeredExternally */
12958 false,
12959 /* aHasValidUserGestureActivation */
12960 aContent->OwnerDoc()->HasValidTransientUserGestureActivation());
12965 uint32_t triggeringSandboxFlags = 0;
12966 uint64_t triggeringWindowId = 0;
12967 bool triggeringStorageAccess = false;
12968 if (mBrowsingContext) {
12969 triggeringSandboxFlags = aContent->OwnerDoc()->GetSandboxFlags();
12970 triggeringWindowId = aContent->OwnerDoc()->InnerWindowID();
12971 triggeringStorageAccess = aContent->OwnerDoc()->UsingStorageAccess();
12974 uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
12975 bool elementCanHaveNoopener = ElementCanHaveNoopener(aContent);
12976 bool triggeringPrincipalIsSystemPrincipal =
12977 aLoadState->TriggeringPrincipal()->IsSystemPrincipal();
12978 if (elementCanHaveNoopener) {
12979 MOZ_ASSERT(aContent->IsHTMLElement() || aContent->IsSVGElement());
12980 nsAutoString relString;
12981 aContent->AsElement()->GetAttr(nsGkAtoms::rel, relString);
12982 nsWhitespaceTokenizerTemplate<nsContentUtils::IsHTMLWhitespace> tok(
12983 relString);
12985 bool targetBlank = aLoadState->Target().LowerCaseEqualsLiteral("_blank");
12986 bool explicitOpenerSet = false;
12988 // The opener behaviour follows a hierarchy, such that if a higher
12989 // priority behaviour is specified, it always takes priority. That
12990 // priority is currently: norefrerer > noopener > opener > default
12992 while (tok.hasMoreTokens()) {
12993 const nsAString& token = tok.nextToken();
12994 if (token.LowerCaseEqualsLiteral("noreferrer")) {
12995 flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
12996 INTERNAL_LOAD_FLAGS_NO_OPENER;
12997 // noreferrer cannot be overwritten by a 'rel=opener'.
12998 explicitOpenerSet = true;
12999 break;
13002 if (token.LowerCaseEqualsLiteral("noopener")) {
13003 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13004 explicitOpenerSet = true;
13007 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
13008 token.LowerCaseEqualsLiteral("opener") && !explicitOpenerSet) {
13009 explicitOpenerSet = true;
13013 if (targetBlank && StaticPrefs::dom_targetBlankNoOpener_enabled() &&
13014 !explicitOpenerSet && !triggeringPrincipalIsSystemPrincipal) {
13015 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13018 if (aNoOpenerImplied) {
13019 flags |= INTERNAL_LOAD_FLAGS_NO_OPENER;
13023 // Get the owner document of the link that was clicked, this will be
13024 // the document that the link is in, or the last document that the
13025 // link was in. From that document, we'll get the URI to use as the
13026 // referrer, since the current URI in this docshell may be a
13027 // new document that we're in the process of loading.
13028 RefPtr<Document> referrerDoc = aContent->OwnerDoc();
13030 // Now check that the referrerDoc's inner window is the current inner
13031 // window for mScriptGlobal. If it's not, then we don't want to
13032 // follow this link.
13033 nsPIDOMWindowInner* referrerInner = referrerDoc->GetInnerWindow();
13034 if (!mScriptGlobal || !referrerInner ||
13035 mScriptGlobal->GetCurrentInnerWindow() != referrerInner) {
13036 // We're no longer the current inner window
13037 return NS_OK;
13040 // referrer could be null here in some odd cases, but that's ok,
13041 // we'll just load the link w/o sending a referrer in those cases.
13043 // If this is an anchor element, grab its type property to use as a hint
13044 nsAutoString typeHint;
13045 RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromNode(aContent);
13046 if (anchor) {
13047 anchor->GetType(typeHint);
13048 NS_ConvertUTF16toUTF8 utf8Hint(typeHint);
13049 nsAutoCString type, dummy;
13050 NS_ParseRequestContentType(utf8Hint, type, dummy);
13051 CopyUTF8toUTF16(type, typeHint);
13054 uint32_t loadType = LOAD_LINK;
13055 if (aLoadState->IsFormSubmission()) {
13056 if (aLoadState->Target().IsEmpty()) {
13057 // We set the right load type here for form submissions with an empty
13058 // target. Form submission with a non-empty target are handled in
13059 // nsDocShell::PerformRetargeting after we've selected the correct target
13060 // BC.
13061 loadType = GetLoadTypeForFormSubmission(GetBrowsingContext(), aLoadState);
13063 } else {
13064 // Link click can be triggered inside an onload handler, and we don't want
13065 // to add history entry in this case.
13066 bool inOnLoadHandler = false;
13067 GetIsExecutingOnLoadHandler(&inOnLoadHandler);
13068 if (inOnLoadHandler) {
13069 loadType = LOAD_NORMAL_REPLACE;
13073 nsCOMPtr<nsIReferrerInfo> referrerInfo =
13074 elementCanHaveNoopener ? new ReferrerInfo(*aContent->AsElement())
13075 : new ReferrerInfo(*referrerDoc);
13077 aLoadState->SetTriggeringSandboxFlags(triggeringSandboxFlags);
13078 aLoadState->SetTriggeringWindowId(triggeringWindowId);
13079 aLoadState->SetTriggeringStorageAccess(triggeringStorageAccess);
13080 aLoadState->SetReferrerInfo(referrerInfo);
13081 aLoadState->SetInternalLoadFlags(flags);
13082 aLoadState->SetTypeHint(NS_ConvertUTF16toUTF8(typeHint));
13083 aLoadState->SetLoadType(loadType);
13084 aLoadState->SetSourceBrowsingContext(mBrowsingContext);
13086 nsresult rv = InternalLoad(aLoadState);
13088 if (NS_SUCCEEDED(rv)) {
13089 nsPingListener::DispatchPings(this, aContent, aLoadState->URI(),
13090 referrerInfo);
13093 return rv;
13096 nsresult nsDocShell::OnOverLink(nsIContent* aContent, nsIURI* aURI,
13097 const nsAString& aTargetSpec) {
13098 if (aContent->IsEditable()) {
13099 return NS_OK;
13102 nsresult rv = NS_ERROR_FAILURE;
13104 nsCOMPtr<nsIWebBrowserChrome> browserChrome = do_GetInterface(mTreeOwner);
13105 if (!browserChrome) {
13106 return rv;
13109 nsCOMPtr<nsIURI> exposableURI = nsIOService::CreateExposableURI(aURI);
13110 nsAutoCString spec;
13111 rv = exposableURI->GetDisplaySpec(spec);
13112 NS_ENSURE_SUCCESS(rv, rv);
13114 NS_ConvertUTF8toUTF16 uStr(spec);
13116 PredictorPredict(aURI, mCurrentURI, nsINetworkPredictor::PREDICT_LINK,
13117 aContent->NodePrincipal()->OriginAttributesRef(), nullptr);
13119 rv = browserChrome->SetLinkStatus(uStr);
13120 return rv;
13123 nsresult nsDocShell::OnLeaveLink() {
13124 nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(mTreeOwner));
13125 nsresult rv = NS_ERROR_FAILURE;
13127 if (browserChrome) {
13128 rv = browserChrome->SetLinkStatus(u""_ns);
13130 return rv;
13133 bool nsDocShell::ShouldBlockLoadingForBackButton() {
13134 if (!(mLoadType & LOAD_CMD_HISTORY) ||
13135 UserActivation::IsHandlingUserInput() ||
13136 !Preferences::GetBool("accessibility.blockjsredirection")) {
13137 return false;
13140 bool canGoForward = false;
13141 GetCanGoForward(&canGoForward);
13142 return canGoForward;
13145 //----------------------------------------------------------------------
13146 // Web Shell Services API
13148 // This functions is only called when a new charset is detected in loading a
13149 // document.
13150 nsresult nsDocShell::CharsetChangeReloadDocument(
13151 mozilla::NotNull<const mozilla::Encoding*> aEncoding, int32_t aSource) {
13152 // XXX hack. keep the aCharset and aSource wait to pick it up
13153 nsCOMPtr<nsIDocumentViewer> viewer;
13154 NS_ENSURE_SUCCESS(GetDocViewer(getter_AddRefs(viewer)), NS_ERROR_FAILURE);
13155 if (viewer) {
13156 int32_t source;
13157 Unused << viewer->GetReloadEncodingAndSource(&source);
13158 if (aSource > source) {
13159 viewer->SetReloadEncodingAndSource(aEncoding, aSource);
13160 if (eCharsetReloadRequested != mCharsetReloadState) {
13161 mCharsetReloadState = eCharsetReloadRequested;
13162 switch (mLoadType) {
13163 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
13164 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE |
13165 LOAD_FLAGS_BYPASS_PROXY);
13166 case LOAD_RELOAD_BYPASS_CACHE:
13167 return Reload(LOAD_FLAGS_CHARSET_CHANGE | LOAD_FLAGS_BYPASS_CACHE);
13168 default:
13169 return Reload(LOAD_FLAGS_CHARSET_CHANGE);
13174 // return failure if this request is not accepted due to mCharsetReloadState
13175 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13178 nsresult nsDocShell::CharsetChangeStopDocumentLoad() {
13179 if (eCharsetReloadRequested != mCharsetReloadState) {
13180 Stop(nsIWebNavigation::STOP_ALL);
13181 return NS_OK;
13183 // return failer if this request is not accepted due to mCharsetReloadState
13184 return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
13187 NS_IMETHODIMP nsDocShell::ExitPrintPreview() {
13188 #if NS_PRINT_PREVIEW
13189 nsCOMPtr<nsIWebBrowserPrint> viewer = do_QueryInterface(mDocumentViewer);
13190 return viewer->ExitPrintPreview();
13191 #else
13192 return NS_OK;
13193 #endif
13196 /* [infallible] */
13197 NS_IMETHODIMP nsDocShell::GetIsTopLevelContentDocShell(
13198 bool* aIsTopLevelContentDocShell) {
13199 *aIsTopLevelContentDocShell = false;
13201 if (mItemType == typeContent) {
13202 *aIsTopLevelContentDocShell = mBrowsingContext->IsTopContent();
13205 return NS_OK;
13208 // Implements nsILoadContext.originAttributes
13209 NS_IMETHODIMP
13210 nsDocShell::GetScriptableOriginAttributes(JSContext* aCx,
13211 JS::MutableHandle<JS::Value> aVal) {
13212 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13215 // Implements nsIDocShell.GetOriginAttributes()
13216 NS_IMETHODIMP
13217 nsDocShell::GetOriginAttributes(JSContext* aCx,
13218 JS::MutableHandle<JS::Value> aVal) {
13219 return mBrowsingContext->GetScriptableOriginAttributes(aCx, aVal);
13222 bool nsDocShell::ServiceWorkerAllowedToControlWindow(nsIPrincipal* aPrincipal,
13223 nsIURI* aURI) {
13224 MOZ_ASSERT(aPrincipal);
13225 MOZ_ASSERT(aURI);
13227 if (UsePrivateBrowsing() || mBrowsingContext->GetSandboxFlags()) {
13228 return false;
13231 nsCOMPtr<nsIDocShellTreeItem> parent;
13232 GetInProcessSameTypeParent(getter_AddRefs(parent));
13233 nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
13234 nsPIDOMWindowInner* parentInner =
13235 parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
13237 StorageAccess storage =
13238 StorageAllowedForNewWindow(aPrincipal, aURI, parentInner);
13240 // If the partitioned service worker is enabled, service worker is allowed to
13241 // control the window if partition is enabled.
13242 if (StaticPrefs::privacy_partition_serviceWorkers() && parentInner) {
13243 RefPtr<Document> doc = parentInner->GetExtantDoc();
13245 if (doc && StoragePartitioningEnabled(storage, doc->CookieJarSettings())) {
13246 return true;
13250 return storage == StorageAccess::eAllow;
13253 nsresult nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs) {
13254 MOZ_ASSERT(!mIsBeingDestroyed);
13255 return mBrowsingContext->SetOriginAttributes(aAttrs);
13258 NS_IMETHODIMP
13259 nsDocShell::ResumeRedirectedLoad(uint64_t aIdentifier, int32_t aHistoryIndex) {
13260 RefPtr<nsDocShell> self = this;
13261 RefPtr<ChildProcessChannelListener> cpcl =
13262 ChildProcessChannelListener::GetSingleton();
13264 // Call into InternalLoad with the pending channel when it is received.
13265 cpcl->RegisterCallback(
13266 aIdentifier, [self, aHistoryIndex](
13267 nsDocShellLoadState* aLoadState,
13268 nsTArray<Endpoint<extensions::PStreamFilterParent>>&&
13269 aStreamFilterEndpoints,
13270 nsDOMNavigationTiming* aTiming) {
13271 MOZ_ASSERT(aLoadState->GetPendingRedirectedChannel());
13272 if (NS_WARN_IF(self->mIsBeingDestroyed)) {
13273 aLoadState->GetPendingRedirectedChannel()->CancelWithReason(
13274 NS_BINDING_ABORTED, "nsDocShell::mIsBeingDestroyed"_ns);
13275 return NS_BINDING_ABORTED;
13278 self->mLoadType = aLoadState->LoadType();
13279 nsCOMPtr<nsIURI> previousURI;
13280 uint32_t previousFlags = 0;
13281 ExtractLastVisit(aLoadState->GetPendingRedirectedChannel(),
13282 getter_AddRefs(previousURI), &previousFlags);
13283 self->SaveLastVisit(aLoadState->GetPendingRedirectedChannel(),
13284 previousURI, previousFlags);
13286 if (aTiming) {
13287 self->mTiming = new nsDOMNavigationTiming(self, aTiming);
13288 self->mBlankTiming = false;
13291 // If we're performing a history load, locate the correct history entry,
13292 // and set the relevant bits on our loadState.
13293 if (aHistoryIndex >= 0 && self->GetSessionHistory() &&
13294 !mozilla::SessionHistoryInParent()) {
13295 nsCOMPtr<nsISHistory> legacySHistory =
13296 self->GetSessionHistory()->LegacySHistory();
13298 nsCOMPtr<nsISHEntry> entry;
13299 nsresult rv = legacySHistory->GetEntryAtIndex(aHistoryIndex,
13300 getter_AddRefs(entry));
13301 if (NS_SUCCEEDED(rv)) {
13302 legacySHistory->InternalSetRequestedIndex(aHistoryIndex);
13303 aLoadState->SetLoadType(LOAD_HISTORY);
13304 aLoadState->SetSHEntry(entry);
13308 self->InternalLoad(aLoadState);
13310 if (aLoadState->GetOriginalURIString().isSome()) {
13311 // Save URI string in case it's needed later when
13312 // sending to search engine service in EndPageLoad()
13313 self->mOriginalUriString = *aLoadState->GetOriginalURIString();
13316 for (auto& endpoint : aStreamFilterEndpoints) {
13317 extensions::StreamFilterParent::Attach(
13318 aLoadState->GetPendingRedirectedChannel(), std::move(endpoint));
13321 // If the channel isn't pending, then it means that InternalLoad
13322 // never connected it, and we shouldn't try to continue. This
13323 // can happen even if InternalLoad returned NS_OK.
13324 bool pending = false;
13325 aLoadState->GetPendingRedirectedChannel()->IsPending(&pending);
13326 NS_ASSERTION(pending, "We should have connected the pending channel!");
13327 if (!pending) {
13328 return NS_BINDING_ABORTED;
13330 return NS_OK;
13332 return NS_OK;
13335 NS_IMETHODIMP
13336 nsDocShell::SetOriginAttributes(JS::Handle<JS::Value> aOriginAttributes,
13337 JSContext* aCx) {
13338 OriginAttributes attrs;
13339 if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
13340 return NS_ERROR_INVALID_ARG;
13343 return SetOriginAttributes(attrs);
13346 NS_IMETHODIMP
13347 nsDocShell::GetAsyncPanZoomEnabled(bool* aOut) {
13348 if (PresShell* presShell = GetPresShell()) {
13349 *aOut = presShell->AsyncPanZoomEnabled();
13350 return NS_OK;
13353 // If we don't have a presShell, fall back to the default platform value of
13354 // whether or not APZ is enabled.
13355 *aOut = gfxPlatform::AsyncPanZoomEnabled();
13356 return NS_OK;
13359 bool nsDocShell::HasUnloadedParent() {
13360 for (WindowContext* wc = GetBrowsingContext()->GetParentWindowContext(); wc;
13361 wc = wc->GetParentWindowContext()) {
13362 if (!wc->IsCurrent() || wc->IsDiscarded() ||
13363 wc->GetBrowsingContext()->IsDiscarded()) {
13364 // If a parent is OOP and the parent WindowContext is no
13365 // longer current, we can assume the parent was unloaded.
13366 return true;
13369 if (wc->GetBrowsingContext()->IsInProcess() &&
13370 (!wc->GetBrowsingContext()->GetDocShell() ||
13371 wc->GetBrowsingContext()->GetDocShell()->GetIsInUnload())) {
13372 return true;
13375 return false;
13378 /* static */
13379 bool nsDocShell::ShouldUpdateGlobalHistory(uint32_t aLoadType) {
13380 return !(aLoadType == LOAD_BYPASS_HISTORY || aLoadType == LOAD_ERROR_PAGE ||
13381 aLoadType & LOAD_CMD_HISTORY);
13384 void nsDocShell::UpdateGlobalHistoryTitle(nsIURI* aURI) {
13385 if (!mBrowsingContext->GetUseGlobalHistory() || UsePrivateBrowsing()) {
13386 return;
13389 // Global history is interested into sub-frame visits only for link-coloring
13390 // purposes, thus title updates are skipped for those.
13392 // Moreover, some iframe documents (such as the ones created via
13393 // document.open()) inherit the document uri of the caller, which would cause
13394 // us to override a previously set page title with one from the subframe.
13395 if (IsSubframe()) {
13396 return;
13399 if (nsCOMPtr<IHistory> history = components::History::Service()) {
13400 history->SetURITitle(aURI, mTitle);
13404 bool nsDocShell::IsInvisible() { return mInvisible; }
13406 void nsDocShell::SetInvisible(bool aInvisible) { mInvisible = aInvisible; }
13408 /* static */
13409 void nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider,
13410 const nsString& aKeyword) {
13411 if (aProvider.IsEmpty()) {
13412 return;
13414 nsresult rv;
13415 nsCOMPtr<nsISupportsString> isupportsString =
13416 do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
13417 NS_ENSURE_SUCCESS_VOID(rv);
13419 rv = isupportsString->SetData(aProvider);
13420 NS_ENSURE_SUCCESS_VOID(rv);
13422 nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
13423 if (obsSvc) {
13424 // Note that "keyword-search" refers to a search via the url
13425 // bar, not a bookmarks keyword search.
13426 obsSvc->NotifyObservers(isupportsString, "keyword-search", aKeyword.get());
13430 NS_IMETHODIMP
13431 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel,
13432 bool* aShouldIntercept) {
13433 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel,
13434 aShouldIntercept);
13437 NS_IMETHODIMP
13438 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel) {
13439 return mInterceptController->ChannelIntercepted(aChannel);
13442 bool nsDocShell::InFrameSwap() {
13443 RefPtr<nsDocShell> shell = this;
13444 do {
13445 if (shell->mInFrameSwap) {
13446 return true;
13448 shell = shell->GetInProcessParentDocshell();
13449 } while (shell);
13450 return false;
13453 UniquePtr<ClientSource> nsDocShell::TakeInitialClientSource() {
13454 return std::move(mInitialClientSource);
13457 NS_IMETHODIMP
13458 nsDocShell::GetEditingSession(nsIEditingSession** aEditSession) {
13459 if (!NS_SUCCEEDED(EnsureEditorData())) {
13460 return NS_ERROR_FAILURE;
13463 *aEditSession = do_AddRef(mEditorData->GetEditingSession()).take();
13464 return *aEditSession ? NS_OK : NS_ERROR_FAILURE;
13467 NS_IMETHODIMP
13468 nsDocShell::GetScriptableBrowserChild(nsIBrowserChild** aBrowserChild) {
13469 *aBrowserChild = GetBrowserChild().take();
13470 return *aBrowserChild ? NS_OK : NS_ERROR_FAILURE;
13473 already_AddRefed<nsIBrowserChild> nsDocShell::GetBrowserChild() {
13474 nsCOMPtr<nsIBrowserChild> tc = do_QueryReferent(mBrowserChild);
13475 return tc.forget();
13478 nsCommandManager* nsDocShell::GetCommandManager() {
13479 NS_ENSURE_SUCCESS(EnsureCommandHandler(), nullptr);
13480 return mCommandManager;
13483 NS_IMETHODIMP_(void)
13484 nsDocShell::GetOriginAttributes(mozilla::OriginAttributes& aAttrs) {
13485 mBrowsingContext->GetOriginAttributes(aAttrs);
13488 HTMLEditor* nsIDocShell::GetHTMLEditor() {
13489 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13490 return docShell->GetHTMLEditorInternal();
13493 nsresult nsIDocShell::SetHTMLEditor(HTMLEditor* aHTMLEditor) {
13494 nsDocShell* docShell = static_cast<nsDocShell*>(this);
13495 return docShell->SetHTMLEditorInternal(aHTMLEditor);
13498 #define MATRIX_LENGTH 20
13500 NS_IMETHODIMP
13501 nsDocShell::SetColorMatrix(const nsTArray<float>& aMatrix) {
13502 if (aMatrix.Length() == MATRIX_LENGTH) {
13503 mColorMatrix.reset(new gfx::Matrix5x4());
13504 static_assert(
13505 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13506 "Size mismatch for our memcpy");
13507 memcpy(mColorMatrix->components, aMatrix.Elements(),
13508 sizeof(mColorMatrix->components));
13509 } else if (aMatrix.Length() == 0) {
13510 mColorMatrix.reset();
13511 } else {
13512 return NS_ERROR_INVALID_ARG;
13515 PresShell* presShell = GetPresShell();
13516 if (!presShell) {
13517 return NS_ERROR_FAILURE;
13520 nsIFrame* frame = presShell->GetRootFrame();
13521 if (!frame) {
13522 return NS_ERROR_FAILURE;
13525 frame->SchedulePaint();
13527 return NS_OK;
13530 NS_IMETHODIMP
13531 nsDocShell::GetColorMatrix(nsTArray<float>& aMatrix) {
13532 if (mColorMatrix) {
13533 aMatrix.SetLength(MATRIX_LENGTH);
13534 static_assert(
13535 MATRIX_LENGTH * sizeof(float) == sizeof(mColorMatrix->components),
13536 "Size mismatch for our memcpy");
13537 memcpy(aMatrix.Elements(), mColorMatrix->components,
13538 MATRIX_LENGTH * sizeof(float));
13541 return NS_OK;
13544 #undef MATRIX_LENGTH
13546 NS_IMETHODIMP
13547 nsDocShell::GetIsForceReloading(bool* aForceReload) {
13548 *aForceReload = IsForceReloading();
13549 return NS_OK;
13552 bool nsDocShell::IsForceReloading() { return IsForceReloadType(mLoadType); }
13554 NS_IMETHODIMP
13555 nsDocShell::GetBrowsingContextXPCOM(BrowsingContext** aBrowsingContext) {
13556 *aBrowsingContext = do_AddRef(mBrowsingContext).take();
13557 return NS_OK;
13560 BrowsingContext* nsDocShell::GetBrowsingContext() { return mBrowsingContext; }
13562 bool nsDocShell::GetIsAttemptingToNavigate() {
13563 // XXXbz the document.open spec says to abort even if there's just a
13564 // queued navigation task, sort of. It's not clear whether browsers
13565 // actually do that, and we didn't use to do it, so for now let's
13566 // not do that.
13567 // https://github.com/whatwg/html/issues/3447 tracks the spec side of this.
13568 if (mDocumentRequest) {
13569 // There's definitely a navigation in progress.
13570 return true;
13573 // javascript: channels have slightly weird behavior: they're LOAD_BACKGROUND
13574 // until the script runs, which means they're not sending loadgroup
13575 // notifications and hence not getting set as mDocumentRequest. Look through
13576 // our loadgroup for document-level javascript: loads.
13577 if (!mLoadGroup) {
13578 return false;
13581 nsCOMPtr<nsISimpleEnumerator> requests;
13582 mLoadGroup->GetRequests(getter_AddRefs(requests));
13583 bool hasMore = false;
13584 while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
13585 nsCOMPtr<nsISupports> elem;
13586 requests->GetNext(getter_AddRefs(elem));
13587 nsCOMPtr<nsIScriptChannel> scriptChannel(do_QueryInterface(elem));
13588 if (!scriptChannel) {
13589 continue;
13592 if (scriptChannel->GetIsDocumentLoad()) {
13593 // This is a javascript: load that might lead to a new document,
13594 // hence a navigation.
13595 return true;
13599 return mCheckingSessionHistory;
13602 void nsDocShell::SetLoadingSessionHistoryInfo(
13603 const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo,
13604 bool aNeedToReportActiveAfterLoadingBecomesActive) {
13605 // FIXME Would like to assert this, but can't yet.
13606 // MOZ_ASSERT(!mLoadingEntry);
13607 MOZ_LOG(gSHLog, LogLevel::Debug,
13608 ("Setting the loading entry on nsDocShell %p to %s", this,
13609 aLoadingInfo.mInfo.GetURI()->GetSpecOrDefault().get()));
13610 mLoadingEntry = MakeUnique<LoadingSessionHistoryInfo>(aLoadingInfo);
13611 mNeedToReportActiveAfterLoadingBecomesActive =
13612 aNeedToReportActiveAfterLoadingBecomesActive;
13615 void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
13616 uint32_t aCacheKey,
13617 nsIURI* aPreviousURI) {
13618 MOZ_ASSERT(mozilla::SessionHistoryInParent());
13620 MOZ_LOG(gSHLog, LogLevel::Debug,
13621 ("nsDocShell %p MoveLoadingToActiveEntry", this));
13623 UniquePtr<SessionHistoryInfo> previousActiveEntry(mActiveEntry.release());
13624 mozilla::UniquePtr<mozilla::dom::LoadingSessionHistoryInfo> loadingEntry;
13625 mActiveEntryIsLoadingFromSessionHistory =
13626 mLoadingEntry && mLoadingEntry->mLoadIsFromSessionHistory;
13627 if (mLoadingEntry) {
13628 MOZ_LOG(gSHLog, LogLevel::Debug,
13629 ("Moving the loading entry to the active entry on nsDocShell %p "
13630 "to %s",
13631 this, mLoadingEntry->mInfo.GetURI()->GetSpecOrDefault().get()));
13632 mActiveEntry = MakeUnique<SessionHistoryInfo>(mLoadingEntry->mInfo);
13633 mLoadingEntry.swap(loadingEntry);
13634 if (!mActiveEntryIsLoadingFromSessionHistory) {
13635 if (mNeedToReportActiveAfterLoadingBecomesActive) {
13636 // Needed to pass various history length WPTs.
13637 mBrowsingContext->SetActiveSessionHistoryEntry(
13638 mozilla::Nothing(), mActiveEntry.get(), mLoadType,
13639 /* aUpdatedCacheKey = */ 0, false);
13641 mBrowsingContext->IncrementHistoryEntryCountForBrowsingContext();
13644 mNeedToReportActiveAfterLoadingBecomesActive = false;
13646 if (mActiveEntry) {
13647 if (aCacheKey != 0) {
13648 mActiveEntry->SetCacheKey(aCacheKey);
13650 MOZ_ASSERT(loadingEntry);
13651 uint32_t loadType =
13652 mLoadType == LOAD_ERROR_PAGE ? mFailedLoadType : mLoadType;
13654 if (loadingEntry->mLoadId != UINT64_MAX) {
13655 // We're passing in mCurrentURI, which could be null. SessionHistoryCommit
13656 // does require a non-null uri if this is for a refresh load of the same
13657 // URI, but in that case mCurrentURI won't be null here.
13658 mBrowsingContext->SessionHistoryCommit(
13659 *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
13660 aPersist, false, aExpired, aCacheKey);
13665 static bool IsFaviconLoad(nsIRequest* aRequest) {
13666 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
13667 if (!channel) {
13668 return false;
13671 nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
13672 return li && li->InternalContentPolicyType() ==
13673 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
13676 void nsDocShell::RecordSingleChannelId(bool aStartRequest,
13677 nsIRequest* aRequest) {
13678 // Ignore favicon loads, they don't need to block caching.
13679 if (IsFaviconLoad(aRequest)) {
13680 return;
13683 MOZ_ASSERT_IF(!aStartRequest, mRequestForBlockingFromBFCacheCount > 0);
13685 mRequestForBlockingFromBFCacheCount += aStartRequest ? 1 : -1;
13687 if (mBrowsingContext->GetCurrentWindowContext()) {
13688 // We have three states: no request, one request with an id and
13689 // eiher one request without an id or multiple requests. Nothing() is no
13690 // request, Some(non-zero) is one request with an id and Some(0) is one
13691 // request without an id or multiple requests.
13692 Maybe<uint64_t> singleChannelId;
13693 if (mRequestForBlockingFromBFCacheCount > 1) {
13694 singleChannelId = Some(0);
13695 } else if (mRequestForBlockingFromBFCacheCount == 1) {
13696 nsCOMPtr<nsIIdentChannel> identChannel;
13697 if (aStartRequest) {
13698 identChannel = do_QueryInterface(aRequest);
13699 } else {
13700 // aChannel is the channel that's being removed, but we need to check if
13701 // the remaining channel in the loadgroup has an id.
13702 nsCOMPtr<nsISimpleEnumerator> requests;
13703 mLoadGroup->GetRequests(getter_AddRefs(requests));
13704 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13705 if (!IsFaviconLoad(request) &&
13706 !!(identChannel = do_QueryInterface(request))) {
13707 break;
13712 if (identChannel) {
13713 singleChannelId = Some(identChannel->ChannelId());
13714 } else {
13715 singleChannelId = Some(0);
13717 } else {
13718 MOZ_ASSERT(mRequestForBlockingFromBFCacheCount == 0);
13719 singleChannelId = Nothing();
13722 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13723 nsAutoCString uri("[no uri]");
13724 if (mCurrentURI) {
13725 uri = mCurrentURI->GetSpecOrDefault();
13727 if (singleChannelId.isNothing()) {
13728 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13729 ("Loadgroup for %s doesn't have any requests relevant for "
13730 "blocking BFCache",
13731 uri.get()));
13732 } else if (singleChannelId.value() == 0) {
13733 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13734 ("Loadgroup for %s has multiple requests relevant for blocking "
13735 "BFCache",
13736 uri.get()));
13737 } else {
13738 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13739 ("Loadgroup for %s has one request with id %" PRIu64
13740 " relevant for blocking BFCache",
13741 uri.get(), singleChannelId.value()));
13745 if (mSingleChannelId != singleChannelId) {
13746 mSingleChannelId = singleChannelId;
13747 WindowGlobalChild* wgc =
13748 mBrowsingContext->GetCurrentWindowContext()->GetWindowGlobalChild();
13749 if (wgc) {
13750 wgc->SendSetSingleChannelId(singleChannelId);
13756 NS_IMETHODIMP
13757 nsDocShell::OnStartRequest(nsIRequest* aRequest) {
13758 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13759 nsAutoCString uri("[no uri]");
13760 if (mCurrentURI) {
13761 uri = mCurrentURI->GetSpecOrDefault();
13763 nsAutoCString name;
13764 aRequest->GetName(name);
13765 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Verbose,
13766 ("Adding request %s to loadgroup for %s", name.get(), uri.get()));
13768 RecordSingleChannelId(true, aRequest);
13769 return nsDocLoader::OnStartRequest(aRequest);
13772 NS_IMETHODIMP
13773 nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
13774 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Verbose))) {
13775 nsAutoCString uri("[no uri]");
13776 if (mCurrentURI) {
13777 uri = mCurrentURI->GetSpecOrDefault();
13779 nsAutoCString name;
13780 aRequest->GetName(name);
13781 MOZ_LOG(
13782 gSHIPBFCacheLog, LogLevel::Verbose,
13783 ("Removing request %s from loadgroup for %s", name.get(), uri.get()));
13785 RecordSingleChannelId(false, aRequest);
13786 return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
13789 void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
13790 MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
13792 if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
13793 nsCOMPtr<nsISimpleEnumerator> requests;
13794 mLoadGroup->GetRequests(getter_AddRefs(requests));
13795 for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
13796 RefPtr<DocumentChannel> channel = do_QueryObject(request);
13797 if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
13798 static_cast<DocumentChannelChild*>(channel.get())
13799 ->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
13802 mChannelToDisconnectOnPageHide = 0;